1 前言
1.1 内嵌servlet 容器的情况下
根据SpringBoot的启动原理,可以知道,是通过以下代码可以直接,启动SpringBoot应用(就是SpringApplication.run(...)
方法):
@SpringBootApplication
public class SpringBootApplicationStarter {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationStarter.class, args);
}
}
1.2 在外置servlet容器的情况下
同上,同样是调用了SpringApplication.run(...)
方法,不过需要先启动 服务器,再间接创建->初始化->启动SpringBoot应用,以下就是这种情况下的SpringBoot启动流程:
这种启动流程原理,是依赖Servlet3.1规范的(大致如下):
-
根据Servlet3.1规范,服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面
ServletContainerInitializer
的实例。 -
ServletContainerInitializer
的实现放在jar包的META-INF/services
文件夹下,有一个名为javax.servlet.ServletContainerInitializer
的文件,内容就是ServletContainerInitializer
的实现类的全类名。 -
在任何
Servlet Listener
的事件被触发之前,当应用正在启动时,ServletContainerInitializer
的onStartup
方法将被调用。 -
ServletContainerInitializer
实现上的@handlesTypes
注解用于寻找感兴趣的类,要么是@HandlesTypes
注解指定的类,要么是指定类的子类。
1.2.1 启动流程(以Tomcat为例)
-
Tomcat启动
-
根据Servlet3.1规范,找到 ServletContainerInitializer 的实现,进行实例化
-
创建 ServletContainerInitializer 实例
-
调用 ServletContainerInitializer 实例 (
SpringServletContainerInitializer
)的 onStartup 方法 -
SpringBoot 应用,就是在 onStartup 方法中启动的
1.2.2 ServletContainerInitializer 接口
ServletContainerInitializer
就是一个接口,只有一个待实现的方法onStartup
// javax/servlet/ServletContainerInitializer.class
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}
2 外置Tomcat启动SpringBoot的流程原理
2.1 Tomcat 启动
重点看下 StandardContext
类的 startInternal
方法
protected synchronized void startInternal() throws LifecycleException {
// 略
// 调用 所有的 ServletContainerInitializer 的 onStartup 方法
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
// 略
}
2.2 根据Servlet3.1规范,找到ServletContainerInitializer的实现,进行实例化
SpringBoot 中的 ServletContainerInitializer
实现类位置在spring-web模块下
javax.servlet.ServletContainerInitializer
文件内容:
org.springframework.web.SpringServletContainerInitializer
2.3 SpringServletContainerInitializer 类
这个类实现了,ServletContainerInitializer 接口,重写了onStartup方法
SpringServletContainerInitializer
将@HandlesTypes(WebApplicationInitializer.class)
标注的所有这个类型( WebApplicationInitializer
)的类都传入到onStartup
方法的 Set集合,
为这些 WebApplicationInitializer
类型的类
创建实例 并遍历调用其 onStartup 方法。
// /org/springframework/web/SpringServletContainerInitializer.class
//感兴趣的类为WebApplicationInitializer及其子类
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//先调用onStartup方法,会传入一系列webAppInitializerClasses
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
// 用于存放 感兴趣的类 的list 集合
List<WebApplicationInitializer> initializers = new LinkedList();
Iterator var4;
if (webAppInitializerClasses != null) {
var4 = webAppInitializerClasses.iterator();
// 遍历感兴趣的类
while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
// 判断是不是接口,是不是抽象类,是不是该类型
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
// 实例化每个WebApplicationInitializer并添加到initializers中
initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
} else {
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
var4 = initializers.iterator();
// 依次调用 WebApplicationInitializer 的onStartup方法
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
initializer.onStartup(servletContext);
}
}
}
}
在SpringServletContainerInitializer
方法中又调用每一个WebApplicationInitializer
的
onStartup
方法。
即先调用SpringServletContainerInitializer实例的onStartup方法,在onStartup()方法内部又遍历每一个WebApplicationInitializer类型的实例,调用其onStartup()方法。
2.4 WebApplicationInitializer(Web应用初始化器)
在 Servlet 3.0+环境中提供的一个接口,以便编程式配置ServletContext
而非传统的xml配置。
该接口的实例被SpringServletContainerInitializer
自动检测(@HandlesTypes(WebApplicationInitializer.class)
这种方式)。
而SpringServletContainerInitializer是Servlet 3.0+容器自动引导的。
通过WebApplicationInitializer,以往在xml中配置的DispatcherServlet
、Filter
等都可以通过代码注入。
你可以不用直接实现WebApplicationInitializer,而选择继承AbstractDispatcherServletInitializer
。
可以看到,将会创建我们的com.springboot.template.ServletInitializer(继承自SpringBootServletInitializer)实例,并调用onStartup方法。
2.5 创建 SpringBootServletInitializer的实例,调用 onStartup方法
继承这个类的,就是作为SpringBoot的启动类
我定义的SpringBoot 启动类,com.springboot.template.ServletInitializer
继承 自 SpringBootServletInitializer;
SpringBootServletInitializer 又实现了 WebApplicationInitializer
接口,并重写了它的 onStartup 方法。
重点是 这个 createRootApplicationContext(servletContext)
通过这个方法 开始 创建->启动 springboot应用
// org/springframework/boot/web/servlet/support/SpringBootServletInitializer.class
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
// 略
// 创建WebApplicationContext 和 为容器添加监听
// 重点是 createRootApplicationContext(servletContext);
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(this.getClass());
// 创建 WebApplicationContext
WebApplicationContext rootApplicationContext = this.createRootApplicationContext(servletContext);
// 如果根容器不为null
if (rootApplicationContext != null) {
// 则添加监听
servletContext.addListener(new SpringBootServletInitializer.SpringBootContextLoaderListener(rootApplicationContext, servletContext));
} else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
}
}
// 开始 创建->启动 springboot应用
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
// 创建 SpringApplicationBuilder --这一步很关键
SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
// 设置SpringBoot应用主启动类
// 这里为 com.springboot.template.ServletInitializer
builder.main(this.getClass());
// 从servletContext中获取
// servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)作为parent。
// 第一次获取肯定为null
ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
// 将ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE重置为null
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
// 注册一个新的ParentContextApplicationContextInitializer--包含parent
builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
}
// 初始化
// 注册ServletContextApplicationContextInitializer--包含servletContext
builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
// 设置applicationContextClass为AnnotationConfigServletWebServerApplicationContext
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
// 调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来
// 此方法被启动引导类 ServletInitializer有方法重写, 传入的是应用构建器SpringApplicationBuilder, 也就是SpringBoot的主程序类
builder = this.configure(builder);
// 添加监听器
builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
// 返回一个准备好的SpringApplication ,准备run-很关键
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && MergedAnnotations.from(this.getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
application.addPrimarySources(Collections.singleton(this.getClass()));
}
Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
application.setRegisterShutdownHook(false);
// 启动SpringBoot应用
return this.run(application);
}
// 略
}
2.6 createRootApplicationContext 详细流程源码分析
/org/springframework/boot/web/servlet/support/SpringBootServletInitializer.class
的createRootApplicationContext
的方法
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
// 创建 SpringApplicationBuilder --这一步很关键
SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
// 略
}
跟踪代码到:
/org/springframework/boot/builder/SpringApplicationBuilder.class
public SpringApplicationBuilder(Class<?>... sources) {
this.application = createSpringApplication(sources);
}
此时的Sources为空,继续跟踪代码:
/org/springframework/boot/SpringApplication.class
public class SpringApplication {
// 略
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// web应用类型--Servlet
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 获取ApplicationContextInitializer,也是在这里开始首次加载spring.factories文件
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 这里是第二次加载spring.factories文件
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
// 略
}
ApplicationContextInitializer
(应用上下文初始化器)
是spring组件spring-context
组件中的一个接口,主要是spring ioc容器刷新之前的一个回调接口,用于处于自定义逻辑。
ConfigurableApplicationContext
Spring IOC容器称为“已经被刷新”状态前的一个回调接口,去初始化ConfigurableApplicationContext。
通常用于需要对应用程序上下文进行某些编程初始化的Web应用程序中。
例如,与ConfigurableApplicationContext#getEnvironment() 对比,注册property sources或激活配置文件。
另外ApplicationContextInitializer(和子类)相关处理器实例被鼓励使用去检测org.springframework.core.Ordered接口是否被实现或是否存在org.springframework.core.annotation.Order注解,如果存在,则在调用之前对实例进行相应排序。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69763.html