赞
踩
源码分析篇_SpringBoot源码分析
田超凡
原创博文,仿冒必究,部分素材转载自每特教育蚂蚁课堂
1.1 Maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring boot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- spring boot aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!-- 自定义YML配置项 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 能够给开发者引入该jar包后 有一定提示 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>
1.2 启动类
/***
* TODO TCF 启动类
*/
@SpringBootApplication
@Slf4j
public class App {
public static void main(String[] args)
{
SpringApplication.run(App.class);
}
}
1.3 @EnableAutoConfiguration加载spring-boot-starter和spring-boot-autoconfigurer依赖中的启动类
注意:
@SpringBootApplication是一个复合注解,基本原理等效于@Configuration + @EnableAutoConfiguration +@ComponentScan,@ComponentScan不指定扫包范围则默认扫描启动类所在目录及其子目录下的所有类,@EnableAutoConfiguration是加载springbootstarter外部依赖的核心注解
spring-boot-autoconfigurer包中的META-INF/spring.factories
AutoConfigurationImportSelector类 selectImports()方法,加载项目所有依赖中及当前项目自身中自定义的META-INF/spring.factories文件中的EnableAutoConfiguration映射的类
递归加载当前项目所有依赖包中的META-INF/spring.factories文件
加载spring.factories中定义的EnableAutoConfiguration映射的所有类
底层是通过造一个SpringApplication实例来调用run()方法,那么我们也可以这样启动SpringBoot项目:new SpringApplication(primarySources).run(args);
也就是说,SpringApplication.run(Class<?> primarySource)
等效于new SpringApplication(Class<?> primarySource).run(args);
接下来看一下SpringApplication的构造函数做了什么:
在带参构造函数中,做了以下几件重要的事:
我们发现SpringBoot的Web应用类型有三个取值:NONE、SERVLET、REACTIVE
看下官方文档中这三个取值分别代表什么意思:
诶,觉得这个方法似曾相识?没错,它的原理就是同加载EnableAutoConfiguration原理一样的,都是同一个方法,都是先扫描当前项目所有依赖包中的META-INF/spring.factories文件,然后加载里面定义的类,只不过前面是加载EnableAutoConfiguration映射的类,此处是加载ApplicationContextInitializer映射的类
到此,SpringApplication带参构造执行完毕,接下来看一下run方法的实现做了什么:
run方法主要做了以下几件事:
至此,SpringBoot启动和加载原理分析完毕
上文讲到,SpringBoot启动前会先加载
@EnableAutoConfiguration
SpringBoot内嵌Tomcat的核心处理类就在EnableAutoConfiguration映射的在启动前被加载的类中,这个核心类就是ServletWebServerFactoryAutoConfiguration
通过@Import引入了三个内嵌的Web服务器配置类,这也意味着SpringBoot支持三种内嵌的Web服务器,分别是:Tomcat(默认,对应的webApplicationType是SERVLET)、Jetty、Undertow
同上,META-INF/spring.factories EnableAutoConfiguration映射的类中,SpringMVC核心处理类是DispatcherServletAutoConfiguration
解析WebMvcProperties配置项并基于配置项创建并自动装载了默认的SpringMVC核心控制器DispatcherServlet
SpringBoot核心上下文AnnotationConfigServletWebServerApplicationContext
SpringApplication run()
createApplicationContext()创建SpringBoot集成Spring IOC容器的全局上下文AnnotationConfigServletWebServerApplicationContext,对应的应用类型webApplicationType是SERVLET
refreshContext()初始化Spring IOC容器,创建并初始化单例Bean,实现自动装配
SpringBoot自定义配置文件解析和加载依赖于SpringBoot核心监听器SpringApplicationRunListener,上文写到该监听器在SpringApplication.run()启动SpringBoot容器时会加载并调用其内部定义的多个事件方法,这些事件方法贯穿SpringBoot容器完整的启动周期(贯穿run方法的执行),现在就来看下这个核心监听器中定义的方法分别是在什么时机被调用的:
public interface SpringApplicationRunListener {
// 在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作
void starting();
// 当environment构建完成,ApplicationContext创建之前,该方法被调用
void environmentPrepared(ConfigurableEnvironment environment);
// 当ApplicationContext构建完成时,该方法被调用
void contextPrepared(ConfigurableApplicationContext context);
// 在ApplicationContext完成加载,但没有被刷新前,该方法被调用
void contextLoaded(ConfigurableApplicationContext context);
// 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
void started(ConfigurableApplicationContext context);
// 在run()方法执行完成前该方法被调用
void running(ConfigurableApplicationContext context);
// 当应用运行出错时该方法被调用
void failed(ConfigurableApplicationContext context, Throwable exception);
}
kid.tcf.userName = tcf
public class MyEnvironmentApplicationListener implements SpringApplicationRunListener {
private SpringApplication application;
private String[] args;
public MyEnvironmentApplicationListener(SpringApplication application, String[] args)
{
this.application = application;
this.args = args;
}
@Override
public void starting()
{
System.out.println("====== starting ======");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment)
{
Properties properties = new Properties();
try
{
properties.load(this.getClass().getClassLoader().getResourceAsStream("my.properties"));
PropertySource propertySource = new PropertiesPropertySource("my", properties);
environment.getPropertySources().addLast(propertySource);
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
}
@Override
public void started(ConfigurableApplicationContext context) {
}
@Override
public void running(ConfigurableApplicationContext context) {
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
注意:自定义SpringApplicationRunListener必须带有带参构造函数,且类型必须是SpringApplication和String[],否则无法启动时被加载,原因是:
SpringApplication.run()加载SpringApplicationRunListener监听器时,是基于反射和监听器实现类中的带参构造创建监听器实现类实例并实现方法调用:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。