Spring filter

What is a filter

The Filter, defined in the Servlet specification, is supported by the Servlet container. This interface is defined under the Javax. Servlet package and is mainly preprocessed in the client request (HttpServletRequest). And post-processing of the server response (HttpServletResponse). The interface code is as follows:

package javax.servlet;

import java.io.IOException;

public interface Filter {
    void init(FilterConfig var1) throws ServletException;

    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

    void destroy(a);
}

Copy the code

Analyze the above three interface methods:

  • Init (FilterConfig): The initialization interface, called when a user-defined Filter is initialized, serves the same purpose as the init method of the Servlet.
  • doFilter(ServletRequest,ServletResponse,FilterChain)This method is called every time a user’s request comes in, and is called before the Servlet’s service method (if we are developing a Servlet project), and FilterChain represents the entire current request chain, through the callFilterChain.doFilterYou can pass the request on, and if you want to intercept the request, you don’t have to call filterchain-dofilter, and the request is returned,So Filter is a chain of responsibility design patternIn thespring securityI use a lot of filters. I have a chain of filters.
  • Destroy: This method is called when the Filter object is destroyed. Note that after the Web container calls this method, the container calls the doFilter method again.

How do I implement my own filters

First we need to create a class that implements the Filter interface, and then override the methods in the interface:

package com.example.demojava.filter;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
@Order(1)   // Filter order, the smaller the value, the earlier the execution
@WebFilter(urlPatterns = "/demoFilter", filterName = "filterTest")
public class Filter1 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter initialization...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("DoFilter () start execution: send to" + ((HttpServletRequest) servletRequest).getRequestURL().toString() + "Request has been blocked.");

        System.out.println(To verify that the interface is called, try to get the contentType as follows: + servletResponse.getContentType());

        // filter; Forward the request to the next filter chain
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println(To verify that the interface is called, try to get the contentType as follows: + servletResponse.getContentType());

        System.out.println("DoFilter () execution completed.");

    }

    @Override
    public void destroy(a) {
        System.out.println("Filter destroyed..."); }}Copy the code

When we configure multiple filters and a request can be intercepted multiple times, the request flows along the client -> filter 1 -> filter 2 -> Servlet -> filter 2 -> filter 1 -> client chain

@Component
@Order(2)   // Filter order, the smaller the value, the earlier the execution
@WebFilter(urlPatterns = "/demoFilter", filterName = "filterTest2")
public class Filter2 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter2 initialization...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("DoFilter2 () start: send to" + ((HttpServletRequest) servletRequest).getRequestURL().toString() + "Request has been blocked.");

        System.out.println(To verify that the interface is called, try to get the contentType as follows: + servletResponse.getContentType());

        // filter; Forward the request to the next filter chain
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println(To verify that the interface is called, try to get the contentType as follows: + servletResponse.getContentType());

        System.out.println("DoFilter2 () execution completed.");

    }

    @Override
    public void destroy(a) {
        System.out.println("Filter2 destroying..."); }}Copy the code

Then create a Controller that provides two request paths:

@RestController
@RequestMapping("demoFilter")
public class FilterController {

    @GetMapping("hello")
    public String hello(a) {
        System.out.println("Interface called: hello()");
        return "hello filter";
    }

    @GetMapping("hi")
    public String hi(a) {
        System.out.println("Interface called: hi()");
        return "hi filter"; }}Copy the code

Start the project and you can see that our filter has been successfully initialized as the program starts.

Send a request to each interface and see the result:

doFilter () implementation: request has been sent to http://localhost:8080/demoFilter/hi to intercept test whether interface is invoked, attempts to acquire contentType is as follows: nulldoFilter2 () implementation: request has been sent to http://localhost:8080/demoFilter/hi to intercept test whether interface is invoked, attempts to acquire contentType is as follows: null interface is called: Hi () checks if the interface is called, trying to get contentType as follows: text/plain; charset=UTF-8doFilter2() No further action is required. To verify that the interface is called, try to get contentType as follows: text/plain; charset=UTF-8doFilter() No further action is required.Copy the code

Finally, the project is stopped, and the filter is destroyed.

As you can see, when a request meets the filter criteria of multiple filters, filterchain.dofilter () passes it to the next filter in a certain Order (which can be specified by @Order) until it enters the servlet for the actual invocation of the interface. When the call is complete, the response returns along the original path and passes through the various filters again before finally reaching the client.

The interceptor

What is an interceptor

Interceptors are an AOP implementation strategy for intercepting a method or field before it is accessed, and then adding some action before or after it. Like a filter, the interceptor is a chained call. Each interceptor call is executed in the order in which it is declared. In general, interceptors can be used for:

  • Logging: Logs of requests for information for information monitoring, statistics, and so on

  • Permission check: Checks user access permission, authentication, or authorization

  • Performance monitoring: Get the processing time of the request by logging the start time and end time before and after the interceptor enters the processor

  • Common behavior: Read cookies to get user information and put user objects in the request header to facilitate subsequent processes

Interceptor interface methods

In SpringMVC, the DispatcherServlet captures each request, which can be processed by the interceptor before it reaches the corresponding Controller, where it is pre-processed before it finally reaches the Controller.

The interceptor interface is org. Springframework. Web. Servlet. HandlerInterceptor interface, the interface code is as follows:

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {}default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {}}Copy the code

Interface method interpretation:

  • The preHandle method prehandles the request from the client. If the method returns true, it continues. If false, it interrupts the request and does not send it to the Controller
  • PostHandler method: after the request is processed, that is, after the Controller method call, but beforepreHandleMethod returns true. Specifically,postHandlerThe method will be called before the DispatcherServlet does the view return rendering, which means we can handle the Controller later in this methodModelAndViewObject to operate on
  • AfterCompletion method: This method is executed after the entire request is completed, of coursepreHandleMethod returns true. This method is generally used for resource cleanup

How to implement an interceptor of your own

Also, first create a class that implements the HandlerInterceptor interface, and then override the methods in the interface:

package com.demo.demofilter.demofilter.interceptor;

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class DemoInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("afterHandle"); }} Copy the codeCopy the code

Then you need to register the interceptor, specifying which interceptor to use and the interceptor URL to intercept:

package com.demo.demofilter.demofilter.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //  If you have more than one interceptor, proceed to Registry.add to add it further
        registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/demoInterceptor/**"); }} Copy the codeCopy the code

Finally, the Controller

package com.demo.demofilter.demofilter.interceptor;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("demoInterceptor")
public class InterceptorController {

    @GetMapping("hello")
    public String hello(a) {
        System.out.println("Interface called: hello()");
        return "hello interceptor";
    }

    @GetMapping("hi")
    public String hi(a) {
        System.out.println("Interface called: hi()");
        return "hi interceptor"; }} Copy the codeCopy the code

The running results are as follows:

The preHandler interface is called: Hello () postHandler afterHandlerCopy the code

Other Matters needing attention

In the Http request execution process, there are several steps:

  1. The request is captured by the DispatcherServlet
  2. The DispatcherServlet maps the received URL to the corresponding Controller
  3. The request is processed by the interceptor before it reaches the corresponding Controller
  4. After the processing is complete, parse the view
  5. Returns the view

Therefore, only requests that go through the DispatcherServlet will be caught by the interceptor, and our custom Servlet requests will not be intercepted.

Filter and interceptor comparison summary

  1. Filters are based on callbacks to functions, while interceptors are based on Java reflection
  2. The Filter depends on the Servlet container. The Interceptor relies on the framework container and can call various dependencies in the IOC container
  3. Interceptors canpreHandleMethod to interrupt by returning false. Filters are more complex, dealing with request and response objects to raise interrupts, requiring additional actions, such as redirecting the user to an error page
  4. Filters can only be used before and after a request, whereas interceptors can be detailed to each method

In the Tomcat container, the execution order is Filter -> Servlet -> Interceptor -> Controller

The last

This article ends, thanks for reading. If you feel good, please pay attention to the public account [when I meet You], your support is the biggest motivation for my writing.