引言
- 在引入 Spring Boot 框架之后,省去了 xml 文件配置可以理解为注解体系的不断完善,但省去的依赖的管理, Spring Boot 框架是如何做到的?
- 我们返回启动类,观察其中代码
这其中有两个关键的地方值得研究
- 注解:@SpringBootApplication
- run方法:SpringApplication.run()
对于注解 @SpringBootApplication
- 查看 @SpringBootApplication 源码
这里重点的注解有3个
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
- 查看 @SpringBootConfiguration 源码
解读:
- 可以看到,这个注解上面,又有一个 @Configuration 注解
- 通过上面的注释了解到:这个注解的作用就是声明当前类是一个配置类,然后 Spring 会自动扫描到添加了 @Configuration 的类,并且读取其中的配置信息
- 而 @SpringBootConfiguration 是来声明当前类是SpringBoot应用的配置类,项目中只能有一个,所以一般我们无需自己添加
- 查看 @EnableAutoConfiguration 源码
解读
- 插入一段官方说明:The second class-level annotation is @EnableAutoConfiguration. This annotation tells Spring Boot to “guess” how you want to configure Spring, based on the jar dependencies that you have added. Since spring-boot-starter-web added Tomcat and Spring MVC, the auto-configuration assumes that you are developing a web application and sets up Spring accordingly.
- 简单翻译为:第二级的注解@EnableAutoConfiguration,告诉SpringBoot基于你所添加的依赖,去“猜测”你想要如何配置Spring。比如我们引入了spring-boot-starter-web,而这个启动器中帮我们添加了tomcat、SpringMVC的依赖。此时自动配置就知道你是要开发一个web应用,所以就帮你完成了web及SpringMVC的默认配置了
- 结合源码内注释,大概得出结论,Spring Boot 内部对大量的第三方库及 Spring 内库进行了默认配置,并且会根据我们在创建项目时候的风格,是否引入了对应库的依赖等来判断哪些配置可能会被使用到,这些默认配置就会生效
- 所以,我们使用SpringBoot构建一个项目,只需要引入所需框架的依赖,配置就可以交给SpringBoot处理了,若你不希望使用SpringBoot的默认配置,它也提供了自定义配置的入口
- 查看 @ComponentScan 源码
解读:
- 注释大概意思:配置组件扫描的指令。提供了类似与< context:component-scan >标签的作用,通过 basePackageClasses 或者 basePackages 属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包
- 我们的@SpringBootApplication注解声明的类就是main函数所在的启动类,因此扫描的包是该类所在包及其子包
- 因此,启动类在项目目录结构中,一般都会处于较浅层的位置,以便于扫描
对于 SpringApplication.run() 方法
- 点击查看源码中方法
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//初始化监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布ApplicationStartingEvent
listeners.starting();
try {
//装配参数和环境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//发布ApplicationEnvironmentPreparedEvent
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//创建ApplicationContext,并装配
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//发布ApplicationPreparedEvent
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//发布ApplicationStartedEvent
listeners.started(context);
//执行Spring中@Bean下的一些操作,如静态方法等
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//发布ApplicationReadyEvent
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
- 观察源码中执行的步骤大概分为
- 初始化监听器
- 发布ApplicationStartingEvent
- 装配参数和环境
- 发布ApplicationEnvironmentPreparedEvent
- 创建ApplicationContext,并装配
- 发布ApplicationPreparedEvent
- 发布ApplicationStartedEvent
- 执行Spring中@Bean下的一些操作,如静态方法等
- 发布ApplicationReadyEvent