spring boot到底是怎么启动的

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);

最主要的就是这些了,细节在慢慢补充吧

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值