[SpringBoot] Add Filter to SpringBoot

Because springboot is based on servlet 3.0+, the embedded tomcat container cannot configure Filter in web.xml as before. This article is based on springboot 1.5.6

First

@WebFilter(filterName = "myFilter",urlPatterns = "/*")
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    }

    @Override
    public void destroy() {
    }
}

@SpringBootApplication
@EnableAutoConfiguration
@EnableWebMvc
@ServletComponentScan(basePackages = "com.fanyin.eghm")
public class EghmApplication {

    public static void main(String[] args) {
        SpringApplication.run(EghmApplication.class, args);
    }
}
  • The package path scanned by @ServletComponentScan must contain the Filter

Second

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new MyFilter2());
        bean.addUrlPatterns("/*");
        return bean;
    }
}

Third

@Bean("proxyFilter")
    public Filter filter (){
        return new Filter() {
            @Override
            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
            }

            @Override
            public void destroy() {
            }
        };
    }

    @Bean
    public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean(){
        DelegatingFilterProxyRegistrationBean bean = new DelegatingFilterProxyRegistrationBean("proxyFilter");
        bean.addUrlPatterns("/*");
        return bean;
    }

Method 4

@Bean("myFilter")
    public Filter filter (){
        return new Filter() {
            @Override
            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
            }

            @Override
            public void destroy() {
            }
        };
    }

Explain:

  • Similar to the second and third, both implements the AbstractFilterRegistrationBean interface, which indirectly implements the ServletContextInitializer, where springboot looks for the beans that implement the interface after starting the container and calls the onStartup() method to add a custom filter. The difference between the two is that DelegatingFilterProxyRegistrationBean uses the proxyFilter name passed in to the WebApplicationContainerExt finds the Fillter Bean and generates a proxy Filter object based on it through DelegatingFilterProxy.
    FilterRegistrationBean, on the other hand, sets a Filter directly, so it can be managed by a spring container or not by a spring
    Note: If a Filter is declared as a Bean, it does not need to be defined as a FilterRegistrationBean and will also be discovered and added by spring. Method 4, which does not define interception rules, etc. Default global, use caution

Change execution springboot 2.0 to ServletWebServerApplicationContext after the EmbeddedWebApplicationContext container starts

private void selfInitialize(ServletContext servletContext) throws ServletException {
        prepareEmbeddedWebApplicationContext(servletContext);
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
                beanFactory);
        WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
                getServletContext());
        existingScopes.restore();
        WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
                getServletContext());
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
            beans.onStartup(servletContext);
        }
    }

getServletContextInitializerBeans() returns a collection that implements the ServletContextInitializer interface and initializes some data as follows:

public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
        this.initializers = new LinkedMultiValueMap<Class<?>, ServletContextInitializer>();
        addServletContextInitializerBeans(beanFactory);
        addAdaptableBeans(beanFactory);
        List<ServletContextInitializer> sortedInitializers = new ArrayList<ServletContextInitializer>();
        for (Map.Entry<?, List<ServletContextInitializer>> entry : this.initializers
                .entrySet()) {
            AnnotationAwareOrderComparator.sort(entry.getValue());
            sortedInitializers.addAll(entry.getValue());
        }
        this.sortedList = Collections.unmodifiableList(sortedInitializers);
    }
  • addAdaptableBeans addServletContextInitializerBeans adds Filter and Servlet before calling onStartup() method, note that Filter and Servlet must have been declared as Bean
  • The first is ultimately the second, except that when springboot starts, the scanned package path is passed as an input to ServletComponentRegisteringPostProcessor, which implements the BeanFactoryPostProcessor interface, so after BeanFactory initializes, the postProcessBeanFactory() method is called, in which the corresponding class is scanned through the previously incoming package.And convert the @WebFilter class from the corresponding processor to a FilterRegistrationBean

ServletComponentRegisteringPostProcessor

    ServletComponentRegisteringPostProcessor(Set<String> packagesToScan) {
        this.packagesToScan = packagesToScan;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
            throws BeansException {
        if (isRunningInEmbeddedContainer()) {
            ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();
            for (String packageToScan : this.packagesToScan) {
                scanPackage(componentProvider, packageToScan);
            }
        }
    }

    private void scanPackage(
            ClassPathScanningCandidateComponentProvider componentProvider,
            String packageToScan) {
        for (BeanDefinition candidate : componentProvider
                .findCandidateComponents(packageToScan)) {
            if (candidate instanceof ScannedGenericBeanDefinition) {
                for (ServletComponentHandler handler : handlers) {
                    handler.handle(((ScannedGenericBeanDefinition) candidate),
                            (BeanDefinitionRegistry) this.applicationContext);
                }
            }
        }
    }

WebFilterHandler

    WebFilterHandler() {
        super(WebFilter.class);
    }

    @Override
    public void doHandle(Map<String, Object> attributes,
            ScannedGenericBeanDefinition beanDefinition,
            BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .rootBeanDefinition(FilterRegistrationBean.class);
        builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));
        builder.addPropertyValue("dispatcherTypes", extractDispatcherTypes(attributes));
        builder.addPropertyValue("filter", beanDefinition);
        builder.addPropertyValue("initParameters", extractInitParameters(attributes));
        String name = determineName(attributes, beanDefinition);
        builder.addPropertyValue("name", name);
        builder.addPropertyValue("servletNames", attributes.get("servletNames"));
        builder.addPropertyValue("urlPatterns",
                extractUrlPatterns("urlPatterns", attributes));
        registry.registerBeanDefinition(name, builder.getBeanDefinition());
    }

Off-topic topic

Adding a custom Servlet can also take method 1@WebServlet or ServletRegistrationBean
Adding a custom Listener can also take method 1@WebListener or ServletListenerRegistrationBean, noting that listening events are generic



Author: Ergo is fierce
Link: https://www.jianshu.com/p/3d421fbce734
Source: Short Book
Copyright belongs to the author.For commercial reprinting, please contact the author for authorization. For non-commercial reprinting, please indicate the source.

Keywords: SpringBoot Spring Tomcat xml

Added by mblack0508 on Thu, 28 Nov 2019 05:50:07 +0200