2023-03-15
原文作者:java-chen-hao 原文地址:https://www.cnblogs.com/java-chen-hao/p/11829056.html

上一篇我们讲了SpringBoot中Tomcat的启动过程,本篇我们接着讲在SpringBoot中如何向Tomcat中添加Servlet、Filter、Listener

自定义Servlet、Filter、Listener

Spring容器中声明ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean

     @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");
        }
    }

先自定义一个 Servlet,重写 service实现自己的业务逻辑,然后通过@Bean注解往Spring容器中注入一个 ServletRegistrationBean类型的bean实例,并且实例化一个自定义的Servlet作为参数,这样就将自定义的Servlet加入Tomcat中了。

@ServletComponentScan注解和@WebServlet、@WebFilter以及@WebListener注解配合使用

@ServletComponentScan注解启用ImportServletComponentScanRegistrar类,是个ImportBeanDefinitionRegistrar接口的实现类,会被Spring容器所解析。ServletComponentScanRegistrar内部会解析@ServletComponentScan注解,然后会在Spring容器中注册ServletComponentRegisteringPostProcessor,是个BeanFactoryPostProcessor,会去解析扫描出来的类是不是有@WebServlet、@WebListener、@WebFilter这3种注解,有的话把这3种类型的类转换成ServletRegistrationBean、FilterRegistrationBean或者ServletListenerRegistrationBean,然后让Spring容器去解析:

    @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");
        }
    }

在Spring容器中声明Servlet、Filter或者Listener

     @Bean(name = "dispatcherServlet")
     public DispatcherServlet myDispatcherServlet() {
         return new   DispatcherServlet(); 
    }

我们发现往Tomcat中添加Servlet、Filter或者Listener还是挺容易的,大家还记得以前SpringMVC是怎么配置 DispatcherServlet 的吗?在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>

和我们SpringBoot中配置Servlet相比是不是复杂很多,虽然SpringBoot中自定义Servlet很简单,但是其底层却不简单,下面我们来分析一下其原理

ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean

我们来看看这几个特殊的类:

ServletRegistrationBean

    public class ServletRegistrationBean extends RegistrationBean {
         //存放目标Servlet实例
        private Servlet servlet;
        //存放Servlet的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);
                }
            }
        }
        
        //略
    }

在我们例子中我们通过return new ServletRegistrationBean(new CustomServlet(), "/custom");就知道,ServletRegistrationBean里会存放目标Servlet实例和urlMapping,并且继承RegistrationBean这个类

FilterRegistrationBean

    public class FilterRegistrationBean extends  AbstractFilterRegistrationBean  {
         //存放目标Filter对象
        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();
        //重写onStartup方法
        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);
                }
            }
        }
        //略...
    }

我们看到FilterRegistrationBean 中也保存了 目标Filter对象,并且继承了 RegistrationBean

ServletListenerRegistrationBean

    public class ServletListenerRegistrationBean<T extends EventListener> extends RegistrationBean {
        //存放了目标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);
                }
            }
        }
        //略...
    }

ServletListenerRegistrationBean也是一样,那我们来看看RegistrationBean这个类

    public abstract class  RegistrationBean implements   ServletContextInitializer , Ordered {
        ...
    }
    public interface  ServletContextInitializer  {
         void onStartup(ServletContext var1) throws   ServletException; 
    }

我们发现 RegistrationBean 实现了 ServletContextInitializer这个接口,并且有一个onStartup方法, ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean都实现了 onStartup方法。

ServletContextInitializer是 Servlet 容器初始化的时候,提供的初始化接口。所以,Servlet 容器初始化会获取并触发所有的FilterRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean 实例中 onStartup方法

那到底是何时触发这些类的onStartup方法呢?

202303152210429971.png

当Tomcat容器启动时,会执行callInitializers,然后获取所有的 ServletContextInitializer,循环执行 onStartup方法触发回调方法。那FilterRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean实例是何时加入到Initializers集合的呢?这要回顾一下我们上一篇文章Tomcat的启动过程

Servlet容器的启动

大家可以看看我上一篇文章,我这里简单的复制一下代码

EmbeddedWebApplicationContext

     1 @Override
     2 protected void onRefresh() {
     3     super.onRefresh();
     4     try {
     5         //核心方法:会获取嵌入式的Servlet容器工厂,并通过工厂来获取Servlet容器
     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         //先获取嵌入式Servlet容器工厂
    18         EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
    19          //根据容器工厂来获取对应的嵌入式Servlet容器
    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 }

关键代码在第20行,先通过getSelfInitializer()获取到所有的Initializer,传入Servlet容器中,那核心就在getSelfInitializer()方法:

    1 private ServletContextInitializer getSelfInitializer() {
    2     //只是创建了一个ServletContextInitializer实例返回
    3     //所以Servlet容器启动的时候,会调用这个对象的onStartup方法
    4     return new  ServletContextInitializer () {
    5          public void onStartup(ServletContext servletContext) throws ServletException {
    6             EmbeddedWebApplicationContext.this.selfInitialize(servletContext);
    7         }
     8     };
    9 }

我们看到只是创建了一个ServletContextInitializer实例返回,所以Servlet容器启动的时候,会调用这个对象的onStartup方法,那我们来分析其onStartup中的逻辑,也就是selfInitialize方法,并将Servlet上下文对象传进去了

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      //这里便是获取所有的 ServletContextInitializer 实现类,会获取所有的注册组件
     9     for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
    10         //执行所有ServletContextInitializer的onStartup方法
    11         beans.onStartup(servletContext);
    12     }
     13 }

关键代码在第9和第11行,先获取所有的ServletContextInitializer 实现类,然后遍历执行所有ServletContextInitializer的onStartup方法

获取所有的ServletContextInitializer

我们来看看getServletContextInitializerBeans方法

    protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
        return new  ServletContextInitializerBeans(getBeanFactory()) ;
    }

ServletContextInitializerBeans对象是对ServletContextInitializer的一种包装:

     1 public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
     2     private final MultiValueMap<Class<?>, ServletContextInitializer> initializers = new LinkedMultiValueMap();
      3     //存放所有的ServletContextInitializer
     4     private List<ServletContextInitializer> sortedList;
      5 
     6      public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
     7         //执行addServletContextInitializerBeans
     8         this.addServletContextInitializerBeans(beanFactory);
     9         //执行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         //返回所有的ServletContextInitializer
    80         return this.sortedList.iterator();
    81     }
    82 
    83     //略...
    84 }

我们看到ServletContextInitializerBeans 中有一个存放所有ServletContextInitializer的集合sortedList,就是在其构造方法中获取所有的ServletContextInitializer,并放入sortedList集合中,那我们来看看其构造方法的逻辑,看到第8行先调用

addServletContextInitializerBeans方法:  

    1 private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
    2     //从Spring容器中获取所有ServletContextInitializer.class 类型的Bean
    3     for (Entry<String, ServletContextInitializer> initializerBean :  getOrderedBeansOfType(beanFactory, ServletContextInitializer.class  ) ) {
    4          //添加到具体的集合中
    5         addServletContextInitializerBean(initializerBean.getKey(),initializerBean.getValue(), beanFactory);
     6     }
    7 }

我们看到先从Spring容器中获取所有 ServletContextInitializer.class 类型的Bean,这里我们自定义的ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean就被获取到了,然后调用addServletContextInitializerBean方法:

     1 private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
     2     //判断ServletRegistrationBean类型
     3      if (initializer instanceof ServletRegistrationBean) {
      4         Servlet source = ((ServletRegistrationBean)initializer).getServlet();
     5          //将ServletRegistrationBean加入到集合中
     6         this.addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
      7     //判断FilterRegistrationBean类型
     8      } else if (initializer instanceof FilterRegistrationBean) {
      9         Filter source = ((FilterRegistrationBean)initializer).getFilter();
    10          //将ServletRegistrationBean加入到集合中
    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     //加入到initializers中
    27     this.initializers.add(type, initializer);
    28 }

很明显,判断从Spring容器中获取的ServletContextInitializer类型,如ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean,并加入到initializers集合中去,我们再来看构造器中的另外一个方法 addAdaptableBeans(beanFactory):

     1 private void addAdaptableBeans(ListableBeanFactory beanFactory) {
     2      //从beanFactory获取所有Servlet.class和Filter.class类型的Bean,并封装成RegistrationBean对象,加入到集合中 
     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      //从Spring容器中获取所有的Servlet.class和Filter.class类型的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              //创建Servlet.class和Filter.class包装成RegistrationBean对象
    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 }

我们看到先从beanFactory获取所有Servlet.class和Filter.class类型的Bean,然后通过 ServletRegistrationBeanAdapter和 FilterRegistrationBeanAdapter两个适配器将Servlet.class和Filter.class封装成 RegistrationBean

    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 = "/";
            }
             //还是将Servlet.class实例封装成ServletRegistrationBean对象
            //这和我们自己创建ServletRegistrationBean对象是一模一样的
            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实例封装成FilterRegistrationBean对象
            return new FilterRegistrationBean(source, new ServletRegistrationBean[0  ]); 
        }
    }

代码中注释很清楚了还是将Servlet.class实例封装成ServletRegistrationBean对象,将Filter.class实例封装成FilterRegistrationBean对象,这和我们自己定义ServletRegistrationBean对象是一模一样的,现在所有的ServletRegistrationBean、FilterRegistrationBean

Servlet.class、Filter.class都添加到List sortedList这个集合中去了,接着就是遍历这个集合,执行其 onStartup 方法了

202303152210439222.png

ServletContextInitializer的 onStartup 方法

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();
            //调用ServletContext的addServlet
            Dynamic added = servletContext.addServlet(name, this.servlet);
        }
        
        //略...
    }
    
    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}));
        }
    }

看到没,ServletRegistrationBean 中的 onStartup先获取Servlet的name,然后调用ServletContext的addServlet将Servlet加入到Tomcat中,这样我们就能发请求给这个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);
        //调用ServletContext的addFilter
        Dynamic added =   servletContext.addFilter(name, filter); 
    }

AbstractFilterRegistrationBean也是同样的原理,先获取目标Filter,然后调用ServletContext的 addFilter 将Filter加入到Tomcat中,这样Filter就能拦截我们请求了。

DispatcherServletAutoConfiguration

最熟悉的莫过于,在Spring Boot在自动配置SpringMVC的时候,会自动注册SpringMVC前端控制器: DispatcherServlet ,该控制器主要在 DispatcherServletAutoConfiguration 自动配置类中进行注册的。DispatcherServlet是SpringMVC中的核心分发器。DispatcherServletAutoConfiguration也在spring.factories中配置了

202303152210445363.png

DispatcherServletConfiguration

     1 @Configuration
     2 @ConditionalOnWebApplication
     3  // 先看下ClassPath下是否有DispatcherServlet.class字节码
     4 // 我们引入了spring-boot-starter-web,同时引入了tomcat和SpringMvc,肯定会存在DispatcherServlet.class字节码
     5 @ConditionalOnClass({DispatcherServlet.class})
      6  // 这个配置类的执行要在EmbeddedServletContainerAutoConfiguration配置类生效之后执行
     7 // 毕竟要等Tomcat启动后才能往其中注入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容器注册DispatcherServlet
    22   @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
     23   public DispatcherServlet dispatcherServlet() {
    24      // 直接构造DispatcherServlet,并设置WebMvcProperties中的一些配置
    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      // 直接使用DispatcherServlet和server配置中的servletPath路径构造ServletRegistrationBean
    35     // ServletRegistrationBean实现了ServletContextInitializer接口,在onStartup方法中对应的Servlet注册到Servlet容器中
    36     // 所以这里DispatcherServlet会被注册到Servlet容器中,对应的urlMapping为server.servletPath配置
    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 // 构造文件上传相关的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 }

先看下ClassPath下是否有DispatcherServlet.class字节码, 我们引入了spring-boot-starter-web,同时引入了tomcat和SpringMvc,肯定会存在DispatcherServlet.class字节码,如果没有导入spring-boot-starter-web,则这个配置类将不会生效

然后往Spring容器中注册DispatcherServlet实例,接着又加入ServletRegistrationBean实例,并把DispatcherServlet实例作为参数,上面我们已经学过了ServletRegistrationBean的逻辑,在Tomcat启动的时候,会获取所有的ServletRegistrationBean,并执行其中的onstartup方法,将DispatcherServlet注册到Servlet容器中,这样就类似原来的web.xml中配置的dispatcherServlet。

    <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>

所以只要导入了spring-boot-starter-web这个starter,SpringBoot就有了Tomcat容器,并且往Tomcat容器中注册了DispatcherServlet对象,这样就能接收到我们的请求了

阅读全文