首先,我们要明确一点的是,SpringBoot不是一个代替Spring的新框架,它的目的是用来简化Spring应用开发的。
Spring的目的是简化Java开发,SpringBoot的目的是简化Spring开发。
先来看看SpringBoot的使用,下面是SpringBoot的典型使用:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
上面代码和普通启动类的区别有两处:类上的注解 @SpringBootApplication 和 main方法中的SpringApplication.run方法调用。我们就来分析一下这两个地方。
@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 {
...
}
其中,@Target、@Retention、@Documented、@Inherited都是一些元注解,重要的是@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 这三个注解。
1. @SpringBootConfiguration
进到 @SpringBootConfiguration 的定义,
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration{
...
}
它里面就是一个@Configuration。我们知道这个注解是用来使用JavaConfig形式配置Spring容器中的Bean。这是SpringBoot推荐使用的Bean配置形式。所以我们在使用SpringBoot的时候,没有任何的xml配置文件。
2. @ComponentScan
这个注解是Spring里面的自动扫描和装配Bean的注解。它里面使用了 excludeFilters 属性,这个属性指定了哪些类不需要被装配。type = FilterType.CUSTOM 表示自定义FilterClass排除。
@ComponentScan默认扫描的是本包以及子包下面的所有类,所以SpringBoot项目的包结构通常像下面这样
启动类放在根package下面。也可以通过basePackages属性来指定要扫描的路径。
3. @EnableAutoConfiguration
这个注解就是SpringBoot自动配置原理的核心注解,也是一个组合注解。看到这个注解以Enable开头,会不会想到Spring里面的@EnableScheduling、@EnableCaching等以Enable开头的注解?作用都是开启某项功能的使用,原理都是借助@Import的支持,收集和注册特定场景相关的bean定义。
来看它的定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
果然有一个@Import注解。 里面的@AutoConfigurationPackage 这个注解实现的是一个自动扫描功能。
@Import(AutoConfigurationImportSelector.class),从名称来看,它导入了一个自动配置导入选择器。我们进入AutoConfigurationImportSelector这个类看看源码,里面实现了selectImports方法
继续点进getAutoConfigurationEntry方法中,这个方法按照意思是获取默认配置入口
继续进入getCandidateConfigurations方法,这个方法的意思是获取候选配置
里面的 SpringFactoriesLoader.loadFactoryNames()方法是真正的加载配置的地方。
它其实是去加载一个外部的文件,而这个文件是在jar包里面的 META-INF/spring.factories 这个路径中,
SpringFactoriesLoader配合@EnableAutoConfiguration注解使用,根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类,配置项也是全类名,可以通过反射实例化配置类的对象。
所以,@EnableAutoConfiguration自动配置的实现流程就是:借助SpringFactoriesLoader从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后加载到IoC容器中。
SpringBoot的执行原理
从main方法中的SpringApplication.run()方法点进去,
先创建一个SpringApplication的实例,再调用实例的run方法。先看看构造方法
- 是否需要创建一个web形式的ApplicationContext是根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定的
- 加载ApplicationContextInitializer是使用SpringFactoriesLoader在应用的classpath中查找并加载的,它找的还是jar包里面的 META-INF/spring.factories 这个文件
- 加载ApplicationListener也是使用SpringFactoriesLoader在应用的classpath中查找并加载的,也是找的jar包里面的 META-INF/spring.factories 这个文件
再看看 run 方法
该方法的几个主要步骤如下:
- 创建了应用的监听器SpringApplicationRunListeners并开始监听
- 加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment
- 配置环境(Environment)加入到监听器对象中
- 创建run方法的返回对象:ConfigurableApplicationContext(应用配置上下文)
- 将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联
- refreshContext(context)方法,配置各种bean的实例化