Spring Boot uses embedded containers. How to configure the custom Filter?

Listener, filter and Servlet are three components commonly used in Java Web development. Filter is the most frequently used component, which is often used for simple permission processing, request header filtering and XSS prevention. If we use the traditional Spring MVC for development, we only need to configure the following in Tomcat's web.xml file:

<!-- To configure Listener -->
<listener>
    <listener-class>org.springframework.web.util.WebAppRootListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> 

<!--To configure Filter,There's a Filter,But there are multiple matches url-pattern-->
<!-- with url-partern Configured by filter , if more than one matches the current request, press web.xml in filter-mapping Run in the order that it appears-->
<filter>  
    <filter-name>filter1</filter-name>  
    <filter-class>com.csx.MyFilter</filter-class>  
</filter>  
<filter-mapping>
    <filter-name>filter1</filter-name>  
    <url-pattern>/url/a/*</url-pattern>  
</filter-mapping>  
<filter-mapping>  
    <filter-name>filter1</filter-name>  
    <url-pattern>/url/b/*</url-pattern>  
</filter-mapping>

<!--To configure Servlet-->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.spring.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!-- It's not recommended here/* -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

PS: when the container is started, the above three components are started in the order of Listener > Filter > Servlet. Here, Amway has a way to remember the starting order as "Listener (Servlet)".

It's easy to configure these three components in web.xml, but when using spring boot, we use embedded containers. There is no web.xml file for us to configure. So how to configure the Listener, Filter, Servlet and other components in spring boot?

This blog takes Filter as the column, which describes how to configure Listener, Filter, Servlet and other components in spring boot.

Mode 1: declare Filter as bean

Let's first customize a Filter, which is used to count the call time of an interface.

public class TimeConsumingCalculationFilter implements Filter {

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest httpRequest=(HttpServletRequest)request;
            long startTime = System.nanoTime();
            logger.info(">>>>>>> Begin ["+httpRequest.getRequestURI()+"]...");
            try{
                chain.doFilter(request, response);
            }finally {
                long endTime = System.nanoTime();
                logger.info(">>>>>>> End ["+httpRequest.getRequestURI()+"]["+(endTime-startTime)/1000/1000.0+"ms].");
            }
        }

    }

To configure the above Filter in spring boot, we only need to do the following Configuration in the @ Configuration file:

@Configuration
public class WebConfig {

    private static Logger logger = LoggerFactory.getLogger(WebConfig.class);

    @Bean
    public Filter filter1(){
        return new TimeConsumingCalculationFilter();
    }
}

The Filter above intercepts all requests by default. If we want to configure multiple interceptors, we just need to add another Bean method.

@Configuration
public class WebConfig {

    private static Logger logger = LoggerFactory.getLogger(WebConfig.class);

    @Bean
    public Filter filter1(){
        return new TimeConsumingCalculationFilter();
    }

    @Bean
    public Filter filter2() {
        return new TimeConsumingCalculationFilter2();
    }
}

The above configuration code configures two filters. By default, both filters will intercept all requests in the Order of filter1 -- > Filter2. The logic here is that the configured filter intercepts first, and then the configured filter intercepts. Of course, if we want to specify the interception Order explicitly, we can use the @ Order annotation. However, it should be noted that this annotation must be added to the defined class.

@Order(Ordered.LOWEST_PRECEDENCE - 2)
public class TimeConsumingCalculationFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       //your logic
    }

}
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public class TimeConsumingCalculationFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       //your logic
    }

}

PS: the higher the value in @ order, the lower the priority of execution. The lower the value, the higher the priority. When we customize the execution order, it is recommended to use the @ order (ordered. Low ﹣ precision - 1) configuration. The larger the value subtracted by ordered.low ﹣ precision is, the higher the priority is, so it looks more intuitive.

In addition, Spring also provides an OrderedFilter interface, which is a combination of Filter and Ordered. The principle is the same as above. You can use it according to the situation.

The advantage of the above configuration method is that it is very simple to configure, but the disadvantage is obvious, that is, the configuration is not flexible enough, and all requests will be intercepted by default.

Mode 2: @ WebFilter mode

@The WebFilter annotation is provided in the Servlet, and Spring also supports this annotation. @WebFilter can be configured in a fine-grained way, which is more flexible than the above methods.

@Configuration
public class WebConfig {
    //You can customize URL pattern
    @WebFilter(urlPatterns="/*")
    @Order(Ordered.LOWEST_PRECEDENCE - 2)
    //If "@ Configuration" is not added here, you need to scan three components: "Listener", "Filter" and "Servlet" through "@ ServletComponentScan"
    @Configuration
    public class TimeConsumingCalculationFilter implements Filter {

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //your logic
        }
    }   

    @WebFilter(urlPatterns="/*")
    @Order(Ordered.LOWEST_PRECEDENCE - 2)
    //If "@ Configuration" is not added here, you need to scan three components: "Listener", "Filter" and "Servlet" through "@ ServletComponentScan"
    @Configuration
    public class TimeConsumingCalculationFilter2 implements Filter {

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //your logic
        }
    }   

}

It is easy to configure Filter by @ WebFilter annotation, and it can customize URL pattern and interception order.

Mode 3: use FilterRegistrationBean configuration

@Configuration
public class WebConfig {

    private static Logger logger = LoggerFactory.getLogger(WebConfig.class);

    @Bean
        public FilterRegistrationBean<Filter> filter1() {
            FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
            registrationBean.setName("filter1");
            registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE - 2);
            registrationBean.setFilter(new TimeConsumingCalculationFilter());
            registrationBean.addUrlPatterns("/foo/*");
            return registrationBean;
        }

        @Bean
        public FilterRegistrationBean<Filter> filter2() {
            FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
            registrationBean.setName("filter2");
            registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE - 3);
            registrationBean.setFilter(new TimeConsumingCalculationFilter1());
            registrationBean.addUrlPatterns("/*");
            registrationBean.addInitParameter("key1","value1");
            registrationBean.addInitParameter("key2","value2");
            //Match Filter by Servlet name, not recommended
            registrationBean.addServletNames("name1");
            return registrationBean;
        }

}

Note:

  • There is a one-to-one relationship between FilterRegistrationBean and Filter.
  • If there are more than one FilterRegistrationBean, it needs to call its setName(String name) to declare a unique name for it, otherwise only the first successful registration is valid.
  • If you need to ensure the call order, you can set it by calling its setOrder(int order) method.

Mode 4: using DelegatingFilterProxyRegistrationBean mode

@Configuration
public class WebConfig {

    @Bean("myFilter")
    //After the delegatingfilterproxyregistration bean is configured, the Filter configured in this way will not take effect, only the request of / foo / * will be blocked
    public Filter myFilter(){
        return new TimeConsumingCalculationFilter();
    }

    @Bean
    public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean(){
        DelegatingFilterProxyRegistrationBean filterProxy = new DelegatingFilterProxyRegistrationBean("myFilter");
        filterProxy.addUrlPatterns("/foo/*");
        filterProxy.addInitParameter("targetFilterLifecycle","true");
        filterProxy.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico");
        filterProxy.setDispatcherTypes(DispatcherType.REQUEST);
        return filterProxy;
    }
}

Difference between FilterRegistrationBean and delegatingfilterproxyregistration bean:

  • The FilterRegistrationBean registers the filter directly through the onStartup method.
  • Delegatingfilterproxyregistration bean registers DelegatingFilterProxy in the container of Servlet3.0 +, and implements the ApplicationContextAware interface. The instance ApplicationContext finds the corresponding bean by passing in the name of the custom filter, and generates the proxy object of the corresponding bean.

Grasp a typical example and you will grasp the whole category

  • You can also use method 1 @ WebServlet or ServletRegistrationBean to add a custom Servlet
  • To add a custom Listener, you can also use the method @ WebListener or ServletListenerRegistrationBean. Note that the listening event is generic

Other related categories

  • ServletComponentRegisteringPostProcessor
  • ServletComponentHandler
  • WebListenerHandler
  • WebFilterHandler
  • WebServletHandler

Tags: Java Spring xml Web Development

Posted on Tue, 14 Jan 2020 06:31:10 -0500 by php3ch0