前言:我们知道spring有很多对外提供的接口,在使用时会不会分不清呢?什么时候该用什么接口,什么接口是干嘛用的,如果缺乏相关知识的整理或思考,很多时候我们的技术都是止步不前的。本文博主盘点一些常用接口,博主深知个人能力有限 ,非常欢迎各位在评论区补充其它常用接口的使用,共同进步。
原创不易,转载请声明出处:csdn博主 孟秋与你
文章目录
- 生命周期相关
- ApplicationContextInitializer 接口
- ApplicationContextAware接口
- BeanFactoryPostProcessor 接口
- BeanPostProcessor 接口
- InstantiationAwareBeanPostProcessor 接口
- SmartInstantiationAwareBeanPostProcessor接口
- BeanNameAware接口
- InitializingBean 接口 (@PostConstruct)
- CommandLineRunner和ApplicationRunner 接口
- 拦截器
- 接口拦截器
- HandlerInterceptor 接口
- WebRequestInterceptor 接口
- 切面通知
- MethodInterceptor 接口
- ConstructorInterceptor 接口
- mvc相关接口
- WebMvcConfigurer接口 (重要)
- 参数相关
- 方法参数解析器
- HandlerMethodArgumentResolver 接口
- 方法返回值处理
- HandlerMethodReturnValueHandler 接口
- ResponseBodyAdvice接口
- 参数类型转换器
- Convert接口
- HttpMessageConverter 参数转换接口
- 过滤器类
- OncePerRequestFilter
- 监听器
- ApplicationListener接口
- servlet参数相关接口
- HttpServletRequestWrapper
- ServletInputStream
生命周期相关
ApplicationContextInitializer 接口
这个接口作用可以理解为: 在spring初始化前 用户可以拓展些功能 ,我们可以在启动类实现这个接口。
/**
* @author 孟秋与你
*/
public class Application implements ApplicationContextInitializer {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
// 默认是启动的 也就是我们平常启动springboot能看到的一个图案
// 启动时的 banner 可以自定义内容在 resources/banner.txt
// 当然 生产环境为了节省启动速度 可以关闭这个
application.setBannerMode(Banner.Mode.CONSOLE);
application.run(args);
}
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
// 获取环境
ConfigurableEnvironment environment =
configurableApplicationContext.getEnvironment();
System.out.println("==init operation here =====springboot初始化完成某些操作在这里定义========");
}
}
我们看看网上已有的分析是如何描述的:
这是整个spring容器在刷新之前初始化ConfigurableApplicationContext的回调接口,简单来说,就是在容器刷新之前调用此类的initialize方法。这个点允许被用户自己扩展。用户可以在整个spring容器还没被初始化之前做一些事情。
可以想到的场景可能为,在最开始激活一些配置,或者利用这时候class还没被类加载器加载的时机,进行动态字节码注入等操作。
扩展方式为
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("[ApplicationContextInitializer]");
}
}
- 在启动类中用springApplication.addInitializers(new TestApplicationContextInitializer())语句加入
- 配置文件配置context.initializer.classes=com.example.demo.TestApplicationContextInitializer
- Spring SPI扩展,在spring.factories中加入org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer
- 看似很复杂,但其实稍微调试一下源码,我们就能发现 无论是博主的方式 还是网上其它教程的种种操作, 无非就是
给SpringApplication类的 List<ApplicationContextInitializer<?>> initializers 变量赋值,
简单粗暴点理解,就是通过不同的方式 间接setInitializers
ApplicationContextAware接口
很经典的一个接口 很多项目会自定义工具类通过这个接口来保存spring上下文信息, 工具类代码如下:
/**
* spring 工具类
*
*/
@Slf4j
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
SpringUtil.context = context;
}
/**
* 获取bean
*
* @param clazz class类
* @param <T> 泛型
* @return T
*/
public static <T> T getBean(Class<T> clazz) {
if (clazz == null) {
return null;
}
return context.getBean(clazz);
}
/**
* 获取bean
*
* @param beanId beanId
* @param <T> 泛型
* @return T
*/
public static <T> T getBean(String beanId) {
if (beanId == null) {
return null;
}
return (T) context.getBean(beanId);
}
/**
* 获取bean
*
* @param beanName bean名称
* @param clazz class类
* @param <T> 泛型
* @return T
*/
public static <T> T getBean(String beanName, Class<T> clazz) {
if (null == beanName || "".equals(beanName.trim())) {
return null;
}
if (clazz == null) {
return null;
}
return (T) context.getBean(beanName, clazz);
}
/**
* 获取 ApplicationContext
*
* @return ApplicationContext
*/
public static ApplicationContext getContext() {
if (context == null) {
return null;
}
return context;
}
/**
* 发布事件
*
* @param event 事件
*/
public static void publishEvent(ApplicationEvent event) {
if (context == null) {
return;
}
try {
context.publishEvent(event);
} catch (Exception ex) {
log.error(ex.getMessage());
}
}
}
调用时机在bean初始化前 早于BeanFactoryPostProcessor 接口 ,具体源码位置如下:
BeanFactoryPostProcessor 接口
BeanFactory的后置处理器(beanFactory的扩展接口)
在BeanFactory组建完之后(注:组建完并不是指所有bean装载完) 可以对beanFactory里面的东西 比如beanDefinition相关属性 进行操作 (beanDefinition就位于beanFactory中)
调用时机在spring在读取beanDefinition信息之后,实例化bean之前。
BeanPostProcessor 接口
BeanPostProcessor提供了两个方法postProcessBeforeInitialization,postProcessAfterInitialization
postProcessBeforeInitialization: 在Bean的初始化之前调用
postProcessAfterInitialization: 在Bean的初始化之后调用 (bean后置处理器)
InstantiationAwareBeanPostProcessor 接口
该接口继承了BeanPostProcess接口,区别如下:
BeanPostProcess接口只在bean的初始化阶段进行扩展(注入spring上下文前后),而InstantiationAwareBeanPostProcessor接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段 执行时间略早于BeanPostProcess。
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// postProcessBeforeInstantiation()在Spring中Bean实例化前触发执行;
System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// postProcessAfterInstantiation()在Spring中Bean实例化后,属性注入前触发执行;
System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);
return bean;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
// postProcessProperties()在Spring中Bean实例化后,属性注入前触发执行;
System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessProperties" + beanName);
return pvs;
}
这个接口对于用户来说,使用较多的场景可能是收集bean,并对bean进行统一赋值了; 这个接口就可以在初始化时 getBean byType byName, 所以是个对bean进行统一处理的好时机。
SmartInstantiationAwareBeanPostProcessor接口
该扩展接口有3个触发点方法:
- predictBeanType:该触发点发生在postProcessBeforeInstantiation之前,这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。
- determineCandidateConstructors:该触发点发生在postProcessBeforeInstantiation之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。
- getEarlyBeanReference:该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。
- 很多框架的代理数据源 就是实现了该接口。
BeanNameAware接口
如果实现了BeanNameAware接口 会执行setBeanName方法,
虽然是setBeanName, 但实际使用中 博主并不认为我们会经常去修改beanName,更多的用途可能是赋值给 thisBeanName,thisBeanName拿到beanName后进行更多的操作。
InitializingBean 接口 (@PostConstruct)
bean初始化前的一些操作 ,这个接口应该是非常常见了,里面可以写众多的初始化前配置、业务逻辑。
实现InitializingBean 和@PostConstruct都是类似的功能。
public interface InitializingBean {
// 实现该方法 进行些bean初始化前的操作
void afterPropertiesSet() throws Exception;
}
CommandLineRunner和ApplicationRunner 接口
博主(csdn: 孟秋与你)之前写过一篇单独的文章介绍这两个接口,感兴趣可以在博主主页搜索 ApplicationRunner 。
这两个接口其实已经不属于bean的生命周期了范畴了,在项目启动完毕时执行,我知道这句话会有歧义,我们直接上源码:
可以看到application.run()方法的最后一行代码:
这回应该不会有歧义了吧~
这两个接口用于监听启动时项目外部传入的参数,但由于其执行时间特性,我们又比较少在启动时给项目传入参数,所以一定程度上 也可以用于业务“初始化”操作,比如希望某依赖关联较少的业务 在项目启动完就执行 。
拦截器
我们一般所说的拦截器,其实都是指接口层面的拦截器(类似filter),但是我们会发现 Interceptor 接口却是位于aop包下的,博主这里大致解释一下。
接口拦截器
HandlerInterceptor 接口
我们通常所理解的拦截器,都是指HandlerInterceptor及其延伸的子类,HandlerInterceptor 位于spring-webmvc包下。我们也可以自己实现该接口,对request进行拦截, 拦截了request 我们就可以获取到remoteAddr , requestURI , request Method, requestURI等等 , 从而满足我们的业务需求。
(需要调用WebMvcConfigurer实现类的addInterceptors方法才能生效,后文关于WebMvcConfigurer接口有示例)
业务场景一般用于接口权限,下面给出一段不完整的代码 应该能看出思路
@Configuration
public class HttpAuthInterceptor implements HandlerInterceptor {
// R:url , C:method GET,POST,PUT... , V:roleIdList
HashBasedTable<String, String, List<Long>> urlStrategy = InitUrlAuth.urlStrategy;
// 进入controller方法之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 获取配置中 所有需要被拦截的url
Set<String> urlSet = urlStrategy.rowKeySet();
for (String url : urlSet) {
PathMatcher pathMatcher = new AntPathMatcher();
// 当前请求是否为需要拦截的接口
if (pathMatcher.match(url, request.getRequestURI())) {
// 当前http请求方法
String method = request.getMethod();
// 获取当前接口配置的http方法、角色
Map<String, List<Long>> columnValueMap = urlStrategy.row(url);
// 当前请求方法是否在配置中 ( ""表示拦截所有http方法 get ,post ,put...等)
if (columnValueMap.containsKey(method) || columnValueMap.containsKey("")) {
// 获取当前用户的角色
String userRole = AuthUtil.getUser().getRoleId();
List<String> roleIdList = Arrays.stream(userRole.split(",")).collect(Collectors.toList());
// 获取配置中 当前接口需要被哪些角色访问
List<Long> configRoleList = columnValueMap.get(method);
if (configRoleList.stream().anyMatch(configRole -> roleIdList.contains(String.valueOf(configRole)))) {
// 授予访问权限
return true;
}
}
}
}
// 写入权限不足提示
ResponseProvider.write(response);
return true;
}
}
我们如果要用于参数拦截,也是可以的,不过参数解析一般实现后文中提到的HandlerMethodArgumentResolver 接口会更简便,HandlerInterceptor 拦截参数的方式 博主也给出示例:
@Configuration
public class CustomHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 获取方法名
String methodName = handlerMethod.getMethod().getName();
// 获取方法参数
Class<?>[] parameterTypes = handlerMethod.getMethod().getParameterTypes();
// 在这里可以对方法名、方法参数进行操作
System.out.println("Method Name: " + methodName);
System.out.println("Parameter Types: " + parameterTypes);
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
这里就会产生一个疑问 为什么拦截器能拦截http请求呢? 我们知道拦截器是spring定义的,它并不是servlet的标准 不是filter, 为什么实现了HandlerInterceptor接口 就可以被拦截?
这里要回到一个经典的八股文,早些年问得比较多,尤其是问structs2和springmvc区别的面试题的时候,springmvc的入口其实是一个DispatcherServlet(它间接继承了HttpServlet)
既然所有请求都会进入到DispatcherServlet,那拦截器相关逻辑 在DispatcherServlet里面处理 就可以实现拦截了。
具体代码在DispatcherServlet类的doDispatch方法里面,处理拦截器链:HandlerExecutionChain ,以下是简化代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Apply preHandle methods of registered interceptors.
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Apply postHandle methods of registered interceptors.
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
WebRequestInterceptor 接口
WebRequestInterceptor 位于spring-web包下,从包路径我们也不难看出,WebRequestInterceptor可以适用于非mvc的环境下,不过我们基本很少遇到非mvc了。我们通过接口方法可以发现,WebRequestInterceptor 接口无法拦截request,日常开发中 我们自己拦截接口 通常都是实现HandlerInterceptor。
切面通知
博主把拦截器分为接口拦截器和切面拦截器, 切面拦截器我们通常叫切面 ,
Interceptor (拦截器)接口位于aop包下 就是实现的Advice接口的。
我们先复习一下切面的5种通知(advice)类型:
- 前置通知(Before Advice):
在目标方法执行前执行的通知。
- 环绕通知(Around Advice):
在目标方法执行前后都能执行的通知,可以完全控制目标方法的执行过程。
- 返回后通知(After Returning Advice):
在目标方法成功执行并返回结果后执行的通知。
- 后置通知(After Advice):
无论目标方法执行成功还是抛出异常,都会执行的通知。(相当于异常中的finally)
- 异常通知(After Throwing Advice):
在目标方法抛出异常时执行的通知。
- 代码层级:
AbstractAspectJAdvice implements Advice
├── BeforeAdvice (前置)
├── AspectJAroundAdvice(环绕)
├── AfterAdvice
├ ├── AspectJAfterAdvice (后置)
├ ├── AspectJAfterReturningAdvice (返回后)
├ ├── AspectJAfterThrowingAdvice (异常)
其中 环绕,后置,异常通知,又还实现了一个接口:MethodInterceptor
Interceptor implements Advice
├── ConstructorInterceptor (构造方法拦截器)
├── MethodInterceptor (方法拦截器 非常重要)
├ ├── AspectJAfterAdvice (后置)
├ ├── AspectJAroundAdvice (环绕)
├ ├── AspectJAfterThrowingAdvice (异常)
MethodInterceptor 接口
定义了在目标方法执行前后,甚至可以完全控制目标方法执行过程的方法。
用通俗的话来说,HandlerInterceptor 用来拦截接口,而MethodInterceptor 则是用来拦截方法。
ConstructorInterceptor 接口
与MethodInterceptor 类似,可以拦截构造方法,使得对象创建前 ,在构造方法里面执行某些逻辑。
mvc相关接口
WebMvcConfigurer接口 (重要)
这个接口大家应该非常熟悉了,convert、跨域、addInterceptors,包括HandlerMethodArgumentResolver 等配置,都是要实现该接口 调用相关的add方法,才会生效的。
@Configuration
public class WechatAppWebMvcConfig implements WebMvcConfigurer {
// ZonedDateTimeConverter implements Converter<String, ZonedDateTime>
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new ZonedDateTimeConverter());
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WechatAppAuthInterceptor())
.addPathPatterns("/app/**")
.excludePathPatterns("/test/**")
.excludePathPatterns("/public/**")
;
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
// MyMethodArgumentResolver implements HandlerMethodArgumentResolver
resolvers.add(new MyMethodArgumentResolver());
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(3600)
.allowCredentials(true);
}
参数相关
方法参数解析器
HandlerMethodArgumentResolver 接口
(注:路径为 org.springframework.web.method.support.HandlerMethodArgumentResolver )
与HandlerInterceptor 有点类似,都是作用于http请求时,不过HandlerMethodArgumentResolver 解析器专注于对GET方法参数(即query传参 也就是form表单提交的参数)进行解析。
当然 我们使用HandlerInterceptor 来解析参数也是可以的 步骤稍微复杂了一点点。
执行先后顺序 HandlerInterceptor > HandlerMethodArgumentResolver
/**
* 注意在WebMvcConfigurer实现类中调用addArgumentResolvers
*/
@Configuration
public class CustomHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
// 在 supportsParameter 方法中,确保对你要解析的参数类型返回 true,(实际应该替换为自己的业务校验)
// 以便告诉 Spring 这个解析器支持解析这个参数。
// 实际场景示例: 做权限校验时 判断参数类型是否为 UserAuth.class
return true;
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
Method method = methodParameter.getMethod();
Class<?> parameterType = methodParameter.getParameterType();
// 本例中 入参所在方法: public String test( UserAuth userAuth)
UserAuth args = (UserAuth )nativeWebRequest.getParameter("userAuth");
// 返回值类型要与原始的参数类型保持一致
return args ;
}
}
方法返回值处理
HandlerMethodReturnValueHandler 接口
负责返回时的处理
HandlerMethodReturnValueHandler 接口代码:
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
踩坑:
在WebMvcConfigurer实现类中重写addReturnValueHandlers方法 有的时候并没有用
(这主要取决于我们想通过该接口做什么 并不意味着完全没用,需要往下看源码分析)
比如我们想全局拦截controller层的返回值统一加密后返回给前端 就不能在WebMvcConfigurer里面重写了
/** 错误写法 **/
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
// 无论在这边清空或者add至最前 都是无效的
handlers.clear();
handlers.add(0, new CustomHandlerMethodReturnValueHandler());
}
因为WebMvcConfigurer实现类里的addReturnValueHandlers 执行时机比较早,而且最最关键的是 在RequestMappingHandlerAdapter类里面 它是由两个变量共同维护returnValueHandlers的!!!
一个是维护我们自定义的returnValueHandlers ,一个是维护spring内置的;
通过重写addReturnValueHandlers 方法添加的处理器是加在customReturnValueHandlers变量里面的
我们再看看代码做了什么:
如下图 可以看到 最后才把我们自定义的处理器给加载进来,
没执行到我们自定义处理器结果就已经被spring默认的处理器RequestResponseBodyMethodProcessor返回了。
所以正确做法是将spring的RequestResponseBodyMethodProcessor处理器给替换掉
下列代码中 CustomHandlerMethodReturnValueHandler 类是自定义的HandlerMethodReturnValueHandler实现类
(视情况选择继承RequestResponseBodyMethodProcessor类,RequestResponseBodyMethodProcessor是HandlerMethodReturnValueHandler的一个实现类)
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import java.util.ArrayList;
import java.util.List;
@Component
public class ResponseBodyWrapFactoryBean implements InitializingBean {
private final RequestMappingHandlerAdapter adapter;
@Autowired
public ResponseBodyWrapFactoryBean(RequestMappingHandlerAdapter adapter) {
this.adapter = adapter;
}
@Override
public void afterPropertiesSet() throws Exception {
List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
if (returnValueHandlers.size() > 0) {
// 将内置的返回值处理器进行替换
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);
decorateHandlers(handlers);
adapter.setReturnValueHandlers(handlers);
}
}
/**
* 将所有RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器
*
*/
private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
for (HandlerMethodReturnValueHandler handler : handlers) {
if (handler instanceof RequestResponseBodyMethodProcessor) {
// 替换为自定义返回值处理器
CustomHandlerMethodReturnValueHandler decorator = new CustomHandlerMethodReturnValueHandler(handler);
int index = handlers.indexOf(handler);
handlers.set(index, decorator);
break;
}
}
}
}
ResponseBodyAdvice接口
看到advice应该会比较熟悉 这个接口是更高一层的封装,实现HandlerMethodReturnValueHandler相关接口 ,如果没有把握就实现ResponseBodyAdvice接口吧
最简单的方式就参照fastjson的写法: 加个ControllerAdvice注解,我们需要把order调到最小(优先级最高)
在RequestMappingHandlerAdapter类中,会去判断是否有被ControllerAdvice注解修饰 且实现了ResponseBodyAdvice的类 如果有则添加到最前面 (代替默认的jackson方式 )
我们可以随便看看默认的实现代码:
代码位于WebMvcConfigurationSupport类中
参数类型转换器
Convert接口
例如: 将ZonedDateTime参数转换成String,这通常都是前后端交互时的参数类型转换
public class ZonedDateTimeConverter implements Converter<String, ZonedDateTime> {
// 需要在WebMvcConfigurer的实现类中调用addFormatters方法
@Override
public ZonedDateTime convert(String source) {
if (Func.isEmpty(source)) {
return null;
}
long parseLong = Long.parseLong(source);
return Instant.ofEpochMilli(parseLong).atZone(ZoneId.systemDefault());
}
}
HttpMessageConverter 参数转换接口
举个最常用的 MappingJackson2HttpMessageConverter接口是HttpMessageConverter的一个实现类,spring默认通过MappingJackson2HttpMessageConverter接口 将实体类转成json字符串返回给前端的。
所以当我们希望返回的json添加一点自定义内容 一般继承MappingJackson2HttpMessageConverter即可,继承类通过mvc配置即可
@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {
/** 不推荐使用configureMessageConverters方法 会替换默认转换器 **/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0,new EncryptHttpMessageConverter());
}
}
过滤器类
OncePerRequestFilter
见名知意,每次请求的过滤器,spring提供的过滤器类
监听器
ApplicationListener接口
博主也有专门讲ApplicationListener的博客,主要是监听事件,实现一个简单的发布-订阅功能。事件需要是ApplicationEvent的子类
最后,再次安利博主的原创idea轻量级小插件: equals inspection
用于解决Objects.equals方法传参容易误传的问题,欢迎各位下载该免费插件~
servlet参数相关接口
spring也是离不开servlet的,上文有提到过参数相关的接口,那就不得不提到servlet的相关接口了。
我们如果想做一个全局参数的解密,这个时候就需要在过滤器里面处理了,拦截器里面只能单纯把值解密,但是无法将解密后的数据重新设置回去。
核心是需要将 request里面的东西修改,并传递给下一个过滤器;篇幅较长,所以博主单开一篇博客,可前往博主(csdn: 孟秋与你) 主页搜索: springboot接口参数全局解密
本文只提出几个重要接口,具体内容不敞开叙述。
HttpServletRequestWrapper
Servlet包装流的接口(类),解决request流只能读取一次的问题、重写reqeust的内容 如inputsream、parameterMap等
ServletInputStream
与HttpServletRequestWrapper 搭配使用,重写inputStream