1, the preface

In back-end project development, there will be programs that need to be based on global processing. In traditional Servlet container-based programs, filters and listeners can be used, and interceptors can also be used in Java framework. AOP for aspect programming is the core idea of Spring framework, which is concerned by everyone. On the one hand, this article explains the difference between Filter, Listener, Interceptor and AOP conceptually, on the other hand, it also explains how to apply development in SpringBoot from the code level.

They are executed in this order (@ControllerAdvice is not covered here) :

Intercept order: For example, an Interception for @ControllerAdvice is an Interception for an Interception. ServletContextListener

According to the realization principle, it can be divided into the following two categories:

  1. Filter and Listener: Rely on the Servlet container, implemented based on function callbacks. You can intercept all requests for greater coverage, but you can’t get beans in the IOC container.
  2. Interceptor and AOP: Rely on the Spring framework, based on Java reflection and dynamic proxy implementation. Only controller requests can be intercepted, and beans in the IOC container can be retrieved.

From Filter -> Interceptor -> AOP, the interception function is more and more detailed, powerful, especially Interceptor and AOP can be better combined with the context of spring framework development. However, the order of interception is also more and more later, the request is first into the Servlet container, the earlier the filtering and interception of the less consumption of system performance. Specific choice of which method, need developers according to the actual business situation comprehensive consideration.

2. Filter

Filter filters are Servlet container-level, implemented based on function callbacks, and can Filter almost any request. Filter is to filter the data, the pretreatment process, when we visit the web site, sometimes will release some sensitive information, after you have sent some will substitute *, and log in access control, etc., a resource, without authorization, affirmation is not to let the user to access, this time, also can use the filter. There are many other functions of filters, such as urL-level permission control, compression of response information, encoding format, and so on.

SpringBoot filter implementation, there are three common ways, the more complex the more powerful.

2.1. No path, No order @Component

The simplest approach is to implement the Filter interface directly and automatically inject beans into the Component using the @Component annotation annotation. However, the disadvantage is that there is no way to set the filter path, the default is /* filter all.

The Filter interface has init, doFilter, and destroy methods, but init and destroy are implemented by default and can not be overwritten.

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); }}Copy the code

2.2 There are paths out of order @webFilter +@ServletComponentScan

This approach is slightly more complex, but more comprehensive. Use @webFilter instead of @Component to set the matching path of the filter. However, you need to use @ServletComponentScan in the startup class. @ServletComponentScan scans @WebFilter, @WebServlet, @WebListener and will help us inject beans.

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); }}Copy the code

2.3 Path and Sequence @Configuration

This is done entirely through the configuration class, which does not need to be injected into the IOC container through any annotations on the filter only interface.

Afilter.java (bfilter.java is also similar)

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); }}Copy the code

The FilterConfig. Java configuration class

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; }}Copy the code

3. Listener

The Listener is also at the Servlet level, and can be used to listen to the creation, destruction, and modification of certain objects and information in Web applications, and then make corresponding responses. According to listening objects, listeners can be divided into three types:

  1. ServletContext: Implements the interface ServletContextListener corresponding to the Application. There is only one in the entire Web service, which is destroyed when the Web service is shut down. It can be used for data caching, such as in conjunction with Redis, to fetch data from the database to the cache server at Web service creation time.
  2. HttpSession: corresponding to the session session, implement the HttpSessionListener interface. Created at the start of a session and destroyed after one end closes the session. Can be used to get the number of online users.
  3. ServletRequest: Implements the interface ServletRequestListener corresponding to the Request. The Request object is created when a customer sends a request. It encapsulates the request data and is destroyed after the request is processed. Can be used to encapsulate user information.

The Listener class is implemented in the same way as the Filter class. There are also two real-time methods. One is to just add @Component; The other is @webListener and @ServletComponentScan. However, implementation interfaces are distinguished by listener objects, such as ServletContextListener, HttpSessionListener, and 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 initializes the context "); } @override public void contextDestroyed(ServletContextEvent sce) {system.out.println ("ServletContextListener destroyed "); }}Copy the code

4. Interceptor

Interceptor is fundamentally different from Filter and Listener in that both rely on Servlet containers, while Interceptor relies on the Spring framework, a representation of AOP, and is implemented based on Java’s dynamic proxy. The way you implement interceptors in SpringBoot is somewhat similar to the way you implement filters in the third way, so there are two steps.

  1. Classes that declare interceptors: Implement the preHandle, postHandle, and afterCompletion methods by implementing the HandlerInterceptor interface.
  2. Configure interceptors through configuration classes: Implement the addInterceptors method by implementing the WebMvcConfigurer interface.

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{/** ** a callback method that implements pre-processing of a processor (such as checking login). The third parameter is the return value of the response processor * : True means to continue the process (such as invoking the next interceptor or processor); False indicates that the process interrupts (such as a failed login check) and does not continue calling other interceptors or handlers, * @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. The out. Println (" = = = = to intercept the method: "+ methodName +", preHandle = = = = "); return true; } /** * post-processing callback method to implement post-processing of the processor (but before rendering the view), at this point we can use modelAndView (modelAndView object) to process model data or to process the view, Public void postHandle(HttpServletRequest Request, HttpServletResponse Response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { System.out.println("====postHandle===="); } /** * A callback method is used when the view is finished rendering. For example, in performance monitoring, we can record the end time and output the elapsed time. We can also perform some resource cleaning, similar to finally in try-catch-finally. Public void afterCompletion(HttpServletRequest Request, HttpServletResponse Response, Object handler, @Nullable Exception ex) throws Exception { System.out.println("====afterCompletion===="); }}Copy the code

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 configurerAdapter configurerAdapter configurerAdapter configurerAdapter */ @configuration Public class InterceptorConfig implements WebMvcConfigurer {@override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**"); }}Copy the code

5, AOP

Compared to the interceptor, Spring’s AOP is more powerful and packaged in more detail, requiring separate references to jar packages.

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>Copy the code

When defining AOP classes, there is no need to go through the same trouble as the previous interceptor, just through annotations, the underlying implementation logic is implemented through the IOC framework, the annotations involved are as follows:

  • Aspect: Define a Java class as an Aspect class.
  • Pointcut: Defines a Pointcut, which can be a regular expression, such as all the functions under a package in the following example, or an annotation, etc.
  • @before: Cut content at the beginning of the pointcut.
  • After: Cut content at the end of the pointcut.
  • AfterReturning: Process logic after the pointcut returns content.
  • Around: Cut content before and after the pointcut and control when the content of the pointcut itself executes. In principle, this is an alternative to @before and @after.
  • @afterthrowing: Handles the logic after an exception is thrown in the content section.
  • @ORDER (100) : AOP slice execution Order, the lower the @before value, the earlier the execution of @after and @afterRETURNING values.

DemoAspect.java

import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @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=="); } /** * Return value type Object, corresponding to the return value type of all intercepted methods, 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; }}Copy the code