一. spring framework
1. BeanPostProcessor(bean初始化前后置操作)
该接口能够在bean被实例化前后完成一些后置操作
例如在springboot环境中,每个bean在被实例化之前,都会执行该后置处理器,完成业务的特定操作
@Component
//@Lazy
public class BeanLife1 implements BeanPostProcessor, InitializingBean {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(bean);
System.out.println(beanName);
System.out.println("this is postProcessBeforeInitialization");
return bean;
}
//判断如果是myselfService,则对该bean进行加强处理等业务操作
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("myselfService")) {
System.out.println("this is postProcessAfterInitialization");
}
// System.out.println("this is postProcessAfterInitialization");
return bean;
}
}
2. InitializingBean & DisposableBean (初始化 & 销毁接口)
实现该接口的方法,可以完成bean的初始化和销毁方法的重写
当然,可以使用@PostConstruct
和@PreDestroy
注释代替
执行顺序为:
@PostConstruct
-> InitializingBean -> PreDestroy
-> DisposableBean
3. Lifecycle & LifecycleProcessor & SmartLifecycle 管理IOC的生命周期(启动和关闭回调)
通过实现以上接口,可以对IOC的启动,刷新,停止,重启进行回调。SmartLifecycle可以更加细粒度的管理IOC容器的自动刷新。从而实现对整个IOC容器的增强业务处理
4. ApplicationContextAware(获取spring当前的上下文对象)
实现该接口后,重写set方法,就可以拿到applicationContext上线文获取spring环境中所有的bean对象。该接口是实现类
ApplicationObjectSupport
提供的API更加强大,推荐使用该类获取上线文
@Component
public class BeanLife4 implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
applicationContext = applicationContext;
}
}
5. factoryBean接口
spring提供了该接口,用于实现自己的bean工厂类
//实现三个方法,分别提供了bean的实例对象,bean的class类型,bean是否单例
public class MyselfBeanFactory implements FactoryBean<FactoryBean1> {
@Override
public FactoryBean1 getObject() throws Exception {
return new FactoryBean1();
}
@Override
public Class<?> getObjectType() {
return FactoryBean1.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
//通过注入该工厂,达到注入指定bean的目的
@Bean
public MyselfBeanFactory myselfBeanFactory() {
return new MyselfBeanFactory();
}
二. spring web mvc
1. HandlerExceptionResolver
全局异常处理接口
该接口实现方法后可以捕获所有来自MVC(requestMapping)的异常,并且返回一个modeAndView对象,可以指向其他的controller层。但是在实际的工作中,直接实现该接口并不是优雅的解决方案,springMVC 提供了一个注解@restControllerAdvice、@ExceptionHandler、@ResponseStatus
其中@restControllerAdvice注解能够拦截所有controller层的异常,并且交给当前类来处理
@exceptionHandler指定了异常被拦截后的处理方法,该注解可以执行具体的拦截异常类型参数
@responsedaStatus,可以指定当前request返回的response是什么类型,也就是执行什么就会返回什么
@RestControllerAdvice
@Slf4j
public class MyselfExceptionAnnotation {
@ExceptionHandler(Exception.class)
// @ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "我愿意")//默认也是返回
public String handler(HttpServletRequest request, Exception ex) {
log.error("捕获到异常,e={}", ex.getMessage());
// ex.printStackTrace();
return "error12123";
}
}
2. WebMvcConfigurer MVC的拦截处理接口
该接口用于实现自定义拦截需求,项目中可以通过实现该接口或者继承WebMvcConfigurationSupport。并加入到spring容器中
该接口中有很多方法比较使用,例如:
①addInterceptors : 实现spring MVC的拦截器
②addArgumentResolvers():实现MVC的参数处理解析
③configurePathMatch(PathMatchConfigurer configurer):这个是和访问路径有关的
④configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer): 以实现静态文件可以像Servlet一样被访
⑤addFormatters(FormatterRegistry registry):增加转化器或者格式化器。这边不仅可以把时间转化成你需要时区或者样式。还可以自定义转化器和你数据库做交互,比如传进来userId,经过转化可以拿到user对象。
⑥addResourceHandlers(ResourceHandlerRegistry registry):静态资源处理器,业务场景就是自己的服务器作为文件服务器
⑦addCorsMappings(CorsRegistry registry):这个是设置跨域问题的
...... 还有其他的方法可以深入研究
exp1:例如拦截器:
先实现拦截器的接口:HandlerInterceptor或者继承HandlerInterceptorAdapter都可以,推荐直接实现
public class HandlerInterceptorConfig implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
request.setAttribute("user_id", "12345");
request.setAttribute("phone", "110");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
然后将该拦截器添加到spring MVC的拦截规则中
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptorConfig()).addPathPatterns("/**");
}
}
此时,拦截器即可生效
exp2: 参数解析
spring MVC的参数可以在webMvcConfigurer中被拦截,并且按照自定义规则解析
实现参数拦截解析接口
public class ParamResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
//判断拦截的参数中是否有该注解,如果此方法返回true, 则执行resolveArgument中的内容
return parameter.hasParameterAnnotation(UserIds.class);
// return false;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//该方法将返回需要被解析的参数值,并进行绑定
return webRequest.getAttribute("user_id", RequestAttributes.SCOPE_REQUEST);
}
}
将参数解析添加到WebMvcConfigurer中
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new ParamResolver());
}
}
3. 消息转化器(HttpMessageConverter)
在springMVC中,前端传送参数&接口返回客户端参数,都需要经过springMVC默认的参数转化器进行转化,我们可以干掉springMVC默认的消息转化器,自定义一个来完成灵活使用。
3.1 首先,springMVC是如何定义默认的converter的呢?
跟踪代码发现:在MVC的统一配置处理拦截器里,在进行controller信息映射时,会讲默认的converter进行添加,此处调用了
getMessageConverters()
该方法中,判断了当前的解析器集合是否为空,不为空,则调用了
addDefaultHttpMessageConverters()方法,添加默认解析器的方法,在该方法中加入了springMVC的解析器,也就是JackSon的解析器
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<>();
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
3.2 要想实现自定义或者覆盖spring配置的解析器。可以在WebMvcConfigurer或者WebMvcConfigurationSupport中实现
extendMessageConverters()或者 configureMessageConverters()方法
此处的集合参数,就是WebMvcConfigurationSupports中默认添加好的解析器,如果不需要使用默认,可以将list执行clear方法,重新添加解析器即可,有关配置fastjson解析器可以参考博客:
https://www.cnblogs.com/hhhshct/p/9676604.html
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.clear();
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
converters.add(converter);
}
4. 获取上下文请求对象 RequestContextHolder
可以根据RequestContextHolder获取当前请求的request和response
RequestAttributes request = RequestContextHolder.currentRequestAttributes()
5. MVC的异步响应模式(DeferredResult(异步响应一个)、servletRequest.startAsync、ResponseBodyEmitter(支持多次发送)、SseEmitter、StreamingResponseBody)
参考博客:https://blog.csdn.net/f641385712/article/details/88710676
-
DeferredResult,当servlet的请求返回值是这个类型时,response的返回将会一直等待响应,直到被添加了返回值才会结束响应。如下示例,flux1接口会一直等待响应,并且把result保存起来,当请求flux2时,将result中添加响应,此时flux1接口接收到响应值并完成。
private static DeferredResult<String> results;
@GetMapping("/flux1")
public DeferredResult<String> flux1(HttpServletRequest request) {
DeferredResult<String> result = new DeferredResult<>();
this.results = result;
return result;
}
@GetMapping("/flux2/{id}")
public String flux2(@PathVariable String id) {
this.results.setResult("FUCK" + id);
return "OK";
}
-
startAsync模式:该模式是servlet3.0后支持的一种异步响应的请求方式,大概意思是,如果有一些IO或者长时间阻塞的请求,如果按照传统的servlet请求,线程会一直被阻塞直到处理完成后释放。现在使用startAsync模式后,假如请求需要响应5s,请求后,请求线程会立即完成,将处理业务的5s阻塞交给另一个线程去处理,处理完成后完成响应。对请求来说,不是阻塞的,耗时会交给一个新线程处理,并且请求线程会等处理完成后完成响应,示例如下:
@GetMapping("/flux3")
public void flux3(HttpServletRequest request, HttpServletResponse response) {
AsyncContext asyncContext = request.startAsync();
asyncContext.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
log.info("is complete");
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
log.info("is timeout");
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
log.info("is error");
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
log.info("is start");
}
});
asyncContext.setTimeout(2000);
//实际的业务处理会交给这个线程来完成,
asyncContext.start(() -> {
try {
Thread.sleep(1000);
System.out.println("ddddd");
log.info("内部线程:"+Thread.currentThread().getName());
// int i = 1/0;
asyncContext.getResponse().setCharacterEncoding("utf-8").;
asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
asyncContext.getResponse().getWriter().print("这是【异步】的请求返回");
}catch (Exception e){
log.error("异常",e);
}
//异步请求完成通知
//此时整个请求才完成
//其实可以利用此特性 进行多条消息的推送 把连接挂起。。
asyncContext.complete();
});
log.info("servlet线程响应结束");
}
-
ResponseBodyEmitter 可以返回多个值给请求,示例如下:当客户端发起骑牛/fulx4时,将emitter保存,然后返回。有其他的请求在emitter中发送消息,则客户端会一直接受消息,直到调用complate后,请求才会结束。
@GetMapping("/flux4")
public ResponseEntity<ResponseBodyEmitter> flux4() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
EMITTERS = emitter;
return ResponseEntity
.ok()
.contentType(MediaType.TEXT_HTML)
.body(emitter); //2
}
@GetMapping("/flux5")
public void flux5() throws IOException {
EMITTERS.send("FUCK5");
}
@GetMapping("/flux6")
public void flux() throws IOException {
EMITTERS.send("FUCK6");
}
@GetMapping("/flux7")
public void flux7() throws IOException {
EMITTERS.send("FUCK7");
EMITTERS.complete();
}