melonjj 2020-01-04
转自:
https://blog.csdn.net/cp026la/article/details/86501019
本章介绍拦截器、过滤器、切片对请求拦截的使用与区别,以及监听器在 springboot1.5 中的简单使用
相同点: 都可以对请求进行拦截。
不同点:
1、过滤器对请求的拦截只能获取到原始的Request 和 Response 的信息。
2、拦截器对请求的拦截可以获取原始的Request、Response和所有的controller及方法名,但无法获取方法的参数信息。
3、Aspect切片只能获取方法的参数,原始的Request、Response不能获取。
所以,实际项目需根据需求选择使用何种方式拦截请求。
/** * @Auther: xf * @Date: 2018/11/19 19:30 * @Description: 自定义过滤器 * * Filter 只能获取request 和 response 并不知道请求是哪个 controller 处理的 */ @Slf4j public class CustomFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("过滤器初始化>>>>>>>"); } /** * 请求被拦截的时候进行调用 */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { log.info(">>>>>>>>>>>>过滤器拦截请求处理逻辑>>>>>>>>>>>>>"); // 业务逻辑 long startTime = System.currentTimeMillis(); // 过滤器链,给下一个过滤器 filterChain.doFilter(servletRequest, servletResponse); log.info("请求时间:" + (System.currentTimeMillis() - startTime)); } @Override public void destroy() { log.info("过滤器销毁>>>>>>>"); } }
传统的web项目中,过滤器的配置是在web.xml中添加。SpringBoot需要使用FilterRegistrationBean来完成配置。
/** * @Auther: xf * @Date: 2018/11/19 19:58 * @Description: 传统的项目配置 Filter 在 web.xml 中添加 * 在Spring boot中,我们需要 FilterRegistrationBean 来完成配置 */ @Configuration public class CustomFilterConfig { @Bean public FilterRegistrationBean customFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new CustomFilter()); registration.addUrlPatterns("/*"); registration.setName("customFilter"); registration.setOrder(1); return registration; } }
控制台有拦截器的日志。
直接通过@WebFilter注解配置(注释掉上面的CustomFilterConfig配置类)
@Slf4j // 多个过滤器,使用此注解指定执行顺序,越小越先执行 @Order(1) // 在 CustomFilterConfig 中配置 注释掉 @WebFilter @WebFilter(urlPatterns = "/*", filterName = "customFilter") public class CustomFilter implements Filter { ... }
注意:
@WebFilter 是Servlet3.0 的规范,并不是SpringBoot提供的,需加上@ServletComponentScan注解扫描指定的包。我们这里加到启动类上。
// filter和servlet、listener之类的需要单独进行注册才能使用,spring boot里面提供了该注解起到注册作用 @ServletComponentScan // mapper 接口类扫描包配置 @MapperScan("com.coolron.*.dao") @SpringBootApplication(scanBasePackages = "com.coolron") public class SpringbootApplication { ... }
@Slf4j public class MyInterceptor1 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info(">>>MyInterceptor1>>>>>>>在请求处理之前进行调用(Controller方法调用之前)"); return true;// 只有返回true才会继续向下执行,返回false取消当前请求 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info(">>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info(">>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)"); } }
/** * 自定义拦截器2 */ @Slf4j public class MyInterceptor2 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info(">>>MyInterceptor2>>>>>>>在请求处理之前进行调用(Controller方法调用之前)"); return true;// 只有返回true才会继续向下执行,返回false取消当前请求 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info(">>>MyInterceptor2>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info(">>>MyInterceptor2>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)"); } }
/** * @Auther: xf * @Date: 2018/11/19 21:09 * @Description: */ @Configuration public class InterceptorConfig extends WebMvcConfigurerAdapter { /* * 拦截器配置 * 在spring-mvc.xml配置文件内添加<mvc:interceptor>标签配置拦截器。 */ @Override public void addInterceptors(InterceptorRegistry registry) { // 多个拦截器组成一个拦截器链 // addPathPatterns 用于添加拦截规则 // excludePathPatterns 用户排除拦截 registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").excludePathPatterns("/login"); registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**"); // 父类的配置 super.addInterceptors(registry); } }
/** * @Description: * 可以拿到请求的具体controller 对应的方法的具体参数值 但是拿不到 原始的request 和 response * * 切入点(注解): * 1、 在哪些方法上起作用 * 2、 什么时候起作用 * * 增强(方法) * 1、起作用是执行的业务逻辑 */ // 声明切片类 @Slf4j @Aspect @Component public class CustomAspect { // 在什么时候起作用 // @Before() 相当于拦截器的 PreHandle() 方法 // @After() 拦截的方法响应之后执行 // @AfterThrowing 方法抛出某些异常的时候调用 // @Around 环绕 覆盖前面三种 // 环绕的方式调用下面的方法 @Around("execution(* com.coolron.*.controller..*.*(..))") // ProceedingJoinPoint 类 包含了当前拦截的方法的一些信息 public Object method(ProceedingJoinPoint pjp) throws Throwable { log.info("=====Aspect处理======="); Object[] args = pjp.getArgs(); for (Object arg : args) { log.info("参数为:" + arg); } long start = System.currentTimeMillis(); // 相当于Filter 的 chain.doFilter() 调用被拦截的那个方法 返回值 object 与 controller中方法的返回值相同 Object object = pjp.proceed(); log.info("Aspect 耗时:" + (System.currentTimeMillis() - start)); return object; } }
execution表达式参看SpringBoot>07 - 事物处理。
观察控制台发现Filter、Interceptor、切片执行顺序:
拦截顺序 : Filter >>> Interceptor >>> Aspect >>> controller
若果有异常返回结果: controller >>> Aspect >>> ControllerAdvice(全局异常处理类上) >>> Interceptor >>> Filter
/** * @Auther: xf * @Date: 2018/11/19 21:42 * @Description: 监听器 */ @Slf4j @WebListener public class RequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent servletRequestEvent) { log.info("监听器销毁>>>>>"); } @Override public void requestInitialized(ServletRequestEvent servletRequestEvent) { log.info("监听器初始化>>>>>"); } }
注意:案例中实现的是request的监听器。用于监听request对象的创建、销毁。
常见的监听器:
1、HttpSessionListener:监听session对象的创建、销毁
2、ServletRequestListener:监听request对象的创建、销毁
3、ServletContextListener:监听servletContext对象的创建、销毁