war包情况下,spring boot 是怎么加载的?
图解:
我们查看spring-web.jar包:
/META-INF/services/javax.servlet.ServletContainerInitializer
文件内容:org.springframework.web.SpringServletContainerInitializer :实现ServletContainerInitializer 接口的类路径名称
这个意思就是spi机制加载SpringServletContainerInitializer 类,并执行onStartup方法
具体什么是java spi 可以查看文章:java servlet3.1规范解读系列九:共享库 运行时可插拔性 ServletContainerInitialize
我们看这个类:
首先:@HandlesTypes({WebApplicationInitializer.class})
这个表示我需要处理的类是实现了WebApplicationInitializer接口的类
HandlesTypes 注解用于表示感兴趣的一些类,它们可能指定了 HandlesTypes 的 value 中的注解(类型、方法或自动级别的注解),或者是其类型的超类继承/实现了这些类之一。无论是否设置了 metadata-complete,HandlesTypes 注解将应用。
然后:onStartup
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
initializer.onStartup(servletContext);
}
入参:Set<Class<?>> webAppInitializerClasses 的处理是容器进行的,容器会根据@HandlesTypes注解中的类,去查找实现了此接口的类
类的实现有:
SpringBootServletInitializer spring boot 应用打成war包需要继承这个类
在onStartup 方法中做了什么?
1: 初始化SpringApplicationBuilder ,目的是生成SpringApplication (主要是注册初始化类,监听类,实现类)
web启动的实现类是:AnnotationConfigEmbeddedWebApplicationContext
需要重点关注的是:
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext)application.run(new String[0]);
} 调用的是:SpringApplication 中的:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.started();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
new FailureAnalyzers(context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
listeners.finished(context, (Throwable)null);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
throw new IllegalStateException(var9);
}
}
在这里做了几件事情:
1: context = this.createApplicationContext();
我们看下:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment ? "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context.annotation.AnnotationConfigApplicationContext");
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
}
在这里生成的ApplicationContext 即为之前在 初始化SpringApplicationBuilder 中注入的类:
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
生成WebApplicationContext
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext)application.run(new String[0]);
}
在ApplicationContext bean生成的过程中,有做了什么呢?
看构造函数:
public AnnotationConfigEmbeddedWebApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
2:为context 加载环境变量,监听器等
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
使用之前注册的 初始化类对context 进行二次操作: builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)}); 这个类的主要做用:this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);
在servetContext 中,保存context
3: 这个是重头戏了this.refreshContext(context);
在这个里面做了什么呢?
在这个里面初始化springBean 等,都是在这个里面了
在这个过程中有一步:this.createEmbeddedServletContainer();
在这个处理里面会获取实现了接口:ServletContextInitializer 的bean名称,做其他初始化操作
主要是两个方面:初始化DispatcherServletRegistrationBean ,这这个初始化动作中,会注册DispatcherServlet
注册 spring secrit 相关的filter
4: 在bean初始化完成后,做的一些其他事情
this.afterRefresh(context, applicationArguments);
系统会扫面实现了接口: ApplicationRunner,CommandLineRunner 的bean,调用run方法,做些非spring 需要做的事情。
这个就很牛逼了,这个地方可以让程序员自己扩展,在系统完全启动前,在bean完全初始化后,可以加载自己的一些东西了,比如初始化redis缓存,执行一些动态脚本等
2: 初始化ServletContextListener:ContextLoaderListener
初始化这个监听器的作用就是为了保存ApplicationContext
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
最主要的就是这些了,细节在慢慢补充吧