我們知道,SpringMVC結合Spring最重要的兩個文件是web.xml和Spring.xml,但是我們通過了註解的方式取消了這兩個文件,這個通過什麼方式解決這些繁瑣的配置文件的呢?首先我們來看web.xml這個文件是如何替換的呢,這個是由於Tomcat啓動應用的時候,首先回去找META-INF/services目錄下的javax.servletContainerInitializer文件,這個文件中存放着實現了ServletContainerInitializer接口的類,然後調用裏面的onStartup方法
public void onStartup(Set<Class<?>> set, ServletContext servletContext)
這個方法有兩個入參,其中這個set集合中的class類型是通過這個類上面的註解
@HandlesTypes(WebApplicationInitializer.class)
中的類型。當Tomcat啓動的時候就會去調用這個類中onStartup()方法了,所以我們可以想象到在這個onStartup()方法中就會完成原web.xml配置文件所實現的功能。而web.xml中有兩個非常重要的元素:一個是Listener元素
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
另一個是DispatcheServlet元素
<servlet>
<servlet-name>spring-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--springmvc的配置文件-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-dispatcher.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
現在我們的關注點就是如何將這兩個元素的實例化。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
//此處的webAppInitializerClasses這個參數爲項目中實現了WebApplicationInitializer接口的類
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
//通過反射構造對象並且加入到集合中
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
//將集合中的對象排序
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
//循環調用WebApplicationInitializer類型對象中的onStartup方法(鉤子方法)
//AbstractDispatcherServletInitializer這個中的onStartup方法完成Listener和Dispatcher兩種對象的註冊
initializer.onStartup(servletContext);
}
}
}
主要是通過,獲取到項目中實現了WebApplicationInitializer接口的方法,然後通過循環調用這個接口中的onStartup將Listener和Dispatcher這兩個對象進行註冊。AbstraceDispatcherServletInitializer這個中的onStartup方法完成的。這個抽象類中的onStartup方法先是註冊Listener對象,即調用registerContextLoaderListener方法,這個方法首先創建Spring的上下文對象。這個上下文對象具體是如何創建的呢?它會調用到createRootApplicationContext方法,這個是個抽象方法,是個鉤子方法,它會調用到子類的方法(AbstractAnnotationConfigDispatcherServletInitializer這個子類,它也是個抽象類)。
protected WebApplicationContext createRootApplicationContext() {
//鉤子方法,調用自己實現的AbstractAnnotationConfigDispatcherServletInitializer中的方法
//主要就是將有這個@ComponentScan註解類型的類加載進來
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
//創建註解上下文環境
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
}
else {
return null;
}
}
主要是通過鉤子方法將創建Spring註解的上下文環境,然後將包含了@ComponentScan註解的類注測到Spring中。接着我們就獲取到了Spring的上下文環境,接着我們就是創建監聽器(Listener)了,至此我們就完成了一個重要對象Listener的創建了。下一個就是Dispatcher對象的創建了。回到我們的AbstractDispatcherServletInitializer對象中的onStartup方法中來,通過registerDispatcherServlet方法我們要完成Dispatcher對象的註冊。
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
//創建springmvc的上下文,註冊了MvcContainer類,主要是將包含了@ComponentScan註解的類註冊到上下文中
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
//創建DispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
/*
* 如果該元素的值爲負數或者沒有設置,則容器會當Servlet被請求時再加載。
如果值爲正整數或者0時,表示容器在應用啓動時就加載並初始化這個servlet,
值越小,servlet的優先級越高,就越先被加載
* */
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
//鉤子方法,調用到自己實現的方法
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
//註冊過濾器到servlet上下文環境中來
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
至此兩個對象就註冊好了,同時對應的上下文環境也初始化完成了。