在后端项目开发中,会有一些须要基于全局处理的程序。传统基于Servlet容器的程序中,咱们可使用过滤器和监听器,在Java 框架中还可使用拦截器,而面向切面编程AOP更是做为Spring框架中的核心思想被你们所关注。本文一方面从概念上讲解Filter、Listener、Interceptor和aop的区别,另外一方面更是从代码层面讲解如何在SpringBoot中应用开发。java
它们的执行顺序以下(@ControllerAdvice本文暂不作介绍):web
拦截顺序:ServletContextListener> Filter > Interception > AOP > 具体执行的方法 > AOP > @ControllerAdvice > Interception > Filter > ServletContextListenerredis
根据实现原理分红下面两大类:spring
从 Filter -> Interceptor -> aop ,拦截的功能愈来愈细致、强大,尤为是Interceptor和aop能够更好的结合spring框架的上下文进行开发。可是拦截顺序也是愈来愈靠后,请求是先进入Servlet容器的,越早的过滤和拦截对系统性能的消耗越少。具体选用哪一种方法,就须要开发人员根据实际业务状况综合考虑了。数据库
Filter过滤器是Servlet容器层面的,在实现上基于函数回调,能够对几乎全部请求进行过滤。过滤器是对数据进行过滤,预处理过程,当咱们访问网站时,有时候会发布一些敏感信息,发完之后有的会用*替代,还有就是登录权限控制等,一个资源,没有通过受权,确定是不能让用户随便访问的,这个时候,也能够用到过滤器。过滤器的功能还有不少,例如实现URL级别的权限控制、压缩响应信息、编码格式等等。编程
SpringBoot实现过滤器,常见有三种方式,越复杂功能越强大。后端
这种方式最简单,直接实现Filter接口,并使用@Component注解标注为组件自动注入bean。可是缺点是没办法设置过滤的路径,默认是 /* 过滤全部。缓存
Filter接口有 init、doFilter、destroy 三个方法,但 init、destroy 是有默认方法实现,能够不重写。服务器
import org.springframework.stereotype.Component; import javax.servlet.*; import java.io.IOException; @Component public class DemoFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException{ HttpServletRequest httpServletRequest=(HttpServletRequest)servletRequest; HttpServletResponse httpServletResponse=(HttpServletResponse)servletResponse; System.out.println("filter1----------"+httpServletResponse.getStatus()); filterChain.doFilter(servletRequest,servletResponse); } }
这种方式要稍微复杂一点,但更全面。使用 @WebFilter替代 @Component,能够经过该注解设置过滤器的匹配路径。不过须要在启动类中使用@ServletComponentScan。@ServletComponentScan扫描带@WebFilter、@WebServlet、@WebListener并将帮咱们注入bean。session
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter(filterName = "filter1",urlPatterns = {"/hello/*"}) public class DemoFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException{ HttpServletRequest httpServletRequest=(HttpServletRequest)servletRequest; HttpServletResponse httpServletResponse=(HttpServletResponse)servletResponse; System.out.println("filter1----------"+httpServletResponse.getStatus()); filterChain.doFilter(servletRequest,servletResponse); } }
这种方式彻底经过配置类来实现,在只实现过滤器的接口,并不须要经过任何注解注入IOC容器,都经过配置类来注入。
AFilter.java(BFilter.java也相似)
import javax.servlet.*; import java.io.IOException; public class AFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterA----------"); filterChain.doFilter(servletRequest,servletResponse); } }
FilterConfig.java 配置类
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import pers.kerry.demo.webfilter.filter.AFilter; import pers.kerry.demo.webfilter.filter.BFilter; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean AFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); AFilter aFilter=new AFilter(); filterRegistrationBean.setFilter(aFilter); filterRegistrationBean.addUrlPatterns("*"); filterRegistrationBean.setName("AFilter"); filterRegistrationBean.setOrder(1); return filterRegistrationBean; } @Bean public FilterRegistrationBean BFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); BFilter bFilter=new BFilter(); filterRegistrationBean.setFilter(bFilter); filterRegistrationBean.addUrlPatterns("*"); filterRegistrationBean.setName("BFilter"); filterRegistrationBean.setOrder(2); return filterRegistrationBean; } }
Listener监听器也是Servlet层面的,能够用于监听Web应用中某些对象、信息的建立、销毁和修改等动做发生,而后作出相应的响应处理。根据监听对象,将监听器分为3类:
在写Listener的类时,实现方式和Filter同样,一样有两种实时方式。一种是只加@Component;另外一种是 @WebListener 和 @ServletComponentScan 配合使用。不过实现接口则根据监听对象区分,如:ServletContextListener、HttpSessionListener和ServletRequestListener。
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class DemoListener implements ServletContextListener{ @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContextListener 初始化上下文"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContextListener 销毁"); } }
Interceptor拦截器和Filter和Listener有本质上的不一样,前面两者都是依赖于Servlet容器,而Interceptor则是依赖于Spring框架,是aop的一种表现,基于Java的动态代理实现的。在SpringBoot中实现拦截器的方式,有点相似于实现过滤器的第三种方式,因此要经过下面两个步骤。
DemoInterceptor.java
import org.springframework.lang.Nullable; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; public class DemoInterceptor implements HandlerInterceptor{ /** * 预处理回调方法,实现处理器的预处理(如检查登录),第三个参数为响应的处理器 * 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登陆检查失败),不会继续调用其余的拦截器或处理器,此时咱们须要经过response来产生响应 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); String methodName = method.getName(); System.out.println("====拦截到了方法:"+methodName+",preHandle===="); return true; } /** * 后处理回调方法,实现处理器的后处理(但在渲染视图以前),此时咱们能够经过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { System.out.println("====postHandle===="); } /** *整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中咱们能够在此记录结束时间并输出消耗时间,还能够进行一些资源清理,相似于try-catch-finally中的finally,但仅调用处理器执行链中 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { System.out.println("====afterCompletion===="); } }
InterceptorConfig.java
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import pers.kerry.demo.webfilter.interceptor.DemoInterceptor; /** * spring 2.x 之前,经过继承 WebMvcConfigurerAdapter 类 * spring 2.x 以后,实现 WebMvcConfigurer 接口 */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**"); } }
相比较于拦截器,Spring 的aop则功能更强大,封装的更细致,须要单独引用 jar包。
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
在定义AOP的类时,不须要和前面拦截器同样麻烦了,只须要经过注解,底层实现逻辑都经过IOC框架实现好了,涉及到的注解以下:
DemoAspect.java
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @Aspect 注解 使之成为切面类 * @Component 注解 把切面类加入到IOC容器中 */ @Aspect @Component public class DemoAspect { @Pointcut( "execution( public * pers.kerry.demo.webfilter.controller.DemoController.*(..) )") public void doPointcut(){ } @Before("doPointcut()") public void doBefore(){ System.out.println("==doBefore=="); } @After("doPointcut()") public void doAfter(){ System.out.println("==doAfter=="); } @AfterReturning("doPointcut()") public void doAfterReturning(){ System.out.println("==doAfterReturning=="); } /** * 返回值类型Object,对应全部被拦截方法的返回值类型,不能为void */ @Around("doPointcut()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{ System.out.println("==doAround.before=="); Object ret=proceedingJoinPoint.proceed(); System.out.println("==doAround.after=="); return ret; } }