1.5 Spring Boot 启动原理解析

前言

前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏。所以这次就跟你们一起一步步揭开SpringBoot的神秘面纱,让它不在神秘。

1.5.1 SpringBootApplication背后的秘密


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
……
}

虽然定义使用了多个Annotation进行了原信息标注,但实际上重要的只有三个Annotation:

  • @Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)
  • @EnableAutoConfiguration
  • @ComponentScan
1.5.1.1 @Configuration注解

这里的@Configuration对我们来说不陌生,它就是JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。

举几个简单例子回顾下,XML跟config配置方式的区别:

1.5.1.1.1 表达形式层面
  • 基于XML配置的方式是这样
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<!--bean定义-->
</beans>
  • JavaConfig的配置方式是这样
@Configuration
public class MockConfiguration{
//bean定义
}

任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。

1.5.1.1.2 注册bean定义层面
  • ,基于XML的配置形式是这样:
<bean id="mockService" class="..MockServiceImpl">
...
</bean>
  • 而基于JavaConfig的配置形式是这样的:
@Configuration
public class MockConfiguration{
@Bean
public MockService mockService(){
return new MockServiceImpl();
}
}

任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。

1.5.1.1.3 表达依赖注入关系层面
  • 为了表达bean与bean之间的依赖关系,在XML形式中一般是这样:
<bean id="mockService" class="..MockServiceImpl">
<propery name ="dependencyService" ref="dependencyService" />
</bean> <bean id="dependencyService" class="DependencyServiceImpl"></bean>
  • 而基于JavaConfig的配置形式是这样的:
@Configuration
public class MockConfiguration{
@Bean
public MockService mockService(){
return new MockServiceImpl(dependencyService());
} @Bean
public DependencyService dependencyService(){
return new DependencyServiceImpl();
}
}

如果一个bean的定义依赖其他bean,则直接调用对应的JavaConfig类中依赖bean的创建方法就可以了。

1.5.1.2 @ComponentScan 注解

@ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。

1.5.1.3 @EnableAutoConfiguration 注解

详见 1.4 Spring Boot 自动配置原理

1.5.2 SpringApplication执行流程

1.5.2.1 SpringApplication.run()源码分析
public ConfigurableApplicationContext run(String... args) {
// 时间监控
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
configureHeadlessProperty();
// 获取spring.factories中的监听器变量,args为指定的参数数组,默认为当前类SpringApplication
//第一步:获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//第二步:构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 设置需要忽略的bean
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
//第三步:创建容器
context = createApplicationContext();
//第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//第五步:准备容器
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//第六步:刷新容器
refreshContext(context);
//第七步:刷新容器后的扩展接口
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;
}

SpringApplication的run方法的实现是我们本次旅程的主要线路,该方法的主要流程大体可以归纳如下:

  • 第一步:获取并启动监听器
  • 第二步:构造容器环境
  • 第三步:创建容器
  • 第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
  • 第五步:准备容器
  • 第六步:刷新容器
  • 第七步:刷新容器后的扩展接口

最新文章

  1. 简述AOP编程
  2. java.lang.NoSuchMethodException: org.apache.ibatis.executor.statement.StatementHandler.prepare(java.sql.Connection)
  3. Android第一天
  4. .Net 转战 Android 4.4 日常笔记(8)--常见事件响应及实现方式
  5. android ImageView组件属性
  6. Debug阶段成员贡献分
  7. 第九十七天请假 PHP TP框架 MVC模式
  8. 优化Laravel网站打开速度
  9. 树形DP+树状数组 HDU 5877 Weak Pair
  10. 安装centos 6.7
  11. Hadoop操作hdfs的命令【转载】
  12. CoreAnimation的使用
  13. java里的日期时间
  14. Effective Java 第三版——27. 消除非检查警告
  15. Swing小技巧总结
  16. 换目标啦,初识PHP
  17. 腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践
  18. vuex最简单的
  19. python的代码缩进和冒号
  20. fckeditor配置

热门文章

  1. VC++类型转换
  2. 1266: gcd和lcm(Java)
  3. 应用人员反馈报错,ORA-03137: TTC protocol internal error : [12333]
  4. Markdown中有序列表和无序列表
  5. Ubuntu16.04安装kubernetes1.13集群
  6. 从jvm源码看synchronized
  7. SpringFramework5.0 @Indexed注解 简单解析
  8. 关于GPU的传输速度与什么有关??
  9. [Vuex系列] - 初尝Vuex第一个例子
  10. python matplotlib拟合直线