In the previous article, we talked about the starting process of Tomcat in SpringBoot, and then we talked about how to add Servlet, Filter, Listener to Tomcat in SpringBoot
Customize Servlet, Filter, Listener
Declare ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean in Spring container
@Bean public ServletRegistrationBean customServlet() { return new ServletRegistrationBean(new CustomServlet(), "/custom"); } private static class CustomServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("receive by custom servlet"); } }
Customize a Servlet, override the service to implement its own business logic, then inject a bean instance of ServletRegistrationBean type into the Spring container through the @Bean annotation, and instantiate a custom Servlet as a parameter so that the custom Servlet is added to Tomcat.
The @ServletComponentScan annotation is used with the @WebServlet, @WebFilter, and @WebListener annotations
The @ServletComponentScan annotation enables the ImportServletComponentScanRegistrar class, which is an implementation class of the ImportBeanDefinitionRegistrar interface and is resolved by the Spring container.ServletComponentScanRegistrar parses the @ServletComponentScan annotation internally, and then registers ServletComponentRegisteringPostProcessor, a BeanFactoryPostProcessor, in the Spring container to resolve whether the scanned class has @WebServlet, @WebListener, @WebFilter annotations, or if any, to convert these three types of classes into ServletRegistrationBean, FileTerRegistrationBean or ServletListenerRegistrationBean and let the Spring container resolve:
@SpringBootApplication @ServletComponentScan public class EmbeddedServletApplication { ... } @WebServlet(urlPatterns = "/simple") public class SimpleServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("receive by SimpleServlet"); } }
Declare a Servlet, Filter, or Listener in the Spring container
@Bean(name = "dispatcherServlet") public DispatcherServlet myDispatcherServlet() { return new DispatcherServlet(); }
We found it easy to add Servlets, Filter s, or Listener s to Tomcat. Do you remember how SpringMVC configured Dispatcher Servlets in the past?In web.xml:
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
Is it a lot more complicated than configuring a Servlet in our SpringBoot? Although customizing a Servlet in SpringBoot is simple, its underlying level is not. Let's analyze how it works
ServletRegistrationBean,FilterRegistrationBean,ServletListenerRegistrationBean
Let's look at these special classes:
ServletRegistrationBean
public class ServletRegistrationBean extends RegistrationBean { //Storage target Servlet Example private Servlet servlet; //Deposit Servlet Of urlMapping private Set<String> urlMappings; private boolean alwaysMapUrl; private int loadOnStartup; private MultipartConfigElement multipartConfig; public ServletRegistrationBean(Servlet servlet, String... urlMappings) { this(servlet, true, urlMappings); } public ServletRegistrationBean(Servlet servlet, boolean alwaysMapUrl, String... urlMappings) { this.urlMappings = new LinkedHashSet(); this.alwaysMapUrl = true; this.loadOnStartup = -1; Assert.notNull(servlet, "Servlet must not be null"); Assert.notNull(urlMappings, "UrlMappings must not be null"); this.servlet = servlet; this.alwaysMapUrl = alwaysMapUrl; this.urlMappings.addAll(Arrays.asList(urlMappings)); } public void onStartup(ServletContext servletContext) throws ServletException { Assert.notNull(this.servlet, "Servlet must not be null"); String name = this.getServletName(); if (!this.isEnabled()) { logger.info("Servlet " + name + " was not registered (disabled)"); } else { logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings); Dynamic added = servletContext.addServlet(name, this.servlet); if (added == null) { logger.info("Servlet " + name + " was not registered (possibly already registered?)"); } else { this.configure(added); } } } //slightly }
In our example, we use return new Servlet RegistrationBean (new CustomServlet (), "/custom"); we know that the target Servlet instance and urlMapping are stored in the Servlet RegistrationBean and inherit the RegistrationBean class
FilterRegistrationBean
public class FilterRegistrationBean extends AbstractFilterRegistrationBean { //Storage target Filter object private Filter filter; public FilterRegistrationBean() { super(new ServletRegistrationBean[0]); } public FilterRegistrationBean(Filter filter, ServletRegistrationBean... servletRegistrationBeans) { super(servletRegistrationBeans); Assert.notNull(filter, "Filter must not be null"); this.filter = filter; } public Filter getFilter() { return this.filter; } public void setFilter(Filter filter) { Assert.notNull(filter, "Filter must not be null"); this.filter = filter; } } abstract class AbstractFilterRegistrationBean extends RegistrationBean { private static final EnumSet<DispatcherType> ASYNC_DISPATCHER_TYPES; private static final EnumSet<DispatcherType> NON_ASYNC_DISPATCHER_TYPES; private static final String[] DEFAULT_URL_MAPPINGS; private Set<ServletRegistrationBean> servletRegistrationBeans = new LinkedHashSet(); private Set<String> servletNames = new LinkedHashSet(); private Set<String> urlPatterns = new LinkedHashSet(); //Rewrite onStartup Method public void onStartup(ServletContext servletContext) throws ServletException { Filter filter = this.getFilter(); Assert.notNull(filter, "Filter must not be null"); String name = this.getOrDeduceName(filter); if (!this.isEnabled()) { this.logger.info("Filter " + name + " was not registered (disabled)"); } else { Dynamic added = servletContext.addFilter(name, filter); if (added == null) { this.logger.info("Filter " + name + " was not registered (possibly already registered?)"); } else { this.configure(added); } } } //slightly... }
We see that the target Filter object is also saved in the FilterRegistrationBean and inherits the RegistrationBean
ServletListenerRegistrationBean
public class ServletListenerRegistrationBean<T extends EventListener> extends RegistrationBean { //Stored target listener private T listener; public ServletListenerRegistrationBean() { } public ServletListenerRegistrationBean(T listener) { Assert.notNull(listener, "Listener must not be null"); Assert.isTrue(isSupportedType(listener), "Listener is not of a supported type"); this.listener = listener; } public void setListener(T listener) { Assert.notNull(listener, "Listener must not be null"); Assert.isTrue(isSupportedType(listener), "Listener is not of a supported type"); this.listener = listener; } public void onStartup(ServletContext servletContext) throws ServletException { if (!this.isEnabled()) { logger.info("Listener " + this.listener + " was not registered (disabled)"); } else { try { servletContext.addListener(this.listener); } catch (RuntimeException var3) { throw new IllegalStateException("Failed to add listener '" + this.listener + "' to servlet context", var3); } } } //slightly... }
The same is true for ServletListenerRegistrationBean, so let's look at the RegistrationBean class
public abstract class RegistrationBean implements ServletContextInitializer, Ordered { ... } public interface ServletContextInitializer { void onStartup(ServletContext var1) throws ServletException; }
We found that the RegistrationBean implements the ServletContextInitializer interface, and that there is an onStartup method that implements the onStartup method in ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean.
ServletContextInitializer is the initialization interface provided when the Servlet container is initialized.Therefore, Servlet container initialization gets and triggers the onStartup method in all FilterRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean instances
So when exactly triggered the onStartup methods for these classes?
When the Tomcat container starts, callInitializers are executed, all ServletContextInitializer s are obtained, and the onStartup method is executed circularly to trigger the callback method.When did the FilterRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean instances join the Initializers collection?This takes a look back at how Tomcat started in our last article
Startup of Servlet Container
As you can see in my last article, I'll just copy the code here
EmbeddedWebApplicationContext
1 @Override 2 protected void onRefresh() { 3 super.onRefresh(); 4 try { 5 //Core method: get embedded Servlet Container factories, and obtained from factories Servlet container 6 createEmbeddedServletContainer(); 7 } 8 catch (Throwable ex) { 9 throw new ApplicationContextException("Unable to start embedded container", ex); 10 } 11 } 12 13 private void createEmbeddedServletContainer() { 14 EmbeddedServletContainer localContainer = this.embeddedServletContainer; 15 ServletContext localServletContext = getServletContext(); 16 if (localContainer == null && localServletContext == null) { 17 //Get Embedded First Servlet Container Factory 18 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); 19 //Get the corresponding embedding based on the container factory Servlet container 20 this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer()); 21 } 22 else if (localServletContext != null) { 23 try { 24 getSelfInitializer().onStartup(localServletContext); 25 } 26 catch (ServletException ex) { 27 throw new ApplicationContextException("Cannot initialize servlet context",ex); 28 } 29 } 30 initPropertySources(); 31 }
The key code is in line 20, getting all the Initializers through getSelfInitializer() and passing them into the Servlet container, the core of which is the getSelfInitializer() method:
1 private ServletContextInitializer getSelfInitializer() { 2 //Just created one ServletContextInitializer Instance Return 3 //therefore Servlet When the container starts, the object's onStartup Method 4 return new ServletContextInitializer() { 5 public void onStartup(ServletContext servletContext) throws ServletException { 6 EmbeddedWebApplicationContext.this.selfInitialize(servletContext); 7 } 8 }; 9 }
We see that only a ServletContextInitializer instance is created and returned, so when the Servlet container starts, it calls the onStartup method of this object. Let's analyze the logic in its onStartup, the selfInitialize method, and pass in the Servlet context object
selfInitialize
1 private void selfInitialize(ServletContext servletContext) throws ServletException { 2 prepareWebApplicationContext(servletContext); 3 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 4 ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(beanFactory); 5 WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,getServletContext()); 6 existingScopes.restore(); 7 WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,getServletContext()); 8 //This is where you get everything ServletContextInitializer Implement class, which gets all registered components 9 for (ServletContextInitializer beans : getServletContextInitializerBeans()) { 10 //Execute All ServletContextInitializer Of onStartup Method 11 beans.onStartup(servletContext); 12 } 13 }
The key code is in lines 9 and 11, getting all the ServletContextInitializer implementation classes, then iterating through the onStartup method that executes all ServletContextInitializers
Get all ServletContextInitializer s
Let's look at the getServletContextInitializerBeans method
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() { return new ServletContextInitializerBeans(getBeanFactory()); }
The ServletContextInitializerBeans object is a wrapper for ServletContextInitializer:
1 public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> { 2 private final MultiValueMap<Class<?>, ServletContextInitializer> initializers = new LinkedMultiValueMap(); 3 //Store all ServletContextInitializer 4 private List<ServletContextInitializer> sortedList; 5 6 public ServletContextInitializerBeans(ListableBeanFactory beanFactory) { 7 //implement addServletContextInitializerBeans 8 this.addServletContextInitializerBeans(beanFactory); 9 //implement addAdaptableBeans 10 this.addAdaptableBeans(beanFactory); 11 List<ServletContextInitializer> sortedInitializers = new ArrayList(); 12 Iterator var3 = this.initializers.entrySet().iterator(); 13 14 while(var3.hasNext()) { 15 Entry<?, List<ServletContextInitializer>> entry = (Entry)var3.next(); 16 AnnotationAwareOrderComparator.sort((List)entry.getValue()); 17 sortedInitializers.addAll((Collection)entry.getValue()); 18 } 19 this.sortedList = Collections.unmodifiableList(sortedInitializers); 20 } 21 22 private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) { 23 Iterator var2 = this.getOrderedBeansOfType(beanFactory, ServletContextInitializer.class).iterator(); 24 25 while(var2.hasNext()) { 26 Entry<String, ServletContextInitializer> initializerBean = (Entry)var2.next(); 27 this.addServletContextInitializerBean((String)initializerBean.getKey(), (ServletContextInitializer)initializerBean.getValue(), beanFactory); 28 } 29 30 } 31 32 private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) { 33 if (initializer instanceof ServletRegistrationBean) { 34 Servlet source = ((ServletRegistrationBean)initializer).getServlet(); 35 this.addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source); 36 } else if (initializer instanceof FilterRegistrationBean) { 37 Filter source = ((FilterRegistrationBean)initializer).getFilter(); 38 this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); 39 } else if (initializer instanceof DelegatingFilterProxyRegistrationBean) { 40 String source = ((DelegatingFilterProxyRegistrationBean)initializer).getTargetBeanName(); 41 this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); 42 } else if (initializer instanceof ServletListenerRegistrationBean) { 43 EventListener source = ((ServletListenerRegistrationBean)initializer).getListener(); 44 this.addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source); 45 } else { 46 this.addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory, initializer); 47 } 48 49 } 50 51 private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory, Object source) { 52 this.initializers.add(type, initializer); 53 if (source != null) { 54 this.seen.add(source); 55 } 56 57 if (logger.isDebugEnabled()) { 58 String resourceDescription = this.getResourceDescription(beanName, beanFactory); 59 int order = this.getOrder(initializer); 60 logger.debug("Added existing " + type.getSimpleName() + " initializer bean '" + beanName + "'; order=" + order + ", resource=" + resourceDescription); 61 } 62 63 } 64 65 private void addAdaptableBeans(ListableBeanFactory beanFactory) { 66 MultipartConfigElement multipartConfig = this.getMultipartConfig(beanFactory); 67 this.addAsRegistrationBean(beanFactory, Servlet.class, new ServletContextInitializerBeans.ServletRegistrationBeanAdapter(multipartConfig)); 68 this.addAsRegistrationBean(beanFactory, Filter.class, new ServletContextInitializerBeans.FilterRegistrationBeanAdapter(null)); 69 Iterator var3 = ServletListenerRegistrationBean.getSupportedTypes().iterator(); 70 71 while(var3.hasNext()) { 72 Class<?> listenerType = (Class)var3.next(); 73 this.addAsRegistrationBean(beanFactory, EventListener.class, listenerType, new ServletContextInitializerBeans.ServletListenerRegistrationBeanAdapter(null)); 74 } 75 76 } 77 78 public Iterator<ServletContextInitializer> iterator() { 79 //Return to All ServletContextInitializer 80 return this.sortedList.iterator(); 81 } 82 83 //slightly... 84 }
We see a sortedList in ServletContextInitializerBeans that holds all ServletContextInitializers, that is, we get all ServletContextInitializers in its construction method and put them into the sortedList collection. Let's look at the logic of its construction method and see that line 8 calls first
addServletContextInitializerBeans method:
1 private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) { 2 //from Spring Get all in container ServletContextInitializer.class Type Bean 3 for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)) { 4 //Add to a specific collection 5 addServletContextInitializerBean(initializerBean.getKey(),initializerBean.getValue(), beanFactory); 6 } 7 }
We see that all ServletContextInitializer.class type beans are retrieved from the Spring container first, where our custom ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean are retrieved, and then the addServletContextInitializerBean method is called:
1 private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) { 2 //judge ServletRegistrationBean type 3 if (initializer instanceof ServletRegistrationBean) { 4 Servlet source = ((ServletRegistrationBean)initializer).getServlet(); 5 //take ServletRegistrationBean Join the collection 6 this.addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source); 7 //judge FilterRegistrationBean type 8 } else if (initializer instanceof FilterRegistrationBean) { 9 Filter source = ((FilterRegistrationBean)initializer).getFilter(); 10 //take ServletRegistrationBean Join the collection 11 this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); 12 } else if (initializer instanceof DelegatingFilterProxyRegistrationBean) { 13 String source = ((DelegatingFilterProxyRegistrationBean)initializer).getTargetBeanName(); 14 this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); 15 } else if (initializer instanceof ServletListenerRegistrationBean) { 16 EventListener source = ((ServletListenerRegistrationBean)initializer).getListener(); 17 this.addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source); 18 } else { 19 this.addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory, initializer); 20 } 21 22 } 23 24 private void addServletContextInitializerBean(Class<?> type, String beanName, 25 ServletContextInitializer initializer, ListableBeanFactory beanFactory, Object source) { 26 //Join in initializers in 27 this.initializers.add(type, initializer); 28 }
Obviously, to determine the ServletContextInitializer type obtained from the Spring container, such as ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean, and add to the initializers collection, let's look at another method in the constructor, addAdaptableBeans(beanFactory):
1 private void addAdaptableBeans(ListableBeanFactory beanFactory) { 2 //from beanFactory Get All Servlet.class and Filter.class Type Bean,And encapsulated into RegistrationBean Object, joined to a collection 3 this.addAsRegistrationBean(beanFactory, Servlet.class, new ServletContextInitializerBeans.ServletRegistrationBeanAdapter(multipartConfig)); 4 this.addAsRegistrationBean(beanFactory, Filter.class, new ServletContextInitializerBeans.FilterRegistrationBeanAdapter(null)); 5 } 6 7 private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type, Class<B> beanType, ServletContextInitializerBeans.RegistrationBeanAdapter<T> adapter) { 8 //from Spring Get all in container Servlet.class and Filter.class Type Bean 9 List<Entry<String, B>> beans = this.getOrderedBeansOfType(beanFactory, beanType, this.seen); 10 Iterator var6 = beans.iterator(); 11 12 while(var6.hasNext()) { 13 Entry<String, B> bean = (Entry)var6.next(); 14 if (this.seen.add(bean.getValue())) { 15 int order = this.getOrder(bean.getValue()); 16 String beanName = (String)bean.getKey(); 17 //Establish Servlet.class and Filter.class Package as RegistrationBean object 18 RegistrationBean registration = adapter.createRegistrationBean(beanName, bean.getValue(), beans.size()); 19 registration.setName(beanName); 20 registration.setOrder(order); 21 this.initializers.add(type, registration); 22 if (logger.isDebugEnabled()) { 23 logger.debug("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order=" + order + ", resource=" + this.getResourceDescription(beanName, beanFactory)); 24 } 25 } 26 } 27 28 }
We see that all Beans of Servlet.class and Filter.class types are obtained from the beanFactory first, and then Servlet.class and Filter.class are encapsulated as RegistrationBean using two adapters, Servlet RegistrationBeanAdapter and FilterRegistrationBeanAdapter.
private static class ServletRegistrationBeanAdapter implements ServletContextInitializerBeans.RegistrationBeanAdapter<Servlet> { private final MultipartConfigElement multipartConfig; ServletRegistrationBeanAdapter(MultipartConfigElement multipartConfig) { this.multipartConfig = multipartConfig; } public RegistrationBean createRegistrationBean(String name, Servlet source, int totalNumberOfSourceBeans) { String url = totalNumberOfSourceBeans == 1 ? "/" : "/" + name + "/"; if (name.equals("dispatcherServlet")) { url = "/"; } //Or will it Servlet.class Instance Encapsulation ServletRegistrationBean object //This and our own creation ServletRegistrationBean Objects are identical ServletRegistrationBean bean = new ServletRegistrationBean(source, new String[]{url}); bean.setMultipartConfig(this.multipartConfig); return bean; } } private static class FilterRegistrationBeanAdapter implements ServletContextInitializerBeans.RegistrationBeanAdapter<Filter> { private FilterRegistrationBeanAdapter() { } public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) { //Filter.class Instance Encapsulation FilterRegistrationBean object return new FilterRegistrationBean(source, new ServletRegistrationBean[0]); } }
The comments in the code make it clear whether the Servlet.class instance is encapsulated as a ServletRegistrationBean object, the Filter.class instance as a FilterRegistrationBean object, which is the same as the ServletRegistrationBean object we define ourselves. Now all Servlet RegistrationBeans, FilterRegistrationBean
Servlet.class, Filter.class are added to the set List <ServletContextInitializer> sortedList, and then the set is traversed to execute its onStartup method
onStartup method of ServletContextInitializer
ServletRegistrationBean
public class ServletRegistrationBean extends RegistrationBean { private static final Log logger = LogFactory.getLog(ServletRegistrationBean.class); private static final String[] DEFAULT_MAPPINGS = new String[]{"/*"}; private Servlet servlet; public void onStartup(ServletContext servletContext) throws ServletException { Assert.notNull(this.servlet, "Servlet must not be null"); String name = this.getServletName(); //call ServletContext Of addServlet Dynamic added = servletContext.addServlet(name, this.servlet); } //slightly... } private javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet, Map<String, String> initParams) throws IllegalStateException { if (servletName != null && !servletName.equals("")) { if (!this.context.getState().equals(LifecycleState.STARTING_PREP)) { throw new IllegalStateException(sm.getString("applicationContext.addServlet.ise", new Object[]{this.getContextPath()})); } else { Wrapper wrapper = (Wrapper)this.context.findChild(servletName); if (wrapper == null) { wrapper = this.context.createWrapper(); wrapper.setName(servletName); this.context.addChild(wrapper); } else if (wrapper.getName() != null && wrapper.getServletClass() != null) { if (!wrapper.isOverridable()) { return null; } wrapper.setOverridable(false); } if (servlet == null) { wrapper.setServletClass(servletClass); } else { wrapper.setServletClass(servlet.getClass().getName()); wrapper.setServlet(servlet); } if (initParams != null) { Iterator i$ = initParams.entrySet().iterator(); while(i$.hasNext()) { Entry<String, String> initParam = (Entry)i$.next(); wrapper.addInitParameter((String)initParam.getKey(), (String)initParam.getValue()); } } return this.context.dynamicServletAdded(wrapper); } } else { throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", new Object[]{servletName})); } }
See, onStartup in the ServletRegistrationBean first gets the name of the Servlet, then calls the addServlet of the ServletContext to add the Servlet to Tomcat so that we can send a request to the Servlet.
AbstractFilterRegistrationBean
public void onStartup(ServletContext servletContext) throws ServletException { Filter filter = this.getFilter(); Assert.notNull(filter, "Filter must not be null"); String name = this.getOrDeduceName(filter); //call ServletContext Of addFilter Dynamic added = servletContext.addFilter(name, filter); }
The same principle applies to AbstractFilterRegistrationBean, which first gets the target Filter, then calls the addFilter of the ServletContext to add the Filter to Tomcat so that the Filter can intercept our request.
DispatcherServletAutoConfiguration
DispatcherServletConfiguration
1 @Configuration 2 @ConditionalOnWebApplication 3 // Look first ClassPath Is there any below DispatcherServlet.class Byte Code 4 // We introduced spring-boot-starter-web,Also introduced tomcat and SpringMvc,Certainly there will be DispatcherServlet.class Byte Code 5 @ConditionalOnClass({DispatcherServlet.class}) 6 // This configuration class is executed in EmbeddedServletContainerAutoConfiguration Execute after configuration class takes effect 7 // After all, wait Tomcat Start before injection into it DispatcherServlet 8 @AutoConfigureAfter({EmbeddedServletContainerAutoConfiguration.class}) 9 protected static class DispatcherServletConfiguration { 10 public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; 11 public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration"; 12 @Autowired 13 private ServerProperties server; 14 15 @Autowired 16 private WebMvcProperties webMvcProperties; 17 18 @Autowired(required = false) 19 private MultipartConfigElement multipartConfig; 20 21 // Spring Container Registration DispatcherServlet 22 @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) 23 public DispatcherServlet dispatcherServlet() { 24 // Direct construction DispatcherServlet,And set WebMvcProperties Some configurations in 25 DispatcherServlet dispatcherServlet = new DispatcherServlet(); 26 dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest()); 27 dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest()); 28 dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound()); 29 return dispatcherServlet; 30 } 31 32 @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) 33 public ServletRegistrationBean dispatcherServletRegistration() { 34 // Direct use DispatcherServlet and server In Configuration servletPath Path construction ServletRegistrationBean 35 // ServletRegistrationBean Implemented ServletContextInitializer Interface, in onStartup Corresponding in the method Servlet Register with Servlet In container 36 // So here it is DispatcherServlet Will be registered Servlet In a container, the corresponding urlMapping by server.servletPath To configure 37 ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), this.server.getServletMapping()); 38 registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); 39 if (this.multipartConfig != null) { 40 registration.setMultipartConfig(this.multipartConfig); 41 } 42 return registration; 43 } 44 45 @Bean // Construction file upload related bean 46 @ConditionalOnBean(MultipartResolver.class) 47 @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) 48 public MultipartResolver multipartResolver(MultipartResolver resolver) { 49 return resolver; 50 } 51 52 }
To see if there is Dispatcher Servlet.class byte code under ClassPath, we introduced spring-boot-starter-web, tomcat and Spring Mvc. There will certainly be Dispatcher Servlet.class byte code. If spring-boot-starter-web is not imported, this configuration class will not take effect.
Then register the Dispatcher Servlet instance in the Spring container, then add the ServletRegistrationBean instance, and take the Dispatcher Servlet instance as a parameter. We have already learned the logic of ServletRegistrationBean above. When Tomcat starts, it takes all ServletRegistrationBeans, executes the onstartup method, and registers the Dispatcher Servlet with ServIn the let container, this is similar to the dispatcher Servlet configured in the original web.xml.
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
So as long as the starter spring-boot-starter-web is imported, SpringBoot has a Tomcat container and registers the Dispatcher Servlet object in the Tomcat container so that we can receive our request
Daily Praise
Well, everyone, that's all about this article. People who can see it are talented people.
If this article is well written and I feel I've learned something, ask for some compliment It's really useful for me!!!
Creation is not easy, your support and approval is the greatest power of my creation, we will see in the next article!