我们知道,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);
}
至此两个对象就注册好了,同时对应的上下文环境也初始化完成了。