1. 程式人生 > >SpringBoot 原始碼解析 (七)----- Spring Boot的核心能力 - SpringBoot如何實現SpringMvc的?

SpringBoot 原始碼解析 (七)----- Spring Boot的核心能力 - SpringBoot如何實現SpringMvc的?

上一篇我們講了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方法呢?

當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<ServletContextInitializer> sortedList這個集合中去了,接著就是遍歷這個集合,執行其onStartup方法了

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中配置了

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物件,這樣就能接收到我們的請求了

日常求贊

好了各位,以上就是這篇文章的全部內容了,能看到這裡的人呀,都是人才。

如果這個文章寫得還不錯,覺得學到了一點東西的話 求點贊