SUMMARY
SpringBoot abandoned while cumbersome xml configuration register provides several components: ServletRegistrationBean,
FilterRegistrationBean, ServletListenerRegistrationBean, DelegatingFilterProxyRegistrationBean, for registration from the corresponding components, such as filters, etc. listener.
Benpian to analyze the filter registration component FilterRegistrationBean, understand that principle, usually contribute to the development of corresponding problems encountered, can quickly analyze and positioning.
Covering the following points:
FilterRegistrationBean loading mechanism
FilterChain responsibility chain configuration mode
custom FilterChain
a loading mechanism FilterRegistrationBean
first look at the class uml:
Springboot 1.x version:
Springboot 2.x version:
First, ServletContextInitializer is initialized when the initialization of the interface Servlet container, provided. FilterRegistrationBean finally realized ServletContextInitializer, therefore, Servlet container and trigger the initialization will get all FilterRegistrationBean instantiated.
Two versions change is not great, but SpringBoot version 2.x, the AbstractFilterRegistrationBean the registration logic to extract DynamicRegistrationBean abstract class.
Look at the source code.
Spring refresh container performs onRefresh:
Follow this method:
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { //获取指定的 Servlet类型 ServletWebServerFactory factory = getWebServerFactory(); //指定 ServletContextInitializer 触发逻辑 this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
First of all the above to get the current Servlet container type, this part to Jetty example for analysis.
There is a more important parameter above:
this.webServer = factory.getWebServer (getSelfInitializer ());
here passed a callback function getSelfInitializer ():
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { return this::selfInitialize; }
This is used to get all ServletContextInitializer
and instantiate callback function, when triggered it?
When the container starts, execution callInitializers
, by onStartup
trigger callback function. The callback function is defined in ServletWebServerApplicationContext
the selfInitialize
method, follow this method:
private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes( beanFactory); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, getServletContext()); existingScopes.restore(); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, getServletContext()); //Here is an overview of all of the implementation class ServletContextInitializer, gets all of the components registered for (ServletContextInitializer Beans: getServletContextInitializerBeans ()) { beans.onStartup (where servletContext); } }
Follow the above getServletContextInitializerBeans
method:
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() { return new ServletContextInitializerBeans(getBeanFactory()); }
ServletContextInitializerBeans
The object is ServletContextInitializer
a package, constructor as follows:
public ServletContextInitializerBeans (ListableBeanFactory beanFactory) { the this .initializers = new new LinkedMultiValueMap <> (); // get all ServletContextInitializer addServletContextInitializerBeans (beanFactory); addAdaptableBeans (beanFactory); List <ServletContextInitializer> = sortedInitializers new new the ArrayList <> (); // Listener , filter, and servlet logic sorting the this .initializers.values () forEach ((contextInitializers) ->. { AnnotationAwareOrderComparator.sort (contextInitializers); sortedInitializers.addAll (contextInitializers); }); the this .sortedList = Collections.unmodifiableList(sortedInitializers); }
Can see the constructor is performed addServletContextInitializerBeans
, the method was introduced to beanFactory, all that is acquired from the container ServletContextInitializer
, and instantiated, and then sorted. Look at how specific is acquired?
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) { //指定ServletContextInitializer.class 类型 for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType( beanFactory, ServletContextInitializer.class)) { //添加到具体的集合中 addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory); } }
addServletContextInitializerBean
The method determines the type of the specific implementation class, i.e. several registered components mentioned in the introduction:
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) { // Servlet注册组件 if (initializer instanceof ServletRegistrationBean) { Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet(); addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source); } //过滤器注册组件 else if (initializer instanceof FilterRegistrationBean) { Filter source = ((FilterRegistrationBean<?>) initializer).getFilter(); addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); } else if (initializer instanceof DelegatingFilterProxyRegistrationBean) { String source = ((DelegatingFilterProxyRegistrationBean) initializer) .getTargetBeanName(); addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); } else if (initializer instanceof ServletListenerRegistrationBean) { EventListener source = ((ServletListenerRegistrationBean<?>) initializer) .getListener(); addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source); } else { addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory, initializer); } }
Said logic is mainly acquired in container ServletContextInitializer
implementation class classifying them into a corresponding set of components. In order to realize the function of each component.
Continue the main flow, look at the registration logic filter. As shown below:
Get all of the above methods ServletContextInitializer
, circulated registration, follow-up onStartup
methods:
RegistrationBean
Class provides a template method: register
corresponding register component to perform respective logical register. Here's look at the components implement filters registration:
@Override protected Dynamic addRegistration(String description, ServletContext servletContext) { Filter filter = getFilter(); return servletContext.addFilter(getOrDeduceName(filter), filter); }
The method of obtaining the above-described filter, by ServletContext
injecting into the Servlet
container, to follow up addFilter
methods:
@Override public FilterRegistration.Dynamic addFilter (<? FilterName String, Class the extends the Filter> filterClass) { // ...... Final ServletHandler Handler = ServletContextHandler. The this .getServletHandler (); // determine whether the filter has been registered FilterHolder holder = handler.getFilter (filterName); IF (Holder == null ) { // new new filter // Create a new holder, is injected into the ServletHandler Holder = handler.newFilterHolder (Source.JAVAX_API); holder.setName(filterName); //将filter设置到holder中 holder.setFilter(filter); handler.addFilter(holder); return holder.getRegistration(); } if (holder.getClassName()==null && holder.getHeldClass()==null) { //preliminary filter registration completion holder.setHeldClass(filterClass); return holder.getRegistration(); } else return null; //existing filter }
Thus, custom Filter are injected into the Servlet container.
Note: ServletWebServerApplicationContext is SpringBoot 2.x version of the name, corresponding to the version 1.x EmbeddedWebApplicationContext.
Two FilterChain responsibility chain configuration mode
FilterChain using a chain of responsibility pattern, is a typical use chain of responsibility pattern. Similar to the Pipeline mode.
FilterChain objects Jetty is the default form of lazy loading, only the first request came in before initialization, as shown below:
Request comes, will first determine _filterMappings
whether it is empty, not empty the acquisition FilterChain
target.
Continuing with getFilterChain
method:
protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder) { String key=pathInContext==null?servletHolder.getName():pathInContext; int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType()); //通过 url,从缓存中获取 FilterChain if (_filterChainsCached && _chainCache!=null) { FilterChain chain = (FilterChain)_chainCache[dispatch].get(key); if (chain!=null) return chain; } // if it is not acquired, the object is constructed a FilterChain FilterChain catena alberghiera = null ; // determines whether to open the cache IF (_filterChainsCached) { IF (filters.size ()> 0 ) catena alberghiera = new new CachedChain (Filters, servletHolder); Final the Map <String, FilterChain> cache = _chainCache [dispatch]; Final Queue <String> the LRU = _chainLRU [dispatch]; // ? have have the Do WE tOO mANY cached chains // determine whether there was too much cache FilterChain, if greater than the maximum length, it is deleted. while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize) { // The LRU list is not atomic with the cache map, so be prepared to invalidate if // a key is not found to delete. // Delete by LRU (where U==created) String k=lru.poll(); if (k==null) { cache.clear(); break; } cache.remove(k); } cache.put(key,chain); lru.add(key); } else if (filters.size() > 0) chain = new Chain(baseRequest,filters, servletHolder); return chain; }
Jetty FilterChain implements a cache function, a URL for the key, each time a request comes in to obtain the corresponding filter chain according to URL.
Further implements an LRU algorithm, the cache when the length exceeds the maximum, to clean out the unused first key-pair.
But many requests, get a different URL filter chain is the same, so there is no need to open the cache. Providing Jetty _filterChainsCached set, the above code is judged by this variable.
Default is true, use the default cache.
Point to note: only open FilterChain cache, create CachedChain
objects using only chain of responsibility pattern.
If you are creating Chain
objects directly through all the filter processing.
Look at the CachedChain
construction method, the chain of responsibility relevant code:
CachedChain(List<FilterHolder> filters, ServletHolder servletHolder) { if (filters.size()>0) { _filterHolder=filters.get(0); filters.remove(0); //递归处理 _next=new CachedChain(filters,servletHolder); } else _servletHolder=servletHolder; }
Code is relatively simple, the method is configured to perform a recursive process, creating CachedChain
a linked list, to generate the final object of the form:
characterEncodingFilter->hiddenHttpMethodFilter->httpPutFormContentFilter->requestContextFilter->webRequestLoggingFilter->authenticationFilter->traceFilter->applicationContextIdFilter->Jetty_WebSocketUpgradeFilter
Three custom FilterChain
The following example code provides two ways to create FilterChain
, construct and implement a common method recursive recursive implementation.
Define a Filter interface:
public interface MyFilter { String getName(); void execute(FilterChain filterChain); }
Defines two implementation classes:
public class MyFilters{ /** * 定义两个个Myfilter * */ public static class MyFilter1 implements MyFilter{ @Override public String getName() { return "myFilter1"; } @Override public void execute(FilterChain filterChain) { System.out.println(getName()+"before..."); if (null != filterChain) { filterChain.doFilter(filterChain); } System.out.println(getName()+"after..."); } } public static class MyFilter2 implements MyFilter{ @Override public String getName() { return "myFilter2"; } @Override public void execute(FilterChain filterChain) { System.out.println(getName()+"before..."); if (null != filterChain) { filterChain.doFilter(filterChain); } System.out.println(getName()+"after..."); } } }
FilterChain objects:
@Data public class the FilterChain { Private the MyFilter currentFilter; Private the FilterChain Next; Private List <the MyFilter> Filters; / ** * * recursive chain of responsibility * / public the FilterChain (the MyFilter myFilter) { the this .currentFilter = myFilter; } / ** * * fiterChain chain of responsibility mechanism implemented in analog SpringBoot Jetty * / public the FilterChain (List <the MyFilter> Filters) { IF (filters.size ()> 0 ) { the this .currentFilter filters.get = (0); filters.remove(0); this.next = new FilterChain(filters); } } public void doFilter(FilterChain filterChain) { MyFilter currentFilter = filterChain.getCurrentFilter(); if (null != currentFilter) { currentFilter.execute(filterChain.next); } } }
By a recursive construction method:
public class FilterChainBuilder { static List<MyFilter> filters; public static FilterChain buildFilterChainBuild(List<MyFilter> myFilters){ filters = myFilters; return FilterChainInstanceFactory.FILTER_CHAIN; } private static class FilterChainInstanceFactory{ final static FilterChain FILTER_CHAIN = new FilterChain(filters); } }
Recursive by ordinary methods:
public class FilterChainBuilder2 { public static FilterChain buildFilterChain(List<MyFilter> filters) { if (CollectionUtils.isEmpty(filters)) { return null; } MyFilter currentFilter = filters.get(0); FilterChain filterChain2 = new FilterChain(currentFilter); filters.remove(0); if (filters.size() > 0) { filterChain2.setNext(buildFilterChain(filters));
} return filterChain2; } }
具体代码 Github:
https://github.com/admin801122/springboot2-spring5-studying/tree/master/filterChain
Summary
SpringBoot when loading Servlet container implementation class gets all the expansion interface ServletContextInitializer. Filter, etc. listener registration component of the interface is achieved, thereby completing the registration of the respective corresponding mechanisms. Further filter chain uses a LRU algorithm to achieve a caching mechanism, and the chain of responsibility achieved by recursively FilterChain constructor mechanism.
Original link: https://blog.csdn.net/woshilijiuyi/article/details/85014183