SpringApplication的初始化
我们都知道我们在创建一个boot项目的时候都会有个启动类如下:
@SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
}
那我们首先看这个SpringApplication这是一个重要的部分,可以看到他调用了run方法,那么这个run方法我们待会再说,先说这个SpringApplication,他主要呢就是做了一些事情。这里做的事情呢也是为后文有用
- 主类赋值给primarySource
- 判断应用类型
- 从META-INF/spring.factories里面拿一些东西然后初始化和赋值最终装入到集合中
- 根据调用栈,拿到main方法的类名
?那么我们就来看看源码是怎么实现的 主要的就是 getspringFactoriesInstance这个方法:
// 这里是推断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 从自动配置里面拿到类然后通过底层实例化成对象(Initializer)
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 从自动配置里面拿到类然后通过底层实例化成对象,这里是监听器是spring的监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//根据调用栈,拿到main方法的类名
this.mainApplicationClass = this.deduceMainApplicationClass();
?SpringBoot启动流程
那么这一步我们就要来看看run方法了 流程也就是这一大段代码 具体的源码我就不去一步一步发了,我直接发总结,最后面会有详细的源码解说图:
ConfigurableApplicationContext是Applicationcontext的子接口在基础上增加了应用上下文是容器的高级接口
// 开启时间
long startTime = System.nanoTime();
// 1.创建上下文
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
// 2.获取开启监听器这个SpringApplicationRunListener是springboot的事件发布监听器,
// 主要作用是监听boot启动的事件然后发送给SpringApplication的初始化里面listeners的监听器
// 主要是就是做了去自动装配里面拿到符合的类然后通过底层去实例化注册为bean
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 这里是开启事件然后发送给SpringApplication的初始化里面listeners的监听器
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 4. 构造应用上下文环境这里主要就是做了一些环境配置比如系统环境然后还有自己写的环境(也就是写的yml,properties那些里面写的配置参数)
// 然后里面还会启动相应的监听器,最重要的就是一个文件加载监听器,在初始化部分里面有一个ConfigFileApplication
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 设置一些需要忽略的bean
this.configureIgnoreBeanInfo(environment);
// 打印图形
Banner printedBanner = this.printBanner(environment);
// 5. 初始化应用上下文 这里最主要的就是方法类实现了父类GenericWebApplicationContext然后通过父类的构造创建了容器
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 6. 刷新应用上下准备阶段
// 这里会 1.设置容器环境 2.调用上面初始化创建好的initializers 3.向各个监听器发布容器已经准备好的事件这里是监听器是时间发布的监听器也就是springboot的监听器发送给spring的监听器boot----->spring
// 4. 然后把args命令注册为单例bean 5. 拿到主类,加载启动类,注册到容器中 6. 发布事件
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 7. 刷新应用上下文这里是真正执行自动装配的路口 这里会走到spring源码中去也就是那十三个方法 不懂得可以去看我上篇的解说
// 这里是这几个方法中最主要的:这里做了kt猫和Mvc的结合底层也是在这里,还有自动装配原理路口也是在这里,这些我们待会再说
this.refreshContext(context);
// 8. 刷新应用上下文后的扩展接口,用于扩展的,这里是空实现设计模式中的模板方法
this.afterRefresh(context, applicationArguments);
Spring自动装配原理
从@springBootApplication注解走进去,这里是主要的:
?那么自动最主要的就是Import引入了一个组件类,自动装配就是在这个里面实现的,那么说这个之前,我们先说说@AutoConfigurationPackage主要就是做了注册bean,然后再看看引入的那个组件类:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 获取是否有配置spring.boot.enableautoconfiguration类 默认返回true
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 拿到自动装配,也就是去META-INF/spring.factories里面拿到所有的自动装配类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 利用LinkedHashSet移除重复的配置类
configurations = this.removeDuplicates(configurations);
// 那要需要排除的类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 检查要被排除的配置类 因为有些不是自动配置,则需要排除
this.checkExcludedClasses(configurations, exclusions);
// 把需要排除的类排除掉
configurations.removeAll(exclusions);
// 有些自动装配不需要 这里是过滤 主要就是看你有没有加依赖,不需要的直接排除掉,以避免浪费资源
configurations = this.getConfigurationClassFilter().filter(configurations);
// 拿到真正符合条件的自动装配类,此时触发AutoConfigurationImportEvent事件
// 目的就是告诉ConditionEvaluationReport条件评估报告器来记录符合条件的自动装配
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
总结:
走run的时候走到那个刷新应用上下文的时候之前的初始化应用上下文就已经把主类已经创建了ioc了,然后去查看@ComponentScan的包扫描路径是什么(如果没有配置也就是@SpringBootApplication这种情况底层会自动把该类的包加入到包扫描作为默认的包扫描),然后再根据scan去扫描主类上面的注解 然后拿到注解看是否有@controller @Service那些注解,有的话就注册beandefinition然后加入到map中 然后再解析看有没有import注解 底层通过递归拿到import注解 也就是先拿到主类的@SpringBootApplication注解 如果不符合基于一直递归查询里面的注解 然后拿到之后就是真正去执行import引导的组件类 完成真正的自动装配(BeanDefinitionMap)
在@EnableAutoConfiguration的@AutoConfigurationPackage通过@Import({Registrar.class})主要就是做了注册bean然后通过@Import({AutoConfigurationImportSelector.class})拿到META-INF/所有的自动装配然后先排除有没有重复的也就是自己写的然后在通过查询所有自动装配的条件注解看看符不符合条件不符合直接排除也就是看加没加依赖然后在第二遍筛查最终加入到map中(前面都是加入到list最后才是放入map中)最后还会实现order排序
上面就是自动装配真正的原理,需要结合run方法来答
最后奉上我精心制作的宝图:
?其实我还画了一个详细的Mybatis源码解析图,就不打算详细讲,看图吧,很全面得解析图:
|