Spring之架构(一)启动入口

一、Spring配置启动类

 1 package config;
 2 
 3 import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
 4 
 5 /**
 6  * Servlet3.0环境下,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类;
 7  * Spring提供了这个接口的实现,名为SpringServletContainerInitializer;
 8  * 而这个类返回来又会去查找实现WebApplicationInitializer的类并将配置任务交给他们来实现。
 9  * Spring3.2引入了一个便利的WebApplicationInitializer基础实现,也就是AbstractAnnotationConfigDispatcherServletInitializer;
10  * 我们自己建的SpitterWebInitializer继承了AbstractAnnotationConfigDispatcherServletInitializer;
11  * 因此当部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文;
12  */
13 public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
14 
15     /** 将DispatcherServlet映射到"/",这表示他会是应用默认的Servlet,他会处理进入应用的所有请求. */
16     @Override
17     protected String[] getServletMappings() {
18         return new String[] { "/" };
19     }
20 
21     /**
22      * DispatcherServlet启动时,他会创建Spring应用上下文,并加载配置文件或者配置类中的bean;
23      * 我么要求DispatcherServlet启动时,会加载WebConfig中的bean
24      */
25     @Override
26     protected Class<?>[] getServletConfigClasses() {
27         return new Class<?>[] { WebConfig.class };
28     }
29 
30     /**
31      * 但是在Spring Web中,通常还有另一个上下文应用。它是由ContextLoaderListener创建的;
32      * DispatcherServlet加载包含Web组件的bean如controller,视图解析器,处理映射器;
33      * ContextLoaderListener要加载应用中的其他的bean,通常是驱动后端的中间层和数据层组件;
34      * 比如没有扫描到service层时,controller层bean的依赖注入就会在ContextLoaderListener加载时报错;
35      */
36     @Override
37     protected Class<?>[] getRootConfigClasses() {
38         return new Class<?>[] { RootConfig.class };
39     }
40 
41 }
SpitterWebInitializer

 通过intellij idea查看继承树:

----------------------------------------------------------------

我们发现树顶是个interface,Classname: WebApplicationInitializer

 1 public interface WebApplicationInitializer {
 2 
 3     /**
 4      * Configure the given {@link ServletContext} with any servlets, filters, listeners
 5      * context-params and attributes necessary for initializing this web application. See
 6      * examples {@linkplain WebApplicationInitializer above}.
 7      * @param servletContext the {@code ServletContext} to initialize
 8      * @throws ServletException if any call against the given {@code ServletContext}
 9      * throws a {@code ServletException}
10      */
11     void onStartup(ServletContext servletContext) throws ServletException;
12 
13 }
WebApplicationInitializer

 WebApplicationInitializer接口只有一个void onStartup(ServletContext servletContext)抽象方法,顾名思义就是启动web容器。

----------------------------------------------------------------------------------------

我再来看AbstractContextLoaderInitializer是如何实现这个WebApplicationInitializer接口的:

 1 public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
 2 
 3     /** Logger available to subclasses */
 4     protected final Log logger = LogFactory.getLog(getClass());
 5 
 6 
 7     @Override
 8     public void onStartup(ServletContext servletContext) throws ServletException {
 9         registerContextLoaderListener(servletContext);
10     }
11 
12     /**
13      * Register a {@link ContextLoaderListener} against the given servlet context. The
14      * {@code ContextLoaderListener} is initialized with the application context returned
15      * from the {@link #createRootApplicationContext()} template method.
16      * @param servletContext the servlet context to register the listener against
17      */
18     protected void registerContextLoaderListener(ServletContext servletContext) {
19         WebApplicationContext rootAppContext = createRootApplicationContext();
20         if (rootAppContext != null) {
21             ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
22             listener.setContextInitializers(getRootApplicationContextInitializers());
23             servletContext.addListener(listener);
24         }
25         else {
26             logger.debug("No ContextLoaderListener registered, as " +
27                     "createRootApplicationContext() did not return an application context");
28         }
29     }
30 
31     /**
32      * Create the "<strong>root</strong>" application context to be provided to the
33      * {@code ContextLoaderListener}.
34      * <p>The returned context is delegated to
35      * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will
36      * be established as the parent context for any {@code DispatcherServlet} application
37      * contexts. As such, it typically contains middle-tier services, data sources, etc.
38      * @return the root application context, or {@code null} if a root context is not
39      * desired
40      * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
41      */
42     protected abstract WebApplicationContext createRootApplicationContext();
43 
44     /**
45      * Specify application context initializers to be applied to the root application
46      * context that the {@code ContextLoaderListener} is being created with.
47      * @since 4.2
48      * @see #createRootApplicationContext()
49      * @see ContextLoaderListener#setContextInitializers
50      */
51     protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
52         return null;
53     }
54 
55 }
AbstractContextLoaderInitializer

 AbstractContextLoaderInitializer重写了onStartup并且追加了2个抽象方法和一个具体方法

AbstractContextLoaderInitializer调用onStartup方法时,直接调用registerContextLoaderListener方法,

而registerContextLoaderListener先是调用了createRootApplicationContext返回一个WebApplicationContext rootAppContext,

registerContextLoaderListener然后创建ContextLoaderListener listener = new ContextLoaderListener(rootAppContext)对象;

通过listener的setContextInitializers方法,又调用了本类另一个抽象方法getRootApplicationContextInitializers().

最后通过addListener方法把listener注册到servletContext。

----------------------------------------------------------------------------------------

我们再来看继承树的下一个AbstractDispatcherServletInitializer

  1 public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
  2 
  3     /**
  4      * The default servlet name. Can be customized by overriding {@link #getServletName}.
  5      */
  6     public static final String DEFAULT_SERVLET_NAME = "dispatcher";
  7 
  8 
  9     @Override
 10     public void onStartup(ServletContext servletContext) throws ServletException {
 11         super.onStartup(servletContext);
 12         registerDispatcherServlet(servletContext);
 13     }
 14 
 15     /**
 16      * Register a {@link DispatcherServlet} against the given servlet context.
 17      * <p>This method will create a {@code DispatcherServlet} with the name returned by
 18      * {@link #getServletName()}, initializing it with the application context returned
 19      * from {@link #createServletApplicationContext()}, and mapping it to the patterns
 20      * returned from {@link #getServletMappings()}.
 21      * <p>Further customization can be achieved by overriding {@link
 22      * #customizeRegistration(ServletRegistration.Dynamic)} or
 23      * {@link #createDispatcherServlet(WebApplicationContext)}.
 24      * @param servletContext the context to register the servlet against
 25      */
 26     protected void registerDispatcherServlet(ServletContext servletContext) {
 27         String servletName = getServletName();
 28         Assert.hasLength(servletName, "getServletName() must not return empty or null");
 29 
 30         WebApplicationContext servletAppContext = createServletApplicationContext();
 31         Assert.notNull(servletAppContext,
 32                 "createServletApplicationContext() did not return an application " +
 33                 "context for servlet [" + servletName + "]");
 34 
 35         FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
 36         dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
 37 
 38         ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
 39         Assert.notNull(registration,
 40                 "Failed to register servlet with name '" + servletName + "'." +
 41                 "Check if there is another servlet registered under the same name.");
 42 
 43         registration.setLoadOnStartup(1);
 44         registration.addMapping(getServletMappings());
 45         registration.setAsyncSupported(isAsyncSupported());
 46 
 47         Filter[] filters = getServletFilters();
 48         if (!ObjectUtils.isEmpty(filters)) {
 49             for (Filter filter : filters) {
 50                 registerServletFilter(servletContext, filter);
 51             }
 52         }
 53 
 54         customizeRegistration(registration);
 55     }
 56 
 57     /**
 58      * Return the name under which the {@link DispatcherServlet} will be registered.
 59      * Defaults to {@link #DEFAULT_SERVLET_NAME}.
 60      * @see #registerDispatcherServlet(ServletContext)
 61      */
 62     protected String getServletName() {
 63         return DEFAULT_SERVLET_NAME;
 64     }
 65 
 66     /**
 67      * Create a servlet application context to be provided to the {@code DispatcherServlet}.
 68      * <p>The returned context is delegated to Spring's
 69      * {@link DispatcherServlet#DispatcherServlet(WebApplicationContext)}. As such,
 70      * it typically contains controllers, view resolvers, locale resolvers, and other
 71      * web-related beans.
 72      * @see #registerDispatcherServlet(ServletContext)
 73      */
 74     protected abstract WebApplicationContext createServletApplicationContext();
 75 
 76     /**
 77      * Create a {@link DispatcherServlet} (or other kind of {@link FrameworkServlet}-derived
 78      * dispatcher) with the specified {@link WebApplicationContext}.
 79      * <p>Note: This allows for any {@link FrameworkServlet} subclass as of 4.2.3.
 80      * Previously, it insisted on returning a {@link DispatcherServlet} or subclass thereof.
 81      */
 82     protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
 83         return new DispatcherServlet(servletAppContext);
 84     }
 85 
 86     /**
 87      * Specify application context initializers to be applied to the servlet-specific
 88      * application context that the {@code DispatcherServlet} is being created with.
 89      * @since 4.2
 90      * @see #createServletApplicationContext()
 91      * @see DispatcherServlet#setContextInitializers
 92      * @see #getRootApplicationContextInitializers()
 93      */
 94     protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() {
 95         return null;
 96     }
 97 
 98     /**
 99      * Specify the servlet mapping(s) for the {@code DispatcherServlet} &mdash;
100      * for example {@code "/"}, {@code "/app"}, etc.
101      * @see #registerDispatcherServlet(ServletContext)
102      */
103     protected abstract String[] getServletMappings();
104 
105     /**
106      * Specify filters to add and map to the {@code DispatcherServlet}.
107      * @return an array of filters or {@code null}
108      * @see #registerServletFilter(ServletContext, Filter)
109      */
110     protected Filter[] getServletFilters() {
111         return null;
112     }
113 
114     /**
115      * Add the given filter to the ServletContext and map it to the
116      * {@code DispatcherServlet} as follows:
117      * <ul>
118      * <li>a default filter name is chosen based on its concrete type
119      * <li>the {@code asyncSupported} flag is set depending on the
120      * return value of {@link #isAsyncSupported() asyncSupported}
121      * <li>a filter mapping is created with dispatcher types {@code REQUEST},
122      * {@code FORWARD}, {@code INCLUDE}, and conditionally {@code ASYNC} depending
123      * on the return value of {@link #isAsyncSupported() asyncSupported}
124      * </ul>
125      * <p>If the above defaults are not suitable or insufficient, override this
126      * method and register filters directly with the {@code ServletContext}.
127      * @param servletContext the servlet context to register filters with
128      * @param filter the filter to be registered
129      * @return the filter registration
130      */
131     protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) {
132         String filterName = Conventions.getVariableName(filter);
133         Dynamic registration = servletContext.addFilter(filterName, filter);
134         if (registration == null) {
135             int counter = -1;
136             while (counter == -1 || registration == null) {
137                 counter++;
138                 registration = servletContext.addFilter(filterName + "#" + counter, filter);
139                 Assert.isTrue(counter < 100,
140                         "Failed to register filter '" + filter + "'." +
141                         "Could the same Filter instance have been registered already?");
142             }
143         }
144         registration.setAsyncSupported(isAsyncSupported());
145         registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName());
146         return registration;
147     }
148 
149     private EnumSet<DispatcherType> getDispatcherTypes() {
150         return (isAsyncSupported() ?
151                 EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) :
152                 EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
153     }
154 
155     /**
156      * A single place to control the {@code asyncSupported} flag for the
157      * {@code DispatcherServlet} and all filters added via {@link #getServletFilters()}.
158      * <p>The default value is "true".
159      */
160     protected boolean isAsyncSupported() {
161         return true;
162     }
163 
164     /**
165      * Optionally perform further registration customization once
166      * {@link #registerDispatcherServlet(ServletContext)} has completed.
167      * @param registration the {@code DispatcherServlet} registration to be customized
168      * @see #registerDispatcherServlet(ServletContext)
169      */
170     protected void customizeRegistration(ServletRegistration.Dynamic registration) {
171     }
172 
173 }
AbstractDispatcherServletInitializer

抽象类AbstractDispatcherServletInitializer也重写了onStartup,先是通过super.onStartup(servletContext)完成父类servletContext的初始化,

然后调用registerDispatcherServlet(servletContext)方法来注册DispatcherServlet;

具体流程:String servletName = getServletName()来获取servletName,获取一个WebApplicationContext servletAppContext对象,

FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);来建一个dispatcherServlet

而protected abstract String[] getServletMappings()抽象了servlet映射的路径;

 

---------------------------------------------------------------------------------------

我们再来看看继承树的下一个AbstractAnnotationConfigDispatcherServletInitializer抽象类

 1 public abstract class AbstractAnnotationConfigDispatcherServletInitializer
 2         extends AbstractDispatcherServletInitializer {
 3 
 4     /**
 5      * {@inheritDoc}
 6      * <p>This implementation creates an {@link AnnotationConfigWebApplicationContext},
 7      * providing it the annotated classes returned by {@link #getRootConfigClasses()}.
 8      * Returns {@code null} if {@link #getRootConfigClasses()} returns {@code null}.
 9      */
10     @Override
11     protected WebApplicationContext createRootApplicationContext() {
12         Class<?>[] configClasses = getRootConfigClasses();
13         if (!ObjectUtils.isEmpty(configClasses)) {
14             AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
15             rootAppContext.register(configClasses);
16             return rootAppContext;
17         }
18         else {
19             return null;
20         }
21     }
22 
23     /**
24      * {@inheritDoc}
25      * <p>This implementation creates an {@link AnnotationConfigWebApplicationContext},
26      * providing it the annotated classes returned by {@link #getServletConfigClasses()}.
27      */
28     @Override
29     protected WebApplicationContext createServletApplicationContext() {
30         AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
31         Class<?>[] configClasses = getServletConfigClasses();
32         if (!ObjectUtils.isEmpty(configClasses)) {
33             servletAppContext.register(configClasses);
34         }
35         return servletAppContext;
36     }
37 
38     /**
39      * Specify {@link org.springframework.context.annotation.Configuration @Configuration}
40      * and/or {@link org.springframework.stereotype.Component @Component} classes to be
41      * provided to the {@linkplain #createRootApplicationContext() root application context}.
42      * @return the configuration classes for the root application context, or {@code null}
43      * if creation and registration of a root context is not desired
44      */
45     protected abstract Class<?>[] getRootConfigClasses();
46 
47     /**
48      * Specify {@link org.springframework.context.annotation.Configuration @Configuration}
49      * and/or {@link org.springframework.stereotype.Component @Component} classes to be
50      * provided to the {@linkplain #createServletApplicationContext() dispatcher servlet
51      * application context}.
52      * @return the configuration classes for the dispatcher servlet application context or
53      * {@code null} if all configuration is specified through root config classes.
54      */
55     protected abstract Class<?>[] getServletConfigClasses();
56 
57 }
AbstractAnnotationConfigDispatcherServletInitializer

AbstractAnnotationConfigDispatcherServletInitializer创建了2个WebApplicationContext,

rootAppContext和servletAppContext

servletAppContext将getServletConfigClasses()方法返回的Class进行注册

rootAppContext将getRootConfigClasses()方法返回的Class进行注册

----------------------------------------------------------------------------------

最后作为SpitterWebInitializer指定两个上下文AppContext需要注册Class,并且实现servlet映射的路径即可。

 由于继承了父类的各种方法,当servlet3.0环境以上时,tomcat启动时会寻找实现AbstractAnnotationConfigDispatcherServletInitializer

的SpitterWebInitializer,并调用onstart方法进行不断的方法调用直到把上下文和映射路径加载完毕,即完成了启动。

posted on 2017-11-19 13:00  剑姬  阅读(5162)  评论(0编辑  收藏  举报

导航