自定义拦截器:
- 实现HandleInterceptor接口
public class MySelfInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("在业务处理器处理请求之前被调用");
//可以进行权限校验,安全控制
MyRequestWrapper requestWrapper = new MyRequestWrapper (request);
// 读取请求内容
BufferedReader br = requestWrapper.getReader();
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
// 将json字符串转换为json对象
JSONObject body = JSONObject.parseObject(sb.toString());
//业务处理
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("在业务处理器处理请求执行完成后,生成视图之前执行");
//可以对返回来的ModelAndView进行处理,这个时候还未渲染视图
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("在DispatcherServlet完全处理完请求后被调用");
//请求已经完成,页面已经渲染,数据已经返回。这个时候可以做一些资源清理,或者记录请求调用时间,做性能监控
}
}
- 继承HandleInterceptorAdapter类(@Deprecated)
此类在SpringBoot2.0以后已经废除,但仍可使用。
@Component
public class MyInterceptor extends HandlerInterceptorAdapter {
public SingleLoginInterceptor() {
super();
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
super.afterConcurrentHandlingStarted(request, response, handler);
}
}
- 实现WebRequestInterceptor接口
@Component
public class MyInterceptor implements WebRequestInterceptor {
@Override
public void preHandle(WebRequest webRequest) throws Exception {
}
@Override
public void postHandle(WebRequest webRequest, ModelMap modelMap) throws Exception {
}
@Override
public void afterCompletion(WebRequest webRequest, Exception e) throws Exception {
}
}
HandlerInterceptorAdapter类底层是实现了HandlerInterceptor接口,多了两个方法,要比
实现HandlerInterceptor接口的方式功能强大。
这两个方法都是HandlerInterceptorAdapter类实现的org.springframework.web.servlet.AsyncHandlerInterceptor接口提供的,而AsyncHandlerInterceptor接口又继承了HandlerInterceptor接口,所以HandlerInterceptorAdapter底层是实现类HandlerInterceptor接口。
两个实现接口方式的异同点
1.相同点
都可以实现controller层的拦截请求
2.不同点
1.WebRequestInterceptor的入参WebRequest是包装了HttpServletRequest 和HttpServletResponse的,通过WebRequest获取Request中的信息更简便。
2.WebRequestInterceptor的preHandle是没有返回值的,说明该方法中的逻辑并不影响后续的方法执行,所以这个接口实现就是为了获取Request中的信息,或者预设一些参数供后续流程使用。
3.HandlerInterceptor的功能更强大也更基础,可以在preHandle方法中就直接拒绝请求进入controller方法。
- 实现RequestInterceptor接口
此方式为微服务Feign调用的自定义拦截器,实现各个微服务之间的参数传递。
@Configuration
public class CenterinsRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
}
}
拦截器的注册:
- 实现WebMvcConfigurer接口
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 实现WebMvcConfigurer不会导致静态资源被拦截
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
- 继承WebMvcConfigurationSupport类
@Configuration
public class MyInterceptorConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
此方式会导致默认的静态资源被拦截,这就需要我们手动将静态资源放开。
除了重写方法外还需要重写addResourceHandlers方法来释放静态资源
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
super.addResourceHandlers(registry);
}
此方式:一个容器内只能有一个WebMvcConfigurationSupport的实现类,也就是说不能有多个继承类,否则只有一个生效,会造成未知的错误,如果想在已有实现类的基础上(基础jar包中存在webConfig)还想继续添加拦截器,可以选择继承WebConfig,但是要super.addInterceptors,避免丢失注册
原因:在WebMvcAutoConfiguration 中 WebMvcConfigurationSupport 是 @ConditionalOnMissingBean 原来SpringBoot做了这个限制,只有当WebMvcConfigurationSupport类不存在的时候才会生效WebMvc自动化配置
- 继承WebMvcConfigurerAdapter类(@Deprecated)
此类在SpringBoot2.0以后已经废除,但仍可使用。
@Configuration
public class WebCofiguration extends WebMvcConfigurerAdapter {
@Bean
MyInterceptor getMyInterceptor (){
return new MyInterceptor ();
}
public void addInterceptors(InterceptorRegistry registry) {
// 将自己定义的拦截器注入进来进行拦截操作
//registry.addInterceptor(new MySelfInterceptor ()) // 如果是new 出来的对象 会导致 拦截器中自动装配为空
registry.addInterceptor(getMyInterceptor ())
.addPathPatterns("/**")
.excludePathPatterns("/logout");
//过滤器可以添加多个,这里的addPathPatterns的/**是对所有的请求都做拦截。
//excludePathPatterns代表排除url的拦截路径,即不拦截
}
}
注意
拦截器中request请求被读取一次后,controller获取为空
继承HandleInterceptorAdapter类 和 实现HandleInterceptor接口 实现WebRequestInterceptor接口 自定义类实现RequestInterceptor接口
HttpServletRequest的输入流只能读取一次的原因
当我们调用getInputStream()方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承与InputStream。
InputStream的read()方法内部有一个position,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()返回-1,表示已经读取完了,如果想要重新读取,则需要调用reset()方法,position就会移动到上次调用mark的位置,mark默认是0,所有就能重头再读了。调用reset()方法的前提是已经重写了reset()方法,当然能否reset也是有条件的,它取决于markSupported()方法是否返回true。
InputStream默认不实现reset(),并且markSupported()默认也是返回false
我们可以把流读取出来后用容器存起来,后面就可以多次利用了。JavaEE提供了一个HttpServletRequestWrapper类,它是一个http请求包装器,基于装饰者模式实现类HttpServletRequest界面。