灵感和参考
作者:舟晚梦星辰
链接:juejin.cn/post/717493…
作者: 苏三说技术[原稿]
链接:juejin.cn/post/714508…
一、常用标签(有整理错误,请留言,我这边修正,方便未来复看)
www.processon.com/view/link/6…
二、拦截器
1.拦截器、过滤器、监听器的执行顺序
监听器 > 过滤器 > 拦截器 > servlet执行 > 拦截器 > 过滤器 > 监听器
2.多个拦截器的执行顺序(两个)
(1)当俩个拦截器都实现放行操作时,顺序为preHandle 1,preHandle 2,postHandle 2,postHandle 1,afterCompletion 2,afterCompletion 1;
(2)当第一个拦截器preHandle返回false,也就是对其进行拦截时,第二个拦截器是完全不执行的,第一个拦截器只执行preHandle部分;
(3)当第一个拦截器preHandle返回true,第二个拦截器preHandle返回false,顺序为preHandle 1,preHandle 2 ,afterCompletion 1。
3.多个过滤器的执行顺序
web服务器根据Filter在web.xml中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法,在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第二个filter,如果没有,则调用目标资源。
4.多个监听器的执行顺序
一个webServlet里面若有多个监听器的话,顺序是按照加载的顺序来加载和注册的这些servlet监听器的。
spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。
5.spring拦截器
HandlerInterceptor
是springMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。
spring mvc拦截器的顶层接口是:HandlerInterceptor,包含三个方法:
- preHandle 目标方法执行前执行
- postHandle 目标方法执行后执行
- afterCompletion 请求完成时执行
为了方便我们一般情况会用HandlerInterceptor接口的实现类HandlerInterceptorAdapter类。
假如有防重复提交、跨域、权限认证、操作日志、统计等场景,可以使用该拦截器。
为了方便我们一般情况会用HandlerInterceptor接口的实现类HandlerInterceptor 不推荐后面实现类 HandlerInterceptorAdapter类。
第二步,将该拦截器注入WebMvcConfigurer不推荐后面实现类WebMvcConfigurerAdapter。
注:Spring5.0废弃了.........Adapter ,6.0已经转为内部类了.
MethodInterceptor
方法拦截,记录方法的log日志,在controller请求上面使用标签用过。 可参考下面回忆: blog.csdn.net/WX5991/arti…
三、通过容器的方式获取对象
在我们日常开发中,如果你不是从controller开始,而是new对象,那么该对象内的(@Autowired或@Resource)标签并不会生效。
1.BeanFactoryAware接口
package com.nice.core.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
@Component
public class BeanContext implements BeanFactoryAware {
private static BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public static <T> T getBean(String name) throws BeansException {
return (T)beanFactory.getBean(name);
}
public static <T> T getBean(Class<T> clz) throws BeansException {
return (T)beanFactory.getBean(clz);
}
}
引用:
Person person = BeanContext.().getBean(Person.class);
2.ApplicationContextAware接口(我常用这个)
package com.nice.core.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class BeanContext implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanContext.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext(){
return applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException {
return (T)applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clz) throws BeansException {
return (T)applicationContext.getBean(clz);
}
}
引用:
Person person = BeanContext.getApplicationContext().getBean(Person.class);
3.ApplicationListener接口
package com.nice.core.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class BeanContext implements ApplicationListener<ContextRefreshedEvent> {
private static ApplicationContext applicationContext;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
applicationContext = event.getApplicationContext();
}
public static <T> T getBean(String name) throws BeansException {
return (T)applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clz) throws BeansException {
return (T)applicationContext.getBean(clz);
}
}
引用:(这个与上面引用的区别自己看)
Person person = BeanContext.().getBean(Person.class);
四、异常处理(非常好用)
package cn.nice.core.response;
import cn.hutool.core.util.StrUtil;
import cn.leap.common.response.ResponseBody;
import cn.leap.common.response.ResponseCode;
import cn.leap.common.singlelock.core.LockException;
import cn.leap.common.utils.JacksonUtil;
import com.alibaba.excel.exception.ExcelAnalysisException;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.sql.SQLException;
@Slf4j
@RestControllerAdvice
public class ResponseAdvisor implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
/**
* Controller层返回数据前处理函数
*/
@Override
public Object beforeBodyWrite(Object o,
MethodParameter methodParameter,
MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
ResponseBody body = new ResponseBody();
body.setRequestId(MDC.get("traceId"));
if (!(o instanceof ResponseBody)) {
body.init(o);
// String类型的返回值比较特殊
if (o instanceof String)
o = JacksonUtil.toString(body);
else
o = body;
}
return o;
}
/**
* Controller层发生异常后进行包装处理
*/
@ExceptionHandler({Exception.class})
public Object globalExceptionHandler(Exception e) {
String mes = e.getMessage() == null ? " 空指针异常 " : e.getMessage();
log.error(mes, e);
ResponseBody body = new ResponseBody();
body.setRequestId(MDC.get("traceId"));
if (e instanceof NoHandlerFoundException) {
body.init(ResponseCode.FAIL, StrUtil.format("请【截全图】联系,客服人员<NoHandlerFoundException> {}", mes));
} else {
body.init(ResponseCode.FAIL, mes);
}
return body;
}
}
只需在handleException方法中处理异常情况,业务接口中可以放心使用,不再需要捕获异常,一般公司技术老大会把常用异常统一封装好的。
五、类型转换器
spring目前支持3种类型转换器:
- Converter<S,T>:将 S 类型对象转为 T 类型对象
- ConverterFactory<S, R>:将 S 类型对象转为 R 类型及子类对象
- GenericConverter:它支持多个source和目标类型的转化,同时还提供了source和目标类型的上下文,这个上下文能让你实现基于属性上的注解或信息来进行类型转换。
不说了,百度. 个人感觉这个实际使用有点鸡肋。
项目中常用:
1.String 转日期
2.日期 转 String
3.list转map
4.map转对象
5.对象copy
6.集合对象copy
7 json -> Gson之toJson和fromJson方法
六、项目启动
springboot提供了:
- CommandLineRunner
- ApplicationRunner
应用
系统参数获取、初始化数据等
执行顺序
- @Order/@Priority 值相同时,ApplicationRunner 优先于 CommandLineRunner。
- @Order/@Priority 值和继承的接口类型都相同时,按照注入容器的顺序(应该是按照类的名称)
springboot 完全初始化完毕后,执行
@Component
public class ServerDispatcher implements CommandLineRunner {
@Override
public void run(String... args){
// 逻辑代码
}
}
@Component
public class ServerDispatcher implements ApplicationRunner {
@Override
public void run(ApplicationArguments args){
// 逻辑代码
}
}
执行顺序
BeanFactoryPostProcessor ---> 普通Bean构造方法 ---> 设置依赖或属性 Constructor >> @Autowired >> @PostConstruct > InitializingBean > ApplicationRunner
七、监听机制
@Component
public class ServerDispatcher implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
// 逻辑代码
}
}
执行时机
在 IOC 容器的启动过程,当所有的 bean 都已经处理完成之后,spring 的 ioc 容器会有一个发布 ContextRefreshedEvent 事件的动作。
注意事项
系统会存在两个容器,一个是 root application context , 另一个就是我们自己的 projectName-servlet context(作为root application context的子容器)
这种情况下,就会造成 onApplicationEvent 方法被执行两次。为了避免上面提到的问题,我们可以只在 root application context 初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理
加入判断即可:
if (event.getApplicationContext().getParent() == null)
@Component
public class ServerDispatcher implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
// 当 spring 容器初始化完成后就会执行该方法
if (event.getApplicationContext().getParent() == null) {
//逻辑代码
}
}
}
监听事件类型
ContextRefreshedEvent:ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。此处的初始化是指:所有的 Bean 被成功装载,后处理 Bean 被检测并激活,所有 Singleton Bean 被预实例化,ApplicationContext 容器已就绪可用。
ContextStartedEvent:当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
ContextStoppedEvent:当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。
ContextClosedEvent:当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
RequestHandledEvent:这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。
八、 修改BeanDefinition
Spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制。该机制允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。这就相当于在容器实现的第一阶段最后加入一道工序,让我们对最终的BeanDefinition做一些额外的操作,比如修 改其中bean定义的某些属性,为bean定义增加其他信息等。
参考地址: zhuanlan.zhihu.com/p/522265861
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addPropertyValue("id", 123);
beanDefinitionBuilder.addPropertyValue("name", "苏三说技术");
defaultListableBeanFactory.registerBeanDefinition("user", beanDefinitionBuilder.getBeanDefinition());
}
}
九、初始化Bean前后
BeanPostProcessor接口。
该接口目前有两个方法:
- postProcessBeforeInitialization 该在初始化方法之前调用。
- postProcessAfterInitialization 该方法在初始化方法之后调用。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof User) {
((User) bean).setUserName("苏三说技术");
}
return bean;
}
}
其实,我们经常使用的注解,比如:@Autowired、@Value、@Resource、@PostConstruct等,是通过AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor实现的 BeanPostProcessor等。
十、初始化方法
1.使用@PostConstruct注解
@Service
public class AService {
@PostConstruct
public void init() {
System.out.println("===初始化===");
}
}
2.实现InitializingBean接口
@Service
public class BService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("===初始化===");
}
}
十一、关闭容器前销毁
@Service
public class DService implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet");
}
}
通常情况下,我们会同时实现InitializingBean和DisposableBean接口,重写初始化方法和销毁方法。