SpringBoot Source Parsing----- Spring Boot Core Competence - How does SpringBoot implement SpringMvc?

Posted by TheChief on Fri, 22 Nov 2019 04:40:43 +0100

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

The most familiar thing is that Spring MVC front-end controllers are automatically registered when Spring Boot automatically configures Spring MVC: Dispatcher Servlet, which is registered mainly in the Dispatcher Servlet AutoConfiguration auto-configuration class.Dispatcher Servlet is the core distributor in Spring MVC.Dispatcher ServletAutoConfiguration is also configured in spring.factories

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!

 
 

Topics: Java Spring Tomcat SpringBoot xml