SpringBoot的底层入口SpringServletContainerInitializer

SpringServletContainerInitializer

参考之前的博文无配置文件SSM
那么容器启动时候,为什么这么MyWebAppInitializer这个类会被加载呢,进而创建根容器,创建web的ioc容器呢
来看这个类的介绍:

WebApplicationInitializer是Spring MVC提供的一个接口,它确保检测到您的实现并自动用于初始化Servlet 3容器。WebApplicationInitializer的抽象基类实现AbstractDispatcherServletInitializer通过重写方法来指定servlet映射和DispatcherServlet配置的位置,使得注册DispatcherServlet更加容易。

这里提到了初始化servlet3.0容器,那就有必要了解一下sevrvlet3.0的一个初始化规范,根据官方文档的解释,用自己的话总结一下有关的重要几点,大概意思就是:

web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer,加载这个文件制定的类,并且可以通过@HandlesTypes注解,把感兴趣的类的信息,注入到onStartup(Set<Class<?>> var1, ServletContext var2)var1当中。

经过查看源码,以及debug调试发现,这个ServletContainerInitializer其实就是SpringServletContainerInitializer,来看SpringServletContainerInitializer所在在包下的目录结构。
在这里插入图片描述
根据上面介绍的servlet3.0规范,当servlet容器启动的时候,就会加载javax.servlet.ServletContainerInitializer文件中指定的类
其内容就是org.springframework.web.SpringServletContainerInitializer,所有也就会在容器启动的时候,加载SpringServletContainerInitializer
SpringServletContainerInitializer代码如下:

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web;

import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    
    
	@Override
	public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {
    
    

		List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();

		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) 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);
		//遍历容器中所有的WebApplicationInitializer#onStartup方法
		for (WebApplicationInitializer initializer : initializers) {
    
    
			initializer.onStartup(servletContext);
		}
	}

}

这段代码的简单的介绍一下:因为这个类上标注了@HandlesTypes(WebApplicationInitializer.class),所以会加载所有WebApplicationInitializer信息都会被注入到onStartup()方法的形参webAppInitializerClasses上,然后遍历,判断如果不是接口【!waiClass.isInterface()】,不是抽象类【!Modifier.isAbstract(waiClass.getModifiers()】,那么就实例化,并且添加在集合当中。最后遍历集合initializers,代用每一个对象的#onStartup(servletContext)方法。在这个遍历上,打上一个断点,看一下initializers中有哪些对象。

		for (WebApplicationInitializer initializer : initializers) {
    
    
			initializer.onStartup(servletContext);
		}

查看结果如下:(加载的感兴趣的类信息一共有4个,因为其他三个都是抽象类,不符合实例化的条件,所有集合中也就一个类,就是我们定义的MyWebAppInitializer
在这里插入图片描述
经过上面的分析,可以大概小结一下:
因为servlet3.0容器加载规范,会加载特定位置的文件中指定的类,在这里也就是SpringServletContainerInitializer,,然后加载@HandleType注解标注的感兴趣的类,然后根据条件实例化这些类,添加到集合中,遍历集合然后调用他们的onStartup方法


有了这些基础,那么就可以来看一下,他的执行调用过程,来看一下容器如何被创建的,核心控制器DispatcherServlet是如何被添加到容器中…
执行initializer.onStartup(servletContext),所以来到MyWebAppInitializer#onStartup的方法,因为他本身没有重写这个方法,所以往上找他的父类AbstractAnnotationConfigDispatcherServletInitializer,但是这里也没有那么就接着再找父类AbstractDispatcherServletInitializer,在这个类中onStartup方法代码如下:

@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
    
    
		super.onStartup(servletContext);
		registerDispatcherServlet(servletContext);
	}

他分为两步,先执行super.onStartup(servletContext),所以接着来到他的父类AbstractContextLoaderInitializer中这个方法的实现,

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
    
    
		registerContextLoaderListener(servletContext);
	}
	
		protected void registerContextLoaderListener(ServletContext servletContext) {
    
    
		//调用#createRootApplicationContext()方法,因为自己没有实现,调用子类的方法
		//执行AbstractAnnotationConfigDispatcherServletInitializer这个类的createRootApplicationContext()
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {
    
    
		//添加容器监听事件
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
			listener.setContextInitializers(getRootApplicationContextInitializers());
			servletContext.addListener(listener);
		}
		else {
    
    
			logger.debug("No ContextLoaderListener registered, as " +
					"createRootApplicationContext() did not return an application context");
		}
	}

进入AbstractAnnotationConfigDispatcherServletInitializer

	@Override
	protected WebApplicationContext createRootApplicationContext() {
    
    
	//getRootConfigClasses()方法,这个类自己没有实现,调用的其实是MyWebAppInitializer#getRootConfigClasses方法
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
    
    
			//创建一个根容器
			AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
			//把我们的配置类注册进去
			rootAppContext.register(configClasses);
			return rootAppContext;
		}
		else {
    
    
			return null;
		}
	}

执行到这里,super.onStartup(servletContext),方法执行完,根容器已经被创建出来了,接着调用registerDispatcherServlet(servletContext)这个方法,方法如下:

protected void registerDispatcherServlet(ServletContext servletContext) {
    
    
		//获得名字 默认是dispatcher
		String servletName = getServletName();
		Assert.hasLength(servletName, "getServletName() must not return empty or null");
		// 因为本类没有实现,调动子类AbstractAnnotationConfigDispatcherServletInitializer的
		//createServletApplicationContext方法,创建出web的ioc容器  这个方法的代码与createRootApplicationContext()的执行过程类似
		/*调用子类MyWebAppInitializer 的getServletConfigClasses方法,加载AppConfig.class 然后创建出一个容器。
		*/
		WebApplicationContext servletAppContext = createServletApplicationContext();
		Assert.notNull(servletAppContext,
				"createServletApplicationContext() did not return an application " +
				"context for servlet [" + servletName + "]");
		//其实就是new DispatcherServlet(servletAppContext),创建出一个DispatcherServlet
		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
		// 将创建出来的DispatcherServlet添加到容器中
		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
		Assert.notNull(registration,
				"Failed to register servlet with name '" + servletName + "'." +
				"Check if there is another servlet registered under the same name.");
		//容器加载,就创建
		registration.setLoadOnStartup(1);
		//设置拦截的mapping  回调子类的getServletMappings()方法 也就是MyWebAppInitializer#getServletMappings方法
		registration.addMapping(getServletMappings());
		//设置异步支持 默认是true
		registration.setAsyncSupported(isAsyncSupported());
		//添加过滤器,可以通过重写ServletFilters方法 ,来添加过滤器
		Filter[] filters = get	ServletFilters();
		if (!ObjectUtils.isEmpty(filters)) {
    
    
			for (Filter filter : filters) {
    
    
				registerServletFilter(servletContext, filter);
			}
		}

		customizeRegistration(registration);
	}

执行到这里,作用是创建出web的ioc容器,并且创建出DispatcherServlet,设置了他的启动机制,设置了ServletMapping,所有就不需要的web.xml配置,就能成功启动容器。
(其实根容器和web容器其实是一个父子关系),每一个容器,装载一些特定的组件。
在这里插入图片描述

DispatcherServlet需要一个WebApplicationContext(一个普通ApplicationContext的扩展)来进行自己的配置。WebApplicationContext有一个指向它关联的ServletContext和Servlet的链接。它还绑定到ServletContext,以便应用程序可以在requestcontext tutils上使用静态方法来查找需要访问的WebApplicationContext。
对于许多只有一个WebApplicationContext的应用程序来说,这是简单而充分的。还可以有一个上下文层次结构,其中一个根WebApplicationContext在多个DispatcherServlet(或其他Servlet)实例之间共享,每个实例都有自己的子WebApplicationContext配置。有关上下文层次结构特性的更多信息,
根WebApplicationContext通常包含基础设施bean,比如需要跨多个Servlet实例共享的数据存储库和业务服务。这些bean是有效继承的,可以在特定于Servlet的子WebApplicationContext中重写(即重新声明),该上下文通常包含给定Servlet的本地

总结一下,画了一个uml时序图:
在这里插入图片描述

  • web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
  • spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
  • 并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)

猜你喜欢

转载自blog.csdn.net/weixin_43732955/article/details/114736453