# ssm **Repository Path**: webthree/ssm ## Basic Information - **Project Name**: ssm - **Description**: ssm项目集合演示库 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-04-26 - **Last Updated**: 2025-11-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SSM > SSM框架整合 ## 部署环境 - Tomcat 9.0.71 ## 开发知识 ### SystemLight的开发手册 - [看云](https://www.kancloud.cn/system_light/magicsword/2037376) - [Spring 脑图](http://naotu.baidu.com/file/c7e854a81be08f6c57a07fdfb9f64d55?token=1e85f69a4d2ded4e) ## 技术栈 - spring-webmvc - shiro - spring-security - mybatis - flyway - spring-jdbc - mysql - postgresql - sqlite - druid - HikariCP - sharding-jdbc - spring-data-redis - jedis - lettuce - apollo - slf4j - jackson - fastjson2 - xstream - xpp3 - dom4j - poi - checker-qual - classmate - hibernate-validator - dozer - orika - kryo - objenesis - asm - javassist - apache-commons - httpclient5 - okhttp - guava - hutool - drools - groovy - sm-crypt - bcpkix-jdk18on - mvel2 - ognl - mustache - velocity - freemarker - thymeleaf - swagger - spring-test ### 二叉树 - 二叉树种类 - 斜树 - 满二叉树: 节点:2ᵏ-1 - 完全二叉树:底部从左到右是连续的 - 二叉搜索树:左子树都小于节点,右子树都大于节点 - 平衡二叉搜索树 - 存储方式:线性存储(求通项公式)、链式存储 - 遍历方式:深度优先、广度优先 - 前中后序方式 - 递归的实现思路: - 确定参数 - 确定终止条件 - 确定单层的递归逻辑 - 迭代的实现思路: - (深度优先)采用栈的思想,先进后出 - (广度优先)采用队列的思想,先进先出 ### DO、DTO、BO、AO、VO、POJO定义 - [阿里巴巴Java开发手册中的DO、DTO、BO、AO、VO、POJO定义](https://zhuanlan.zhihu.com/p/555833984) ![DO、DTO、BO、AO、VO、POJO定义.jpg](src/main/webapp/assets/images/DO%E3%80%81DTO%E3%80%81BO%E3%80%81AO%E3%80%81VO%E3%80%81POJO%E5%AE%9A%E4%B9%89.jpg) ## 配置说明 ### Maven #### 基本概念说明 ###### Parent 继承 - 继承是 Maven 中很强大的一种功能,继承可以使得子POM可以获得 parent 中的各项配置,可以对子pom进行统一的配置和依赖管理 ###### Maven 的聚合(多模块) - 通过一个pom打包的项目可以将它们列为模块来聚合成一组项目进行构建,这些模块名是这些项目的相对目录 #### maven多环境配置 - 1)配置:pom.xml ```xml dev true dev ``` #### 配置额外打包资源文件 - 1)配置:pom.xml ```xml src/main/webapp META-INF/resources *.* ``` ### Java Web #### ServletContainerInitializer 自动加载机制 > - SCI 是Servlet 3.0规范中引入的一个重要接口 > - 在web应用启动时,通过编程的方式动态地添加Servlet、Filter和Listener等组件 > - SCI是基于Java的服务提供者接口(SPI)机制实现的 - 1)配置:src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer ```text xyz.lisys.bootstrap.SpringServletContainerInitializer ``` - 2)配置:SpringServletContainerInitializer ```java @HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set> clazzSet, ServletContext ctx) throws ServletException { List initializers = Collections.emptyList(); if (clazzSet != null) { initializers = new ArrayList<>(clazzSet.size()); for (Class clazz : clazzSet) { // 防御:一些servlet容器为我们提供了无效的类 // 不管@HandlesTypes怎么声明... // 检查:是否为接口 | 是否为抽象类 | 是否可以转换为MyInitializer if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(clazz)) { try { initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(clazz).newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { ctx.log("No Spring WebApplicationInitializer types detected on classpath"); return; } ctx.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); // 用于对一组对象进行排序,这些对象可以是实现了 PriorityOrdered 或 Ordered 接口的类实例,也可以是使用了 @Order 注解修饰的类实例 AnnotationAwareOrderComparator.sort(initializers); for (WebApplicationInitializer initializer : initializers) { // 委托给所有实现WebApplicationInitializer进行执行逻辑 initializer.onStartup(ctx); } } } ``` #### 启用注解扫描 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml ``` #### Servlet 注册 ###### XML 方式 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml hello xyz.lisys.servlet.HelloServlet hello /hello ``` ###### 注解方式 - 1)配置:HelloServlet ```java @WebServlet(urlPatterns = "/hello", loadOnStartup = 1) public class HelloServlet extends HttpServlet { @Override public void init() { // 初始启动执行加载 // getServletName() 获取名称 // getServletConfig().getInitParameter("message") 获取 init-param 配置参数 } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().write("hello servlet"); } } ``` #### 错误异常处理 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml 404 /error/404 /error/any ``` #### 配置会话 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml 15 ``` #### ServletContextListener - 监听 ServletContext 的创建和销毁事件 ```java @WebListener public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContext servletContext = sce.getServletContext(); // 在应用启动时初始化一些内容 } @Override public void contextDestroyed(ServletContextEvent sce) { // 在应用销毁时做一些清理工作 } } ``` ### Spring > - [参考API](https://docs.spring.io/spring-framework/docs/5.3.29/javadoc-api/) #### Spring 生命周期 ![spring生命周期.png](src/main/webapp/assets/images/spring%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.png) #### ApplicationContext容器创建 - Bean工厂创建 ```java public class BeanTest { @Test public void beanFactory() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions("applicationContextTest/bean.xml"); Foo person = (Foo) beanFactory.getBean("foo"); } } ``` - ClassPathXmlApplicationContext - AnnotationConfigApplicationContext - FileSystemXmlApplicationContext - GenericGroovyApplicationContext #### 模块导入 - 1)配置:spring.xml ```xml ``` #### 开启注解 - 1)配置:spring.xml ```xml ``` #### 组件扫描 - 1)配置:spring.xml ```xml ``` #### 加载属性文件 - 1)配置:spring.xml ```xml ``` ```xml ``` ```xml ``` #### 自定义 XML 标签 - 1)创建自定义的NamespaceHandler ```java public class CustomNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("my-custom-tag", new MyCustomBeanDefinitionParser()); } } ``` - 2)创建BeanDefinitionParser ```java public class MyCustomBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { // 指定这个自定义标签对应的Bean类 return MyCustomBean.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinition beanDefinition) { // 解析自定义标签的属性,并设置到BeanDefinition中 String attributeValue = element.getAttribute("my-attribute"); beanDefinition.getPropertyValues().addPropertyValue("myAttribute", attributeValue); } } ``` - 3)定义自定义Bean类 ```java public class MyCustomBean { private String myAttribute; public String getMyAttribute() { return myAttribute; } public void setMyAttribute(String myAttribute) { this.myAttribute = myAttribute; } } ``` - 4)配置Spring的XML Schema > spring.handlers ```text http\://www.example.com/schema/custom=com.example.spring.custom.CustomNamespaceHandler ``` > spring.schemas ```text http\://www.example.com/schema/custom http\://www.example.com/schema/custom.xsd ``` #### Bean 注册 ###### Bean 作用域 - singleton - prototype - request - session - globalSession ###### 简单注册 - 1)配置:spring.xml ```xml ``` ###### 初始化构造函数 - 1)配置:spring.xml ```xml ``` ###### 参数映射转换 > 主要用于表单提交时的字符串到对象转换 ```xml ``` ```xml ``` ###### 类型转换器 > 更通用的类型转换,支持各种来源的数据转换 ```xml ``` ###### 常用功能类 - InitializingBean:当Spring容器完成Bean的实例化并设置其属性后,会自动调用该方法来执行Bean的初始化逻辑 - FactoryBean:用于创建和配置复杂Bean。 - DisposableBean:管理Bean生命周期,在销毁前调用。 - BeanPostProcessor:在Bean实例化、属性设置及初始化前后添加自定义逻辑。 - BeanDefinitionRegistryPostProcessor:干预和定制Bean定义的注册过程。 - InstantiationAwareBeanPostProcessor:在Bean实例化和初始化各阶段进行自定义处理。 - BeanFactoryPostProcessor:在容器实例化所有Bean前执行自定义逻辑。 - ApplicationListener:监听容器事件,实现事件驱动开发。 - BeanNameAware:让Bean获取自身在Spring容器中的名称。 - BeanFactoryAware:使Bean能获取所在的BeanFactory。 - ApplicationContextAware:让Bean获取所在的ApplicationContext。 - MessageSourceAware:使Bean可获取MessageSource实例处理国际化消息。 - ApplicationEventPublisherAware:让Bean能获取ApplicationEventPublisher实例发布事件。 - ResourceLoaderAware:使Bean可获取ResourceLoader实例加载资源。 - ServletContextAware:让Bean在Web应用中获取ServletContext实例。 - BeanClassLoaderAware:使Bean能获取加载自身的类加载器。 ###### 常用注解 - @PostConstruct:充当init method,初始化方法的注解方式,等同于init - method。 - @PreDestroy:充当destroy method,销毁方法的注解方式,等同于destroy - method。 - @PropertySource:用于指定要加载的属性文件,以便在配置中使用。 - @Value:用于将配置文件中的属性值注入到Bean的字段或方法参数中。 - @ConfigurationProperties:用于将配置文件中的属性值批量绑定到一个JavaBean上。 - @Conditional:根据条件来决定是否创建某个Bean。 - @Autowired:自动装配Bean,默认按类型匹配。 - @Qualifier:与@Autowired配合使用,按名称指定要装配的Bean。 - @Resource:用于注入Bean,默认按名称查找,也可指定类型。 - @Inject:与@Autowired类似,用于注入依赖,需导入相关包。 - @Scope:用于指定Bean的作用域,如单例、原型等。 - @SessionScope:指定Bean的作用域为HTTP Session。 - @RequestScope:指定Bean的作用域为HTTP请求。 - @Component:将类标记为Spring组件,可被自动扫描和注册为Bean。 - @Service:用于标记业务逻辑层的Bean。 - @Repository:用于标记数据访问层的Bean,通常用于数据库操作。 - @ImportResource:用于导入传统的XML配置文件。 - @Import:用于导入其他的配置类,将其定义的Bean添加到当前的配置中,导入的类可以实现ImportBeanDefinitionRegistrar。 - @ComponentScan:用于指定要扫描的包,以查找带有@Component、@Service、@Repository等注释的类,并自动将它们注册为Bean。 - @EnableAspectJAutoProxy:用于启用AspectJ自动代理支持,允许在运行时动态创建代理对象。 - @EnableTransactionManagement:用于启用事务管理支持。 - @EnableScheduling:用于启用Spring的定时任务支持。 - @EnableCaching:用于启用Spring的缓存支持。 - @EnableWebMvc:用于启用Spring MVC框架的支持。 - @EnableConfigurationProperties:用于启用外部化配置属性的支持,可以将配置文件中的属性绑定到Java对象。 ### SpringMVC #### 基本概念说明 ###### 路由注册 - > 在spring中一般采用`@RequestMapping`注解来完成映射关系 - > 要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例 - > 两个实例分别在类级别和方法级别处理路由,annotation-driven配置帮助我们自动完成上述两个实例的注入 ```java public class BeanNameSimpleController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { /** * 一般结合这两种映射器和适配器进行注册 * * */ ModelAndView mv = new ModelAndView(); mv.addObject("msg", "HelloSpringMVC!"); mv.setViewName("index"); return mv; } } ``` ```java public class IndexController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { response.getWriter().write("hello world"); return null; } } ``` ```java public class IndexHttpRequestHandler implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("hello world"); } } ``` ###### 获取WebApplicationContext的方法 - servlet中: - WebApplicationContext context = (WebApplicationContext) getServletContext().getAttribute( WebApplicationContext.ROOT); - WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(ServletContext servletContext); - controller中: - ApplicationContextAware - Resource注入,WebApplicationContext wac; ###### RequestContextHolder获取HttpServletRequest - ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); ###### 获取ServletContext - @Resource private ServletContext servletContext; ###### spring中获取【resources/】中文件的几种方法 - resourceLoader.getResource(); - @Resource private ResourceLoader resourceLoader; - ResourceUtils.getFile(); - new ClassPathResource(); - new FileSystemResource(); ```java File resourceFile = new File(resourceLoader.getResource("classpath:").getURI()); ``` ```java File resourceFile = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX); ``` ###### @Autowired注入的步骤,byType - 1)按照属性类型查找对应的类型的 bean,如果没有则报错。如果有多个实现类,开始执行第二步; - 2)查看实现类是有 @Primary 注解,或者在调用类中使用 @Qualifier("xxx") 执行具体的实现类,如果都没有使用这两注解指定的话,开始执行第三步; - 3)通过反射拿到属性的变量名作为 beanName,通过这个beanName去查找,如果找不到报错 ###### @Resource 注入的步骤,byName - 1)先通过首先@Resouce注解有没有指定name属性,如果没有,开始执行第二步。有则通过name去 Ioc容器找对应的bean,找不到或者找到多个就会直接报错; - 2)通过反射属性的变量名作为beanName去单例池(可理解是 Ioc容器)里寻找,如果这时还找不到,开始执行第三步; - 3)按照属性类型查找对应的类型的 bean,找到多个也会报错。 ###### 日期参数格式化 - @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) ###### 请求顺序 - WEB容器 -> 过滤器 -> Servlet -> 拦截器 -> 控制器增强器 -> 切面 -> 控制器 #### AbstractAnnotationConfigDispatcherServletInitializer 自动加载机制 > - 全注解开发可以实现 ServletContainerInitializer 或 AbstractAnnotationConfigDispatcherServletInitializer > - spring 提供 SpringServletContainerInitializer 会查找 WebApplicationInitializer > - spring3.2版本之后有一个实现AbstractAnnotationConfigDispatcherServletInitializer - 1)配置:MyServletContainerInitializer ```java public class MyServletContainerInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { // 方法返回了 RootConfig 类,这通常包含了数据源、事务管理等非 Web 层的配置 return new Class[0]; } @Override protected Class[] getServletConfigClasses() { // 返回了 WebConfig 类,这通常包含了视图解析器、控制器等 Web 层的配置 return new Class[0]; } @Override protected String[] getServletMappings() { // 设置了 DispatcherServlet 的 URL 映射 return new String[0]; } } ``` #### 代理 Filter - DelegatingFilterProxy > - 对于Servlet Filter的代理,用这个类的好处主要是通过Spring容器来管理Servlet Filter的生命周期 > - filter-name 必须和注册bean同名,调用过滤器时DelegatingFilterProxy会使用对应真实的Filter #### XML 委托注册 - 父容器 ContextLoaderListener - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml contextConfigLocation classpath:applicationContext.xml org.springframework.web.context.ContextLoaderListener ``` #### XML 委托注册 - 子容器 DispatcherServlet ###### 初始化执行加载方法顺序 - HttpServlet - init() - FrameworkServlet - initBeanWrapper() - FrameworkServlet - initWebApplicationContext() - DispatcherServlet - onRefresh() - initMultipartResolver(context) - 初始化文件上传解析器 - CommonsMultipartResolver - initLocaleResolver(context) - 本地化解析 - initThemeResolver(context) - 主题解析器 - initHandlerMappings(context) - 处理器映射器,保存Url映射关系 - BeanNameUrlHandlerMapping - RequestMappingHandlerMapping - initHandlerAdapters(context) - 处理器适配器 - SimpleControllerHandlerAdapter - StringHttpMessageConverter - ResourceHttpMessageConverter - MappingJackson2HttpMessageConverter - Jaxb2RootElementHttpMessageConverter - HandlerFunctionAdapter - RequestMappingHandlerAdapter - HttpRequestHandlerAdapter - initHandlerExceptionResolvers(context) - 异常解析器 - initRequestToViewNameTranslator(context) - 视图提取器,从request中获取viewName - initViewResolvers(context) - 视图解析器 - InternalResourceViewResolver - initFlashMapManager(context) - 闪存参数解析器 - HttpServletBean - initServletBean() ###### DispatcherServlet 响应过程 > DispatcherServlet -> HandlerMapping -> HandlerInterceptor(InterceptorRegistry) -> Handler\[Controller] -> > HandlerAdapter -> ViewResolver ```text 1. DispatcherServlet 接收请求 2. Interceptor.preHandle() ← 拦截器前置处理 3. HandlerMapping 确定处理方法 4. RequestBodyAdvice.supports() ← 判断是否处理请求体 5. RequestBodyAdvice.beforeBodyRead() ← 请求体反序列化前处理 6. HttpMessageConverter.read() ← 将请求体转换为Java对象 7. RequestBodyAdvice.afterBodyRead() ← 请求体反序列化后处理 8. HandlerMethodArgumentResolver.supportsParameter() ← 判断是否支持参数解析 9. HandlerMethodArgumentResolver.resolveArgument() ← 解析方法参数 10. 执行控制器方法 11. Interceptor.postHandle() ← 拦截器后置处理 12. 返回响应(类似步骤4-9的流程处理响应体) 13. Interceptor.afterCompletion() ← 请求完成后处理 ``` | 组件 | 作用范围 | 核心场景 | |-------------------------------|---------------------|--------------------| | HandlerMethodArgumentResolver | 单个方法参数的解析 | 自定义参数类型、注解驱动解析 | | RequestBodyAdvice | 请求体整体处理(反序列化前) | 请求体加密 / 解密、日志记录 | | HttpMessageConverter | HTTP 消息与 Java 对象的转换 | 自定义 JSON/XML 序列化格式 | | Interceptor | 请求处理前后的拦截 | 日志、权限校验、性能监控 | ###### XML方式 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml DispatcherServlet org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.XmlWebApplicationContext contextConfigLocation classpath:applicationContext/spring-mvc.xml 2 DispatcherServlet /spring/* ``` ###### 配置类方式 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml DispatcherServlet org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext contextConfigLocation xyz.lisys.config.SpringMvcConfiguration 2 DispatcherServlet /spring/* ``` #### Servlet容器中获取Spring上下文 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml org.springframework.web.context.request.RequestContextListener ``` #### 解决SpringMVC请求参数中文乱码 - CharacterEncodingFilter - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml characterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 characterEncodingFilter /spring/* ``` #### SpringMvc 响应类型 ###### 采用HttpMessageConverter对返回的实体对象执行转换操作并写入响应正文(响应体) - @ResponseBody - HttpEntity - ResponseBodyEmitter - ResponseEntity - ModelAndView ###### 文件资源类 - ClassPathResource - 类文件资源的映射,它通过 ClassLoader 的getResourceAsStream 方法访问指定路径 path 的类文件资源 - UrlResource - Url类资源的映射 - FileUrlResource - 继承自 UrlResource,和 UrlResource 的主要区别是它主要针对文件系统的 Url 资源实现了 WritableResource 接口,是一个可写的资源,如果它内部的 Url 资源指向的不是文件系统的文件,则使用 WritableResource 接口时会产生异常 - ByteArrayResource - 内部存有一个字节数组类型的成员变量,该变量存储的内容本身就是资源 - FileSystemResource - PathResource - 访问文件系统的指定路径下的文件资源 - VfsResource - 用于访问 JBoss 框架的 VFS(虚拟文件系统) 资源,它通过反射的方式调用相关类的方法获取资源 - DescriptiveResource - 并不直接指向任何一个资源,它可以被当做一个占位符使用 - ServletContextResource - 映射的是 web 应用服务器的文件资源,资源路径的根目录跟 web 应用的主目录对应 ###### 转换器 - HttpMessageConverter - org.springframework.http.converter.StringHttpMessageConverter - 负责读取字符串格式的数据和写出二进制格式的数据 - org.springframework.http.converter.ResourceHttpMessageConverter - 负责读取资源文件和写出资源文件数据 - org.springframework.http.converter.json.MappingJackson2HttpMessageConverter - 解析JSON数据 - org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter - 解析XML数据 ###### 配置文件上传 ```xml ``` ###### 配置视图解析器:ViewResolver ```xml ``` #### 参数映射转换 ```java @RestController @RequestMapping("/property") public class PropertyEditorController { @Resource private CustomPropertyEditorRegistrar customPropertyEditorRegistrar; @InitBinder public void init(WebDataBinder binder) { // 调用registerCustomEditors方法向当前DateBinder注册PropertyEditor customPropertyEditorRegistrar.registerCustomEditors(binder); } @GetMapping("/param1") public String param1() { return "ok"; } } ``` #### 自定义Http消息转换器 ```java import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; public class CustomMessageConverter extends AbstractHttpMessageConverter { public CustomMessageConverter() { // 指定支持的媒体类型 super(new MediaType("application", "x-custom", StandardCharsets.UTF_8)); } @Override protected boolean supports(Class clazz) { return MyCustomType.class.isAssignableFrom(clazz); } @Override protected MyCustomType readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException { // 从输入流读取数据并转换为 MyCustomType try (InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), StandardCharsets.UTF_8)) { StringBuilder content = new StringBuilder(); char[] buffer = new char[1024]; int bytesRead; while ((bytesRead = reader.read(buffer)) != -1) { content.append(buffer, 0, bytesRead); } // 解析自定义格式(示例:假设格式为 "key=value") String[] parts = content.toString().split("="); return new MyCustomType(parts[0], parts[1]); } catch (Exception e) { throw new HttpMessageNotReadableException("Failed to read custom message", e); } } @Override protected void writeInternal(MyCustomType obj, HttpOutputMessage outputMessage) throws IOException { // 将 MyCustomType 对象写入输出流 try (OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody(), StandardCharsets.UTF_8)) { writer.write(obj.getKey() + "=" + obj.getValue()); } catch (Exception e) { throw new HttpMessageNotWritableException("Failed to write custom message", e); } } } ``` #### 常用注解 - @InitBinder:用于初始化DataBinder,可注册自定义编辑器或格式化器。 - @RestController:是@Controller和@ResponseBody的组合注解,用于创建RESTful控制器,返回JSON/XML格式响应。 - @RequestMapping:用于映射HTTP请求到控制器的处理方法,可指定路径、请求方法等。 - @GetMapping:是@RequestMapping(method = RequestMethod.GET)的快捷方式,处理HTTP GET请求。 - @ExceptionHandler:用于处理控制器中抛出的特定异常,返回自定义响应。 - @ControllerAdvice:用于定义全局控制器增强,可包含@ExceptionHandler、@InitBinder等方法。 - @RestControllerAdvice:是@ControllerAdvice和@ResponseBody的组合,用于全局处理REST控制器的异常。 - @RequestHeader:用于获取HTTP请求头中的值,并绑定到方法参数。 - @RequestParam:用于获取URL中的查询参数,并绑定到方法参数。 - @RequestPart:用于处理multipart/form-data请求,通常用于文件上传。 - @RequestBody:用于将HTTP请求体绑定到方法参数,常与JSON/XML格式数据结合使用。 - @PathVariable:用于获取URL中的路径变量,并绑定到方法参数。 - @ModelAttribute:用于将请求参数绑定到模型对象,或在视图中暴露对象。 - @ResponseBody:用于将方法返回值直接写入HTTP响应体,而不是解析为视图。 #### 文件配置 - 1)配置:SpringMvcConfiguration.java ```java /** * Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制 * 可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter *

* WebMvcConfigurer 为接口 * WebMvcConfigurerAdapter 是 WebMvcConfigurer 的实现类大部分为空方法 ;是WebMvcConfigurer的子类实现,由于Java8中可以使用default关键字为接口添加默认方法,为在源代码中spring5.0之后就已经弃用本类。如果需要我接着可以实现WebMvcConfigurer类 * WebMvcConfigurationSupport 是mvc的基本实现并包含了WebMvcConfigurer接口中的方法 *

* * ImportResource引入资源文件有三种方式: * 1.直接引入,该路径就是src/resources/下面的文件:file * 2.classpath引入:该路径就是src/java下面的配置文件:classpath:file * 3.引入本地文件:该路径是一种绝对路径:file:D://.... * \@ImportResource(locations = {"personal.xml"}) */ @Configuration @EnableWebMvc public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public Validator getValidator() { // ResourceBundleMessageSource source = new ResourceBundleMessageSource(); // source.setBasename("messages"); System.out.println("getValidator"); LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); validator.setProviderClass(HibernateValidator.class); // validator.setValidationMessageSource(source); return validator; } @Override public void configurePathMatch(PathMatchConfigurer configurer) { // 结尾斜线有没有都触发匹配 configurer.setUseTrailingSlashMatch(true); } // @Bean // public CorsFilter corsFilter() { // // 启用跨域后会检测Origin请求头字段 // final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); // final CorsConfiguration corsConfiguration = new CorsConfiguration(); // corsConfiguration.setAllowCredentials(true); // corsConfiguration.addAllowedOrigin("*"); // corsConfiguration.addAllowedHeader("*"); // corsConfiguration.addAllowedMethod(HttpMethod.GET); // corsConfiguration.addAllowedMethod(HttpMethod.POST); // corsConfiguration.addAllowedMethod(HttpMethod.OPTIONS); // urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); // return new CorsFilter(urlBasedCorsConfigurationSource); // } } ``` - 1)配置:SpringMVC.xml 同效配置 ```xml ``` ### Mybatis #### 文件配置 - 1)mybatis-config.xml ```xml ``` #### 参数获取 > - 在 MyBatis 中,_parameter 是一个内置参数,用于表示传递给 SQL 语句的整个参数对象 > - 当我们传递一个 List 实例或者数组作为参数对象传给 MyBatis > - 当你这么做的时候,MyBatis会自动将它包装在一个 Map 中,用名称在作为键 > - List 实例将会以 `list` 作为键,而数组实例将会以 `array` 作为键 > - 用#{0}、#{1}、…表示参数1、参数2、… > - 用#{param1}、#{param2}、…表示参数1、参数2、… > - @Param 可以指定入参的名称 #### 一级缓存/二级缓存 - 一级缓存是 SqlSession 级别的,每个 SqlSession 都有自己独立的缓存,且默认开启 - 一级缓存不会被触发的一些情况 - 不同的 SqlSession 实例 - 执行了更新操作(INSERT、UPDATE、DELETE) - 手动清空缓存 - 使用了不同的查询参数 - 添加 @Options(flushCache = Options.FlushCachePolicy.TRUE) 配置选项 - sqlSessionTemplate.clearCache() 手动清除缓存 #### 常用标签 - cache:缓存命名空间的配置 - property:缓存的具体配置属性 - cache-ref:引用其他命名空间的缓存配置 - include:引入其他SQL片段 - sql:定义可重用的SQL代码片段 - choose:相当于Java中的switch语句,提供分支选择 - when:choose中的条件分支,相当于switch中的case - otherwise:choose中其他条件都不满足时执行的分支,相当于switch中的default - if:条件判断,满足条件时包含SQL片段 - foreach:集合遍历,用于动态生成SQL中的IN条件或批量操作 - where:智能处理WHERE子句,自动去除多余的AND或OR - bind:创建一个变量并将其绑定到上下文 - trim:自定义字符串处理,可用于自定义where、set等功能 - resultMap:自定义结果集映射 - id:映射结果集的主键 - result:映射结果集的普通字段 - discriminator:结果集的鉴别器,用于根据不同值选择不同的结果映射 - case:鉴别器的分支情况 - select:查询语句 - insert:插入语句 - selectKey:在插入语句前后执行的查询,用于获取自增主键等 - update:更新语句 - set:智能处理SET子句,自动去除多余的逗号 - delete:删除语句 #### 常用注解 以下是MyBatis框架中常用注解的简要说明: - @CacheNamespace:声明命名空间的缓存配置,支持LRU、FIFO等策略。 - @CacheNamespaceRef:引用其他命名空间的缓存配置。 - @Select:映射SQL查询语句,替代XML配置。 - @Insert:映射SQL插入语句。 - @Update:映射SQL更新语句。 - @Delete:映射SQL删除语句。 - @SelectProvider:动态生成查询SQL,通过指定的类和方法。 - @InsertProvider:动态生成插入SQL。 - @UpdateProvider:动态生成更新SQL。 - @DeleteProvider:动态生成删除SQL。 - @Results:定义结果集映射规则,替代XML中的``。 - @Result:定义单个字段的映射关系。 - @ResultMap:引用已定义的结果集映射。 - @ResultType:指定返回结果的类型。 - @MapKey:将结果集转换为Map时,指定作为键的属性。 - @One:实现一对一关联查询(懒加载)。 - @Many:实现一对多关联查询(懒加载)。 - @TypeDiscriminator:根据字段值动态选择结果映射。 - @ConstructorArgs:通过构造函数注入结果集。 - @Arg:定义构造函数参数的映射。 - @Flush:清空缓存并提交事务。 - @Lang:指定SQL解析器(如OGNL、Groovy)。 - @Mapper:标记接口为MyBatis映射器,无需XML配置。 - @Options:配置SQL执行选项(如缓存、主键生成)。 - @Param:为方法参数命名,用于SQL中引用。 - @SelectKey:在插入/更新前后执行SQL,获取主键值。 - @Intercepts:标记MyBatis插件拦截器。 - @Signature:定义拦截器的方法签名(类型、方法、参数)。 #### 常用类 - VendorDatabaseIdProvider 用于根据不同的数据库厂商来提供对应的数据库标识(databaseId) - TransactionManagementConfigurer 用于配置事务管理器,它在 Spring 事务管理的自定义配置方面发挥着重要作用 #### 结果映射 - 1)mapper.xml ```xml ``` #### 分页插件 - PageInterceptor - 1)配置注入 ```xml ``` ### mybatis-spring #### 文件配置 - 1)mybatis.xml ```xml ``` ### Flyway #### 基本概念说明 > * Flyway在第一次执行时,会创建一个默认名为flyway_schema_history的历史记录表, > * 这张表会用来跟踪或记录数据库的状态,然后每次项目启动时都会自动扫描在resources/db/migration下的文件的版本号 > * 并且通过查询flyway_schema_history来判断是否有新增文件,从而判断是否进行迁移 > * 常用命令Migrate, Clean, Info, Validate, Baseline, Repair > * 官方提供了命令行、API、以及 Maven 和 Gradle 插件的方式 > * 后续修改sql脚本需要在 db.migration 中新建 V__.sql 脚本进行执行 #### 文件配置 - 1)配置 FlywayConfiguration.java ```java @Configuration public class FlywayConfiguration { @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public Flyway flyway() { // val classicConfiguration = new ClassicConfiguration(); // classicConfiguration.setDataSource(url, username, password); // classicConfiguration.setLocationsAsStrings("classpath:db/migration"); // classicConfiguration.setBaselineOnMigrate(true); // new Flyway(classicConfiguration); /* FluentConfiguration - dataSource 用于配置数据库连接 - locations - encoding 设置SQL迁移的编码 - cleanOnValidationError - baselineOnMigrate 当对没有模式历史表的非空模式执行迁移时,是否自动调用基线 - load */ return Flyway.configure() .dataSource(url, username, password) .locations("classpath:db/migration") .baselineOnMigrate(true) .load(); } @Bean public FlywayMigrationInitializer flywayMigrationInitializer(Flyway flyway) { return new FlywayMigrationInitializer(flyway); } @Data @AllArgsConstructor public static class FlywayMigrationInitializer implements InitializingBean { private Flyway flyway; @Override public void afterPropertiesSet() { flyway.migrate(); } } } ``` - 2)配置 Maven Plugin ```xml org.flywaydb flyway-maven-plugin 7.15.0 jdbc:mysql://localhost:3306/demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8 root 123456 filesystem:${project.basedir}/src/main/resources/db/migration true UTF-8 ``` ### Swagger2 #### 文件配置 - 1)配置:pom.xml ```xml io.springfox springfox-boot-starter 3.0.0 ``` - 2)配置:Swagger2Configuration ```java @Configuration @EnableSwagger2 @Import(OpenApiDocumentationConfiguration.class) public class Swagger2Configuration { @Bean public Docket petApi() { return new Docket(DocumentationType.OAS_30) .apiInfo( new ApiInfoBuilder() .title("SSM API") .description("SSM演示程序") .termsOfServiceUrl("http://lisys.club") .contact(new Contact("SystemLight", "http://lisys.club", "123@qq.com")) .license("MIT") .licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE") .version("1.0") .build() ) .enable(true) .pathProvider(new CustomPathProvider()) // .pathMapping("/spring") .select() .apis(RequestHandlerSelectors.basePackage("xyz.lisys.controller")) .paths(PathSelectors.any()) .build(); } public static class CustomPathProvider extends DefaultPathProvider { @Override public String getOperationPath(String operationPath) { return super.getOperationPath(operationPath).replace("/ssm", "/ssm/spring"); } } } ``` #### 常用注解 - @Api: 资源描述 - @ApiIgnore: 资源过滤 - @ApiOperation: 方法描述 - @ApiImplicitParam(s): 隐式参数描述 - @ApiParam: 参数描述 - @ApiResponse(s): 返回参数描述 - @ApiModel: 实体类描述 - @ApiModelProperty: 实体类成员描述 ### spring-aop #### 基本概念说明 - 动态代理技术 - proxy-target-class="true" 使用 cglib 代理,反之使用 JDK 代理 ,同时支持注解方式配置该属性 - JDK的动态代理:Java Proxy 必须有接口定义,返回代理对象 - CGLIB:使用字节码处理框架ASM,来转换字节码并生成新的类 - 通过切点和切面进行连接 - 切面(Aspect):定义一个横切关注点,包括需要执行的代码和它所执行的位置。 - 连接点(Joinpoint):程序执行过程中的某个特定点,比如方法的调用或异常的抛出等。 - 切入点(Pointcut):应用于连接点的一个或多个表达式,用于选择出需要被切入的连接点(方法)。 - 通知|增强(Advice):切面所定义的横切关注点所要执行的代码。通知类型有 Before、After、Around、AfterReturning、AfterThrowing 等几种。 - 织入(Weaving):将切面和目标对象(通常是一组类)连接起来,创建一个新的代理对象,代理对象具有切面所定义的横切关注点的行为。有编译时织入、类加载时织入、运行时织入三种方式。 - 引入(Introduction):在不修改类结构的情况下引入新的方法或属性。 - 目标对象(Target):需要被通知的对象。 - AOP代理(AOP Proxy):AOP框架动态创建的对象,用于连接目标对象和切面对象。 - 织入器(Weaver):实现织入过程的程序,可以使用编译时织入、类加载时织入、运行时织入等方式将切面织入到目标对象中。 - 基于注解的AOP:使用注解来进行 AOP 的配置,主要包括 `@Aspect`、`@Pointcut`、`@Before`、`@After`、`@Around`、`@AfterReturning`、 `@AfterThrowing` 等注解。 - AOP五种通知类型: - 前置通知(Before Advice):在目标方法执行前调用,可以在方法执行前完成一些预处理操作,如设置事务、权限等。实现该通知类型需要实现 `org.springframework.aop.MethodBeforeAdvice` 接口。 - 后置通知(After Advice):在目标方法执行后调用,可以获得目标方法的返回结果,并对其进行处理。实现该通知类型需要实现 `org.springframework.aop.AfterReturningAdvice` 接口。 - 环绕通知(Around Advice):在目标方法执行前后均可以调用,可以在方法执行前后完成一些预处理和后处理操作,如计时、日志输出等。实现该通知类型需要实现 `org.aopalliance.intercept.MethodInterceptor` 接口。 - 抛出异常后通知(After Throwing Advice):在目标方法抛出异常后调用,可以对异常进行处理和记录。实现该通知类型需要实现 `org.springframework.aop.ThrowsAdvice` 接口。 - 强制使用最终通知(Introduction Advice):用于向目标类中添加方法或属性,强制使用最终通知可以在目标类中动态地添加方法和属性。实现该通知类型需要继承 `org.springframework.aop.IntroductionInterceptor` 类。 #### 表达式 - 访问修饰符 返回值\[\*] 包名\[\*|..].类名\[\*].方法\[*](参数类型\[..]) - execution: 用于匹配方法执行的连接点(Joinpoint) - within: 用于匹配指定类型内的方法执行,不关心方法的签名 - this: 用于匹配当前代理对象(即bean)的实例方法执行,其中代理对象是指定的类型 - target: 类似于this,但用于匹配目标对象(即被代理对象)的实例方法执行 - bean: 匹配所有匹配bean里面的方法 - args: 用于匹配方法执行时参数为指定类型的连接点 - @annotation: 用于匹配方法执行时,方法上具有指定注解的连接点 - @within: 用于匹配方法执行时,方法所在类(或父类、接口)上具有指定注解的连接点,级别为@Retention(RetentionPolicy.CLASS) - @target: 类似于@within,但通常用于Spring AOP的某些文档或示例中,更标准的表达是@target,级别为@Retention( RetentionPolicy.RUNTIME) - @args: 用于匹配方法执行时,方法的参数上具有指定注解的连接点 ### spring-tx #### 基本概念说明 **一、Spring 事务框架的核心组件和流程** 1. **事务上下文创建**: - 在 Spring 中,事务上下文的创建是通过 `spring-tx` 相关组件实现的。`@Transactional` 注解可以用于修饰方法,当这些方法被调用时,会触发 Spring 的事务管理机制。 - `TransactionDefinition` 包含了事务的各种配置信息,包括事务的隔离级别。可以通过该对象获取在 `@Transactional` 注解中配置的事务隔离级别。 2. **数据库操作与事务包裹**: - 在使用 `jdbcTemplate` 和 `sqlSessionTemplate` 进行数据库查询操作时,当它们处于 `@Transactional` 注解修饰的方法内部,这些查询操作会被事务包裹。 - 对于 MyBatis 的集成,使用已配置的 `DataSource` 对象进行查询。在与 Spring 整合时,`sqlSessionFactory` 首先会通过 `SqlSessionUtils -> TransactionSynchronizationManager.getResource()` 尝试获取当前线程绑定的连接。这是因为 Spring 会将事务与线程进行关联,确保在同一个事务内的操作使用相同的数据库连接,以保证事务的一致性。 - 如果通过 `TransactionSynchronizationManager.getResource()` 无法获取到上下文(即没有绑定的连接),则会调用 `sessionFactory.openSession()` 来获取一个新的会话,这可能会创建一个新的连接,具体取决于 `sessionFactory` 的实现和配置。 3. **事务状态的获取和操作**: - `TransactionAspectSupport.currentTransactionStatus()` 可用于获取当前的事务状态。这对于在代码中动态地检查事务状态,或者根据事务状态进行不同的处理非常有用。 - 例如,在某些复杂的业务逻辑中,可能需要根据事务是否正在进行,以及事务的不同阶段(如已提交、未提交、回滚等)来决定是否执行某些操作,该方法可以为这些逻辑提供事务状态信息。 **二、整体事务处理流程总结** - `TransactionInterceptor` 是 Spring AOP 实现事务管理的关键组件。 - `TransactionInterceptor.invoke()` 方法会拦截 `@Transactional` 注解修饰的方法调用。 - `TransactionInterceptor(TransactionAspectSupport).invokeWithinTransaction()` 是核心的处理方法,它会在方法调用前后进行事务管理的逻辑,包括事务的开始、提交或回滚操作。 - `TransactionInterceptor(TransactionAspectSupport).createTransactionIfNecessary()` 会根据方法上的 `@Transactional` 注解信息判断是否需要创建一个新的事务,如果需要,它会调用 `DataSourceTransactionManager` 等相关组件来创建和开始事务。 - `PlatformTransactionManager` 是 Spring 事务管理的核心接口,负责事务的创建、提交、回滚等操作。 - `DataSourceTransactionManager` 是 `PlatformTransactionManager` 的一个具体实现,它通常用于基于 JDBC 的事务管理。它继承自 `AbstractPlatformTransactionManager`,并提供了具体的事务管理逻辑,例如: - `DataSourceTransactionManager(AbstractPlatformTransactionManager, PlatformTransactionManager).getTransaction()` 方法会根据配置信息获取事务。 - `DataSourceTransactionManager(AbstractPlatformTransactionManager, PlatformTransactionManager).doBegin()` 方法会开始一个新的事务,涉及到资源的获取(如数据库连接)和事务状态的初始化等操作。 - `MasterSlaveDataSource(DataSource)` 可能是一个自定义的数据源实现,在获取连接时通过 `MasterSlaveDataSource(DataSource).getConnection()` 方法,会根据主从数据源的配置来获取相应的数据库连接,在多数据源的场景下,它可以灵活地选择主数据源或从数据源进行操作。 - 调用流程:TransactionInterceptor -> TransactionAspectSupport -> AbstractPlatformTransactionManager -> DataSourceTransactionManager -> TransactionSynchronizationManager **三、注意事项** 1. 事务的隔离级别设置需要根据具体的业务需求和性能要求进行权衡,不同的隔离级别适用于不同的业务场景。 2. 在使用多数据源时,`MasterSlaveDataSource` 等自定义数据源的配置和使用需要注意连接的分配和事务的一致性。 3. 对于 `TransactionInterceptor` 的使用,需要确保其正确配置在 Spring 的 AOP 配置中,以便对 `@Transactional` 注解进行正确的拦截和事务管理。 4. 事务的范围和生命周期需要仔细考虑,避免将不需要事务的操作纳入事务范围,以减少性能开销。 #### 文件配置 - 1)配置 transaction.xml ```xml ``` ### spring-context #### Async - 异步方法 - 使用注意 > - 默认采用 Java Proxy 进行代理切片要求必须有接口实现注入接口 > - 如下方式会使 @Async 失效 > - 一、异步方法使用static修饰 > - 二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类 > - 三、异步方法不能与异步方法在同一个类中,如果非要在同一个类不同方法中可以采用spring容器getBean()的方式获取 > - 四、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象,getBean()的方式也可以 > - 五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解 > - 六、在 Async 方法上标注 @Transactional 是没用的,在 Async 方法调用的方法上标注 @Transactional 有效 - 1)配置 AsyncConfiguration.java ```java @Configuration @EnableAsync @Slf4j public class AsyncConfiguration implements AsyncConfigurer { // 核心线程数 private static final int CORE_POOL_SIZE = 5; // 最大线程数 private static final int MAX_POOL_SIZE = 5; // 队列大小 private static final int QUEUE_CAPACITY = 50; // 线程池中的线程的名称前缀 private static final String THREAD_NAME = "MyExecutor-"; @Override public ThreadPoolTaskExecutor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 配置核心线程数 executor.setCorePoolSize(CORE_POOL_SIZE); // 配置最大线程数 executor.setMaxPoolSize(MAX_POOL_SIZE); // 配置队列大小 executor.setQueueCapacity(QUEUE_CAPACITY); // 配置线程池中的线程的名称前缀 executor.setThreadNamePrefix(THREAD_NAME); // 配置线程池拒绝策略,我设置为CallerRunsPolicy,当线程和队列都满了,由发起线程的主线程自己执行 /* 拒绝策略: - CallerRunsPolicy:线程调用运行该任务的 execute 本身 - AbortPolicy:处理程序遭到拒绝将抛出运行时异常 - DiscardPolicy:不能执行的任务将被删除但不抛出异常 - DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序 */ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new MyAsyncUncaughtExceptionHandler(); } private static class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(@NonNull Throwable ex, @NonNull Method method, @NonNull Object... params) { log.error("输出报错信息"); } } } ``` - 2)配置 AsyncServiceImpl.java ```java @Service @Slf4j public class AsyncServiceImpl implements AsyncService { @Async public void test1() throws InterruptedException { log.info("---------test1 start: {}", Thread.currentThread().getName()); Thread.sleep(5000); log.info("---------test1 end: {}", Thread.currentThread().getName()); } @Async public Future test2() throws InterruptedException { Thread.sleep(5000); return new AsyncResult<>("hello world"); } } ``` #### Scheduling - 定时任务 - 1)配置 DynamicSchedulingConfig.java ```java @Configuration @EnableScheduling public class DynamicSchedulingConfig implements SchedulingConfigurer { @Autowired private TickService tickService; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { // 通过 @Bean 可以注册多个 Executor 对象,@Async("executor") 可以指定使用的名称 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); // 默认情况下,Spring 使用本地单线程调度器来运行任务 // 因此,即使有多个 @Scheduled 方法,每个方法都需要等待线程完成前一个任务的执行 // 如果任务确实是独立的,那么并行运行它们会更方便 // ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler(); // executor.setPoolSize(5); // executor.setThreadNamePrefix("ThreadPoolTaskScheduler"); // executor.initialize(); // 动态配置改变延时时间 taskRegistrar.setScheduler(executor); taskRegistrar.addTriggerTask( new Runnable() { @Override public void run() { tickService.tick(); } }, new Trigger() { @Override public Date nextExecutionTime(TriggerContext context) { Optional lastCompletionTime = Optional.ofNullable(context.lastCompletionTime()); Instant nextExecutionTime = lastCompletionTime.orElseGet(Date::new).toInstant().plusMillis(tickService.getDelay()); return Date.from(nextExecutionTime); } } ); } } ``` ### spring-data-redis - 1)配置:redis.xml ```xml ``` ### spring-test #### 结合 junit4 测试 - 1)配置:SpringForTest.java ```java @RunWith(SpringJUnit4ClassRunner.class) // 让测试运行于Spring测试环境,默认是JUnit4.class @ContextConfiguration("classpath:applicationContext/bean.xml") public class SpringForTest { @Resource public Animal animal; @Value("2020-12-12") public Date date; } ``` ### spring-security #### 基本概念说明 ###### 启用方式 - @EnableMethodSecurity - 已弃用 - @EnableGlobalMethodSecurity - 用于启用全局方法安全性,可以与`GlobalMethodSecurityConfiguration`共同使用 - 启用后通过结合使用不同的注解,如 @PreAuthorize、@PostAuthorize、@Secured 等,你可以灵活地定义方法的访问权限和条件 - securedEnabled 当设置为 true 时,启用 @Secured 注解的处理 - prePostEnabled 当设置为 true 时,启用 @PreAuthorize 和 @PostAuthorize 注解的处理 - jsr250Enabled 当设置为 true 时,启用 JSR-250 注解(如 @RolesAllowed、@PermitAll、@DenyAll)的处理 - Spring Security 支持三种内置的方法授权注解: - prePostEnabled 用于 Spring pre/post 注解。 - securedEnabled 用于 Spring @Secured 注解。 - jsr250Enabled 用于标准的 Java @RoleAllowed 注解 - @EnableWebSecurity - 用于启用基于 Web 的安全性,可以与`WebSecurityConfigurerAdapter`共同使用 ###### 验证模式 - HTTP BASIC authentication headers:基于IETF RFC 标准 - HTTP Digest authentication headers:基于IETF RFC 标准 - HTTP X.509 client certificate exchange:基于IETF RFC 标准 - LDAP:跨平台身份验证 - Form-based authentication:基于表单的身份验证 - Run-as authentication:用户用户临时以某一个身份登录 - OpenID authentication:去中心化认证 ###### 与以下 Servlet API 方法集成 - HttpServletRequest#getRemoteUser() - HttpServletRequest.html#getUserPrincipal() - HttpServletRequest.html#isUserInRole(java.lang.String) - HttpServletRequest.html#login(java.lang.String, java.lang.String) - HttpServletRequest.html#logout() #### 注入验证过滤器 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /spring/* ``` - 2)配置:security.xml ```xml ``` - 3)配置:SecurityConfiguration.java ```java @EnableWebSecurity public class SecurityConfiguration // extends WebSecurityConfigurerAdapter { @Bean public UserDetailsService userDetailsService( // AuthenticationManagerBuilder authenticationManagerBuilder ) throws Exception { // UserDetailsService 底层为 AuthenticationManagerBuilder 所配置构建 // AuthenticationManager本身并不直接执行认证逻辑,而是将认证请求委托给一个或多个AuthenticationProvider来处理 log.info("userDetailsService 初始化"); // UserDetailsService 的一个实现,它将用户信息存储在内存中,负责根据用户名检索用户信息(如密码、角色、权限等) InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); // new JdbcUserDetailsManager(); // 为了让我们的登录能够运行 这里我们初始化一个用户Felordcn 密码采用明文 当你在密码12345上使用了前缀{noop} 意味着你的密码不使用加密, // authorities 一定不能为空 这代表用户的角色权限集合 // authorities 结合 @PreAuthorize("hasAuthority('p1')") 注解使用 // UserDetails felordcn = User.withUsername("Felordcn").password("{noop}12345").authorities(AuthorityUtils.NO_AUTHORITIES).build(); manager.createUser(User.withUsername("admin").password("654321").authorities("p1").build()); return manager; } @Bean public PasswordEncoder passwordEncoder() { /* 加密方式与对应的类 noop - NoOpPasswordEncoder bcrypt - BCryptPasswordEncoder (Also used for encoding) ldap - LdapShaPasswordEncoder MD4 - Md4PasswordEncoder MD5 - new MessageDigestPasswordEncoder("MD5") pbkdf2 - Pbkdf2PasswordEncoder scrypt - SCryptPasswordEncoder SHA-1 - new MessageDigestPasswordEncoder("SHA-1") SHA-256 - new MessageDigestPasswordEncoder("SHA-256") sha256 - StandardPasswordEncoder */ // 用于密码编码和解码,NoOpPasswordEncoder 是无加密方式 // String idForEncode = "bcrypt"; // Map encoders = new HashMap<>(); // encoders.put(idForEncode, new BCryptPasswordEncoder()); // encoders.put("noop", NoOpPasswordEncoder.getInstance()); // encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()); // encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()); // encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()); // encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()); // encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()); // encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()); // encoders.put("sha256", new StandardPasswordEncoder()); // PasswordEncoderFactories.createDelegatingPasswordEncoder(); return NoOpPasswordEncoder.getInstance(); } @Bean WebSecurityCustomizer webSecurityCustomizer() { // return web -> web.ignoring() // .antMatchers("/error") // .requestMatchers(PathRequest.toStaticResources().atCommonLocations()); // 允许自定义 SecurityFilterChain 的配置,提供了一种编程方式来配置 Spring Security,允许开发者通过 Java 配置而不是 XML 或注解来定义安全策略 return web -> web.ignoring().antMatchers("/hello"); } @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { /* 开启基于表单的用户登录 http.formLogin(); loginPage 用户登录页面跳转路径 successForwardUrl 用户登录成功后的重定向地址 successHandler 用户登录成功后的处理 defaultSuccessUrl 用户登录成功后的跳转地址 failForwardUrl 登陆失败后重定向地址 failureUrl 用户登录后的跳转地址 failureHandler 用户登录失败后的错误处理 usernameParameter 登录用户的用户名参数,默认为 username passwordParameter 登录用户的密码参数,默认为 password loginProcessingUrl 登录表单提交的路径,默认为post请求的/login permitAll 无条件对请求进行放行 */ // 开启基于HTTP请求的Basic认证登录 // http.httpBasic(); /* 开启基于HttpServletRequest请求访问的限制 http.authorizeRequests(); antMatchers 开启Ant风格的路径匹配 mvcMatchers 开启MVC风格的路 regexMatchers 开启正则表达式的路径匹配 and() 功能连接符 anyRequest() 匹配任何请求 rememberMe() 开启记住我功能 access 匹配给定的 SpEL 表达式计算结果是否为 true hasAnyRole 匹配用户是否有参数中的任意角色 hasRole 匹配用户是否有某个角色 hasAnyAuthority 用户是否有参数中的任意权限 hasAuthority 匹配用户是否有某一个权限 authenticated() 匹配已经登录认证的用户 fullyAuthenticated 匹配完整登录认证的用户(非 rememberMe用户登录) haslpAddress 匹配某IP地址的访问请求 permitAll() 无条件对请求进行放行 */ // 只对指定路径请求进行过滤 // http.antMatcher(); /* 开启退出登录的支持 http.logout(); logoutUrl 出处理控制URL,默认为post请求 logoutSuccessUrl 用户退出成功后的重定向地址 logoutSuccessHandler 用户退出成功后的处理器设置 deleteCookies 用户退出后删除指定 Cookie invalidateHttpSession 用户退出后是否立即清除Session (默认为false) clearAuthentication 用户退出后是否立即清除Authentication(默认为true) */ /* 开启Session 管理配置 http.sessionManagement(); sessionCreationPolicy(SessionCreationPolicy.STATELESS) session生成策略 invalidSessionUrl(String invalidSessionUrl): 指定会话失效时(请求携带无效的 JSESSIONID 访问系统)重定向的 URL,默认重定向到登录页面。 invalidSessionStrategy(InvalidSessionStrategy invalidSessionStrategy):指定会话失效时(请求携带无效的 JSESSIONID 访问系统)的处理策略。 maximumSessions(int maximumSessions):指定每个用户的最大并发会话数量,-1 表示不限数量。 maxSessionsPreventsLogin(boolean maxSessionsPreventsLogin):如果设置为 true,表示某用户达到最大会话并发数后,新会话请求会被拒绝登录;如果设置为 false,表示某用户达到最大会话并发数后,新会话请求访问时,其最老会话会在下一次请求时失效并根据 expiredUrl() 或者expiredSessionStrategy() 方法配置的会话失效策略进行处理,默认值为 false。 expiredUrl(String expiredUrl):如果某用户达到最大会话并发数后,新会话请求访问时,其最老会话会在下一次请求时失效并重定向到 expiredUrl。 expiredSessionStrategy(SessionInformationExpiredStrategy expiredSessionStrategy):如果某用户达到最大会话并发数后,新会话请求访问时,其最老会话会在下一次请求中失效并按照该策略处理请求。注意如果本方法与 expiredUrl() 同时使用,优先使用 expiredUrl() 的配置。 sessionRegistry(SessionRegistry sessionRegistry):设置所要使用的 sessionRegistry,默认配置的是 SessionRegistryImpl 实现类。 */ /* 开启记住我功能 http.rememberMe(); rememberMeParameter 指示在登录时记住用户的HTTP参数 key 记住我认证生成的Token令牌标识 tokenValiditySeconds 记住我Token令牌有效期,单位为s (秒) tokenRepository 指定要使用的PersistentTokenRepostory,用来配置持久化Token令牌 alwaysRemember 是否应该始终创建记住我Cookie,默认为false clearAuthentication 是否设置Cookie为安全的,如果设置为true,则必须通过HTTPS进行连接请求 */ /* 配置CSRF跨站请求伪造防护功能 http.csrf(); disable 关闭Security默认开启的CSRF防御功能 csrfTokenRepository 指定要使用的CsrfTokenRepository ( Token令牌持久化仓库)。默认是由LazyCsrfTokenRepository 包装的HttpSessionCsrfTokenRepository requireCsrfProtectionMatcher 指定针对什么类型的请求应用CSRF防护功能默认设置是忽珞GET、HEAD、TRACE和OPTIONS请求,而处理并防御其他所有请求 ignoringAntMatchers */ /* 配置Spring Security以禁用“X-Frame-Options”响应头的设置 headers().frameOptions().disable() */ /* addFilterBefore() 向 Spring Security 的过滤器链中添加一个新的过滤器,并且指定这个新过滤器应该位于某个过滤器之前 .requestMatchers().antMatchers() 可以自定义匹配路径,进行添加指定过滤器 */ // http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); // 可以在密码验证之前增加一个Token验证过滤器 /* return http.csrf(AbstractHttpConfigurer::disable) .authorizeRequests(auth -> auth.anyRequest().authenticated()) .formLogin(Customizer.withDefaults()) .httpBasic(Customizer.withDefaults()) .build(); */ // Spring Security 5 引入的一个新概念,它代表了一组按顺序执行的过滤器,这些过滤器共同负责处理 HTTP 请求和响应,以实现安全策略 log.info("http 初始化"); return http.authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic() .and() .build(); } // @Override // public void configure(WebSecurity web) throws Exception { // // 配置 Spring Security 中的过滤器链 // // web.ignoring().antMatchers("/hello"); // } // // @Override // protected void configure(HttpSecurity http) throws Exception { // // 配置 Spring Security 中的过滤器链 // http.authorizeRequests() // .anyRequest().authenticated() // .and() // .httpBasic(); // } // @Override // public void configure(AuthenticationManagerBuilder auth) throws Exception { // auth.userDetailsService(new UserDetailsService() { // @Override // public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // return null; // } // }).passwordEncoder(NoOpPasswordEncoder.getInstance()); // } } ``` ### spring-security-oauth2 #### 基本概念说明 - [理解OAuth 2.0](https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html) - [spring security oauth2 常用授权方式配置详细教程](https://blog.csdn.net/tiancxz/article/details/108856337) ###### 架构图 ```text +--------+ +---------------+ | |--(A)- Authorization Request ->| Resource | | | | Owner | | |<-(B)-- Authorization Grant ---| | | | +---------------+ | | | | +---------------+ | |--(C)-- Authorization Grant -->| Authorization | | Client | | Server | | |<-(D)----- Access Token -------| | | | +---------------+ | | | | +---------------+ | |--(E)----- Access Token ------>| Resource | | | | Server | | |<-(F)--- Protected Resource ---| | +--------+ +---------------+ ``` ###### 授权类型 - Grant Type代表当前授权的类型 - authorizedGrantTypes - authorization_code:传统的授权码模式 - implicit:隐式授权模式或简化模式 - password:资源所有者(即用户)密码模式 - client_credentials:客户端凭据(客户端ID以及Key)模式 - refresh_token:获取access token时附带的用于刷新新的token模式 ###### 规范术语 - clientId:应用唯一标识符 - openId:某一应用下的用户唯一标识符 - unionId:同一主体下的用户唯一标识符 - userId:用户唯一标识符,oauth2视为敏感数据 #### 内置端点 - AuthorizationEndpoint 用于根据用户的认证获得授权码 - /oauth/authorize - GET 通常用于通过URL参数传递请求参数,引导用户到授权页面进行认证和授权 - /oauth/authorize - POST 可以用于通过POST请求体传递请求参数,更适合在需要传输敏感信息或大量数据时使用 - TokenEndpoint 客户端根据之前获得的授权码或其他凭证(如用户名和密码、客户端凭证)来请求访问令牌(token) - /oauth/token - GET 虽然可以通过GET请求获取token,但通常不推荐,因为GET请求的参数会暴露在URL中,可能导致敏感信息泄露 - /oauth/token - POST 更常用和推荐的方法,通过POST请求体传递参数,可以更安全地传输敏感信息 - CheckTokenEndpoint 用于远程解码和验证令牌的有效性,资源服务器或其他服务可以通过此端点检查令牌的状态和信息 - /oauth/check_token - WhitelabelApprovalEndpoint 显示授权服务器的确认页,当用户同意授权时,会重定向到这个页面,显示授权的具体信息,让用户最终确认 - /oauth/confirm_access - WhitelabelErrorEndpoint 显示授权服务器的错误页,当发生错误(如认证失败、授权拒绝等)时,用户会被重定向到这个页面 - /oauth/error #### 配置授权服务器的端点 - AuthorizationServerEndpointsConfigurer - authenticationManager:认证管理器,当你选择了资源所有者密码(password)授权类型的时候,请设置这个属性注入一个 AuthenticationManager 对象。 - userDetailsService:如果你设置了这个属性的话,那说明你有一个自己的 UserDetailsService 接口的实现,或者你可以把这个东西设置到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 这个配置对象),当你设置了这个之后,那么 "refresh_token" 即刷新令牌授权类型模式的流程中就会包含一个检查,用来确保这个账号是否仍然有效,假如说你禁用了这个账户的话。 - authorizationCodeServices:这个属性是用来设置授权码服务的(即 AuthorizationCodeServices 的实例对象),主要用于 " authorization_code" 授权码类型模式。 - implicitGrantService:这个属性用于设置隐式授权模式,用来管理隐式授权模式的状态。 - tokenGranter:当你设置了这个东西(即 TokenGranter 接口实现),那么授权将会交由你来完全掌控,并且会忽略掉上面的这几个属性,这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的需求的时候,才会考虑使用这个。 #### 常用注解 - @EnableAuthorizationServer - 验证服务器 - @EnableResourceServer - 资源服务器 - @EnableOAuth2Sso - 授权码类型的客户端 - @EnableOAuth2Client - 客户端凭据类型的客户端 #### 文件配置 ###### 验证服务器 - 1)配置 AuthorizationServerConfiguration.java > 客户端 -> 验证服务器请求 -> 调整客户端地址 ```java @Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Bean public TokenStore tokenStore() { // DefaultTokenServices 默认的存储 Token 类 return new InMemoryTokenStore(); } @Bean public AuthorizationCodeServices authorizationCodeServices() { return new InMemoryAuthorizationCodeServices(); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { // client-id + client-secret 进行的客户端认证 security .tokenKeyAccess("permitAll()") // oauth/token_key是公开 .checkTokenAccess("permitAll()") // oauth/check_token公开 .allowFormAuthenticationForClients(); // 表单认证(申请令牌) } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化, // 你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息 // 当授权服务器完成用户认证并授予访问令牌后,它会将用户重定向回客户端指定的URI clients.inMemory() // 使用in-memory存储 .withClient("client")// client_id .secret(new BCryptPasswordEncoder().encode("secret")) // 客户端密钥 .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token") // 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials .scopes("all") // 允许的授权范围 .autoApprove(false) // false跳转到授权页面 .redirectUris("https://www.baidu.com"); // 加上授权码的回调地址 } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // 用来配置令牌(token)的访问端点和令牌服务(tokenservices) endpoints .authorizationCodeServices(authorizationCodeServices()) // 授权码服务 .tokenStore(tokenStore()) // 令牌管理服务 .authenticationManager(authenticationManager) // 密码认证服务 .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); // 授权码认证模式 // http://localhost:8080/auth/oauth/authorize?client_id=client&response_type=code&redirect_uri=https://www.baidu.com // http://localhost:8080/auth/oauth/token?client_id=client&client_secret=secret&grant_type=authorization_code&code=rC-bcW&redirect_uri=https://www.baidu.com // 密码认证模式 // http://localhost:8080/auth/oauth/token?client_id=client&client_secret=secret&grant_type=password&username=admin&password=123456 // 客户端凭证模式 // http://localhost:8080/auth/oauth/token?client_id=client&client_secret=secret&grant_type=client_credentials // 简单模式 // http://localhost:8080/auth/oauth/authorize?client_id=client&response_type=token&redirect_uri=https://www.baidu.com // 刷新令牌 // http://localhost:8080/auth/oauth/token?client_id=client&client_secret=secret&grant_type=refresh_token&refresh_token=abc } } ``` ###### 资源服务器 - 1)配置 ResourceServerConfiguration.java ```java @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Bean public RemoteTokenServices tokenServices() { // 如果资源服务器分离,才需要配置当前这个选项,资源服务器无需额外管控SpringSecurity配置 RemoteTokenServices tokenServices = new RemoteTokenServices(); tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/auth/oauth/check_token"); tokenServices.setClientId("client"); tokenServices.setClientSecret("secret"); return tokenServices; } @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.resourceId("*").stateless(true); } @Override public void configure(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and().requestMatchers().anyRequest() .and().anonymous() .and().authorizeRequests() // .antMatchers("/product/**").access("#oauth2.hasScope('select') and hasRole('ROLE_USER')") .antMatchers("/**").authenticated(); // 配置访问权限控制,必须认证过后才可以访问 } } ``` ###### 客户端 ### Apache Shiro #### 基本概念说明 - Subject(外部应用交互) -> SecurityManager(权限管理器->工厂创建) -> - Authenticator 登录认证管理器 - Authorizer 授权管理器 - SessionManager - CacheManager - PluggableRealms 数据库读取+认证功能+授权功能实现 - SessionDAO - Cryptography #### 注入验证过滤器 - 1)配置:src/main/webapp/WEB-INF/web.xml ```xml shiroFilterChain org.springframework.web.filter.DelegatingFilterProxy targetFilterLifecycle true shiroFilterChain /spring/* ``` - 2)配置:shiro.xml ```xml /user/login = anon /user/dologin = anon /user/list = authc /user/loginOut=logout /** = authc ``` - 3)配置:ShiroConfiguration.java ```java @Configuration public class ShiroConfiguration { @Bean public DefaultWebSecurityManager defaultWebSecurityManager(DefinitionRealm definitionRealm) { // Shiro Web 安全管理的默认实现,主要负责整合 Shiro 的各个组件,以提供 Web 应用的安全服务 DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); // CredentialsMatcher 接口的一个实现类,该类的主要作用在于对用户的明文密码进行哈希处理,并与存储在系统中的哈希密码进行比对,以实现用户身份验证的功能 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashIterations(3); hashedCredentialsMatcher.setHashAlgorithmName("md5"); definitionRealm.setCredentialsMatcher(hashedCredentialsMatcher); // 基于内存的缓存管理器实现,它使用 JVM 的内存来存储缓存对象 MemoryConstrainedCacheManager cacheManager = new MemoryConstrainedCacheManager(); defaultWebSecurityManager.setRealm(definitionRealm); defaultWebSecurityManager.setCacheManager(cacheManager); return defaultWebSecurityManager; } @Bean public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() { val definition = new DefaultShiroFilterChainDefinition(); /** * anon 允许匿名访问,无需认证或授权 * authc 必须通过登录认证才能访问,且不包括 "Remember Me" 功能 * authBasic 基于 HTTP Basic Authentication 的认证方式 * logout 处理用户退出登录请求,清除会话并注销 * noSessionCreation 阻止 Shiro 创建新的会话(Session) * perms 要求用户具备指定权限才能访问 * port 限制请求的端口号 * rest 基于 HTTP 方法自动映射权限 * roles 要求用户具备指定角色才能访问 * ssl 强制使用 HTTPS 协议访问 * user 允许已认证用户或通过 "Remember Me" 功能登录的用户访问 */ definition.addPathDefinition("/**", "authc"); return definition; } } ``` - 4)配置:DefinitionRealm.java ```java public class DefinitionRealm extends AuthorizingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 获取身份验证信息 String loginName = token.getPrincipal().toString(); String credentials = new String((char[]) token.getCredentials()); System.out.println(loginName); System.out.println(credentials); // mock 查询密码 String password = new SecurityService().findPasswordByLoginName(loginName); if ("".equals(password)) { throw new UnknownAccountException("账户不存在"); } return new SimpleAuthenticationInfo(loginName, password, getName()); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 获取授权信息 return null; } static class SecurityService { public String findPasswordByLoginName(String loginName) { return "123456"; } } } ``` #### 配置权限角色 - 1)配置:shiro.ini ```text ; [main] ; md5CredentialsMatcher=org.apache.shiro.authc.credential.Md5CredentialsMatcher ; md5CredentialsMatcher.hashIterations=3 ; definitionRealm=xyz.lisys.util.DefinitionRealm ; definitionRealm.credentialsMatcher=$md5CredentialsMatcher ; securityManager.realms=$definitionRealm [users] admin=123456,role1,role2 [roles] role1=user:insert,user:select ``` ### Drools #### 基本概念说明 ###### 整体架构 > - RETE 是一种进行大量模式集合和大量对象集合间比较的高效方法,通过网络筛选的方法找出所有匹配各个模式的对象和规则 > - Fact(事实):对象之间及对象属性之间的关系 > - Rule(规则):是由条件和结论构成的推理语句,一般表示为if…Then,一个规则的if部分称为LHS(left-hand-side),then部分称为RHS(right hand side) > - Module(模式):就是指IF语句的条件,这里IF条件可能是有几个更小的条件组成的大条件,模式就是指的不能在继续分割下去的最小的原子条件 ```text 事实对象 Fact | ⬇ Working Memory (工作内存) Rule Base (规则库) --- 定义规则代码 ----> 属性、条件(LHS)、结果(RHS) Inference Engine (推理引擎) - Pattern Matcher(匹配器) - Agenda(议程) - Execution Engine(执行引擎) ``` ###### 编码对象 - 1)KieContainer > - KieContainer就是一个KieBase的容器,KieContainer承载了KieModule和其依赖 > - 一个层级的KieModules结构可以被加载到一个KieContainer实例中 > - KieContainer可以拥有一个或者多个KieBases - 2)KieBase > - KieBase就是一个知识仓库,包含了若干的规则、流程、方法等,在Drools中主要就是规则和方法 > - KieBase本身并不包含运行时的数据之类的,如果需要执行规则KieBase中的规则的话,就需要根据KieBase创建KieSession - 3)KieRepository > - KieRepository存放KieModule的仓库 > - KieModule由kmodule.xml文件定义(当然不仅仅只是用它来定义),用一个ReleaseId做区分 - 4)KieContainer > - KieContainer通过KieProject来初始化、构造KieModule > - 并将KieModule存放到KieRepository中,然后KieContainer可以通过KieProject来查找KieModule定义的信息 > - 并根据这些信息构造KieBase和KieSession ```text KieContainer(KieBase,KieBase) <- KieRepository <- KieModule[kmodule.xml] ⬇ KieProject -> 初始化 KieModule -> 构造 KieBase 和 KieSession ``` ###### 其它概念补充 - META-INF/kmodule.xml 配置声明了规则脚本放置的位置 ```xml ``` ### Apache Log4j #### 文件配置 - 1)配置:log4j.properties ```properties # log4j日志级别如下: # A:off 最高等级,用于关闭所有日志记录。 # B:fatal 指出每个严重的错误事件将会导致应用程序的错误。 # C:error 指出虽然发生错误事件,但仍然不影响系统的继续运行。 # D:warn 表明会出现潜在的错误情形。 # E:info 一般和在粗粒度级别上,强调应用程序的运行全程。 # F:debug 一般用于细粒度级别上,对调试应用程序非常有帮助。 # G:all 最低等级,用于打开所有日志记录。 # 但log4j只建议使用4个级别,优先级从高到低分别是: # error>warn>info>debug log4j.rootLogger=debug,systemOut,logFile # 输出到控制台 log4j.appender.systemOut=org.apache.log4j.ConsoleAppender log4j.appender.systemOut.layout=org.apache.log4j.PatternLayout # 精简 # log4j.appender.systemOut.layout.ConversionPattern =%-5p %30.30c %x - %m\n # 详细 log4j.appender.systemOut.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n log4j.appender.systemOut.Target=System.out # 输出到文件 log4j.appender.logFile=org.apache.log4j.FileAppender log4j.appender.logFile.layout=org.apache.log4j.PatternLayout log4j.appender.logFile.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n log4j.appender.logFile.File=D:/log/log4j.log log4j.appender.logFile.Encoding=UTF-8 # 将日志输记录到MySQL数据库 # log4j.appender.logDB = org.apache.log4j.jdbc.JDBCAppender # log4j.appender.logDB.layout = org.apache.log4j.PatternLayout # log4j.appender.logDB.Driver = com.mysql.jdbc.Driver # log4j.appender.logDB.URL = jdbc:mysql://localhost:3306/log4j?characterEncoding=utf-8 # log4j.appender.logDB.User = root # log4j.appender.logDB.Password = root # log4j.appender.logDB.Sql = INSERT INTO t_log4j(project_name,create_date,level,category,file_name,thread_name,line,all_category,message)values('mybatis','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m') ``` ## Issue ### Tomcat 控制台打印乱码问题? - 配置vm虚拟机参数 ```text -Dfile.encoding=utf-8 ``` - 配置Catalina日志打印编码,`logging.properties` ```text java.util.logging.ConsoleHandler.encoding = UTF-8 ``` ### 配置 `RequestMappingHandlerAdapter` 某些内容不生效? > 由于启用注解 `@EnableWebMvc` 和 `` 会自动注册一个 RequestMappingHandlerAdapter 与手动注册的冲突 > 莫要手动注册和自动注册同时使用