Servlet3.0规则
1、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例:
2、ServletContainerInitializer的实例放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名
文件内容如下:org.springframework.web.SpringServletContainerInitializer
也就是说在启动服务器时会加载这个类
3、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;
启动步骤:
1、启动Tomcat
2、启动时根据Servlet3.0的规则自动检查文件目录后加载SpringServletContainerInitializer类,注意onStartup方法中的参数Set<Class<?>> webAppInitializerClasses
3、将 Set<Class<?>> webAppInitializerClasses 集合中如果不是接口和抽象类并且是WebApplicationInitializer的子类的话,则创建对象
(@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都将传入到这个集合中,自己写的类因为继承了SpringBootServletInitializer,SpringBootServletInitializer又继承了WebApplicationInitializer,所以自己写的类也将会被传入这个集合中创建实例对象)
4、调用每一个容器对象的onStartup方法
// SpringServletContainerInitializer.onStartup方法()
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// 遍历传入的webAppInitializerClasses集合,如果不是接口和抽象类,并且是WebApplicationInitializer的子类的话,则创建对象
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);
// 遍历集合,调用每一个容器中的onStartup方法
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
5、相当于自己写的ServletInitializer会被创建对象,并且调用onStartup方法。去父类 SpringBootServletInitializer 中可以找到对应方法。
6、在onStartup方法中创建了根容器,通过config方法获取SpringBoot的主程序后去创建应用并运行。Spring的应用启动并创建IOC容器
// SpringBootServletInitializer类部分代码
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(getClass());
// 这里会创建根容器
WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
// no-op because the application context is already initialized
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not "
+ "return an application context");
}
}
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
// 创建构建器SpringApplicationBuilder
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
// 调用configure方法,自己写的类重写了这个方法,将SpringBoot的主程序类传入了进来
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
// 使用builder创建一个Spring应用
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty()
&& AnnotationUtils.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
// 启动Spring应用
return run(application);
}
8、总结:先启动Servlet容器,再启动SpringBoot应用。启动容器时根据Servlet3.0之后的标准去扫描每个包下的对应实现类并创建容器实例,并且调用每个容器的onStartup方法。调用时会创建根容器,根容器创建时又通过config方法来获取SpringBoot主程序的位置。