其实关于这块我是觉得翻来覆去的讲了好多遍了,尤其是开头讲的启动,自动配置之类的,不过课程这么设置也应该有它自己的原理。下面让我们按照教程一步一步学习了解SpringBoot的启动原理。
SpringBoot启动原理
因为之前我自己一步一步往下找走过这个,但是很多方法都是临时百度或者连蒙带猜的,这里老师一步一步讲解能让思路更清晰。我这里用图文并茂的方式记录下。
-
在启动类中的run方法
- 点进去发现本质是创建SpringApplication对象并运行run方法
2.1 . 创建SpringApplication对象过程
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//这句代码是判断当前应用是不是web应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取类路径META-INF/spring.factories下配置的所有ApplicationContextInitlalizer保存
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取类路径META-INF/spring.factories下配置的所有ApplicationListener保存
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 从多个配置类中找到有main方法的主配置类。
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2. 运行run方法:
下面一步一步分析源码:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//据说这个注解以上都是jwt的东西,从这个注解往下开始分析
//获取类路径/META-INF/spring.factories下所有的SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
//回调所有SpringApplicationRunListener的starting方法
listeners.starting();
try {
//封装命令行参数。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//控制台打印这个spring的图标及版本信息
Banner printedBanner = printBanner(environment);
//创建applicationContext。在这里会决定创建web容器还是普通的。2.x版本又加了个reactive容器
context = createApplicationContext();
//1.x版本没这句,但是看代码可以猜一下,应该是/META-INF/spring.factories下所有的异常报告?
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备上下文环境,将environment保存到ioc中。这个方法中有两个方法:
//applyInitializers(context)回调所有2.1中保存的ApplicationContextInitlalizer的initialize方法。
//listeners.contextPrepared(context):回调所有SpringApplicationRunListener的contextPrepared方法。
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//在这个方法的最后回调所有的SpringApplicationRunListener的contextLoaded方法。
//刷新容器:IOC容器初始化,如果是web应用,还会创建嵌入式的tomcat
//扫描,创建,加载所有组件的地方(配置类,组件,自动配置)
refreshContext(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。(注意,这里ApplicationRunner先于CommandLineRunner回调)
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
至此,这个springBoot项目算是启动了。
其实这里听的云里雾里的,毕竟好多延伸出更多的东西。尽量理解吧。
事件监听机制
配置在META-INF/spring.factories:
- ApplicationContextInitlalizer
- SpringApplicationRunListener
只需要放在IOC容器中:
- ApplicationRunner
- CommandLineRunner
这些都是组件。我们在代码中写好,要配置在指定的位置。后两个只要注入bean纳入spring管理就行了,比较简单,而前两个要写在配置文件中,我们可以找一个现成的看看人家怎么写的,参考一下:
然后我们照着自己写一下(这里四个实现类就不说了,就是自己建个类分别继承上面四个接口,方法只要写打印语句我们看看什么时候输出就行了):
启动项目会发现输出顺序如上图。
因为我们方法中都是输出语句所以显着比较简单,但是实际工作中可以在这个方法中写各种逻辑代码的。挺实用一个功能。
SpringBoot场景启动器starter
自定义starter:
- 这个场景需要依赖什么?
- 如何编写自动配置
一些用得到的经验:
- @Configuration //指定这个类是一个配置类
- @ConditionalOnxxx //在指定条件下配置生效
- @AutoConfigureAfter //指定自动配置类的顺序
- @Bean //给容器中添加组件
- @ConfigurationProperties //结合相关 xxxProperties类来绑定相关配置
- @EnableConfigurationProperties //让xxProperties生效加入到容器中
自动配置类要能加载:必须将这个启动就加载的自动配置类配置在META-INF/spring.factories中(参考上文中容器初始化和lister的用法)
这些经验说完了,下面简单说一下spring 中启动器的常规用法:
- 启动模块是一个空jar文件,仅仅提供辅助性依赖管理。这些依赖可能用于自动装配或其他类库。
- 命名规则:
- 推荐使用xxx-starter命名规约
- 官方命名空间:
- 前缀:spring-boot-starter
- 模式: spring-boot-starter-模块名
- 举例: spring-boot-starter-web,spring-boot-starter-jdbc
- 自定义命名空间:
- 后缀:spring-boot-starter
- 模式: 模块名-spring-boot-starter
- 举例: mybatis-spring-boot-starter
总结一下:启动器只用来做依赖导入。 专门写一个自动配置模块。启动器依赖自动配置,别人只需要引入启动器
接下来自己写一个简单的启动器:
首先大概说一下,如果按照官方规范的来做,这个demo最少三个项目:
- starter场景启动器
- autoConfigurate自动装配
- 引用这个场景启动器的自己的项目
所以这里也是直接三个项目(因为我是eclipse,所以同时创建三个,hiahia)
这里面starter里啥也没有就是引用了autoConfigure依赖而已,一个java类都莫得,所以不用多说了。
然后第一个假装是自己项目。用啥引入啥就行。也没啥特别的。
最主要的要说一下这个自动配置项目。
实现了自动配置的功能,而且还是动态的。具体怎么实现的呢?其实简单来说就是一个配置属性类xxxProperties。一个配置类xxxAutoConfiguration。
最后这个自动配置是根据自动扫描META-INF/spring.factories里的类名实现的。
下面附上实现的代码:
这个类就是用来绑定配置文件中的属性的。
注意这个类上没有任何注解!因为所有的注入都统一用自动配置类完成
这个类把之前的配置属性类和那个service都注入进来了。接下来重点是怎么让这个类自动执行呢?META-INF/spring.factories
至此这个自动配置就完成了,然后把starter中引入这个依赖。再在我们的项目中引入starter依赖就完事了。
接着我们就可以测试一下啦:
然后接口访问:
到了这里我们想要的效果就实现啦。这个代码主要就是为了实现这个思路,功能比较浅薄。但是我们举一反三,就知道为什么spring boot只配置一些重要参数就能启动一些东西啦!
本篇笔记就记到这里,如果稍微有帮到你记得点个喜欢点个关注!这一篇其实都是原理啊,底层啊之类的东西,挺好的,不见得学了能立竿见影的看到进步,可是拓宽思路和原理,尤其是知道底层对一些问题的分析也更容易。而且这篇算是我看的教程的springboot基础篇的结束了,下面就要讲一些整合中间件的教程了!感觉学了快一个月,现在翻源码颇有心得了,对spring boot 起码有个浅显的认识而不是之前只会使用啦,每天进步一点点~~我们共勉!