SpringBoot框架使用好几年了,都晓得5分钟搭建一个项目,方便快捷,但是却并没有总结过为啥会这么快捷呢?他到底是如何自动装配的呢?
先看看SpringBoot的主配置类:
@SpringBootApplication(scanBasePackages = {"com.huohuo.info"})
public class Bootstrap {
public static void main(String[] args) {
SpringApplication.run(Bootstrap.class, args);
}
}
main方法启动,运行run方法,传入一个被@SpringBootApplication注解的类。
一、整体图解
来看一个简单的概要图:
@SpringBootApplication点进去能看到是一个组合注解:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
二、@SpringBootConfiguration
通过源码得知,@SpringBootConfiguration其实就是想表达我是一个配置值类,和配置文件一样。表面翻译也就是SpringBoot的配置
三、@EnableAutoConfiguration
顾名思义,这个注解一定和自动配置相关。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
1)@AutoConfigurationPackage
这个注解是自动配置包,主要是使用的@Import来给Spring容器中导入一个组件 ,这里导入的是Registrar.class。
我们可以看到Registrar是一个静态内部类,获取扫描的包路径。debug调试可以看一下。
(PS:右键通过Evaluate Expression窗口,输入红色标记信息,查看想看的值)可以看到,我们获取到了包路径信息
metadata参数到底是什么呢?我们通过调试看到,其实就是标记在最开始项目标记的包路径,也就是主配置类。
说白了理解就是将主配置类所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意。
2)@Import({AutoConfigurationImportSelector.class})
@AutoConfigurationPackage注解实现了包扫描路径,那具体加载哪些组件呢?
@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。
AutoConfigurationImportSelector实现了DeferredImportSelector。Spring内部在解析@Import时会调用getAutoConfigurationEntry方法,扫描具有META-INF/spring.factories文件的jar包。
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
/* 从META-INF/spring.factories中获取候选的自动配置类*/
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
/*排重*/
configurations = this.removeDuplicates(configurations);
/*根据EnableAutoConfiguration注解中属性,获取不需要自动装配的类名单*/
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
/*根据@EnableAutoConfiguration.exclude
@EnableAutoConfiguration.excludeName
spring.autoconfigure.exclude进行排除*/
this.checkExcludedClasses(configurations, exclusions);
/*exclusions也排除*/
configurations.removeAll(exclusions);
/*通过读取spring.factories中的OnBeanCondition\OnClassCondition\OnWebApplicationCondition进行过滤*/
configurations = this.filter(configurations, autoConfigurationMetadata);
/*这个方法是调用实现了AutoConfigurationImportListener的bean.分别把候选的配置名单,和排除的配置名单传进去做扩展*/
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
看getCandidateConfigurations,获取候选的配置
/*获得候选的配置*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
实际上它返回了一个List,这个List是由loadFactoryNames()方法返回的,其中传入了一个getSpringFactoriesLoaderFactoryClass(),我们可以看看这个方法的内容。
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
我们看到了一个眼熟的词 —— EnableAutoConfiguration,也就是说,它实际上返回的就是标注了这个类的所有包。标注了这个类的包不就是@SpringBootApplication吗!
再看还有一个断言:意思是,configurations必须非空,否则就打印一段话,No auto configuration classes found in META-INF/spring.factories。
通过断点调试,我们会发现加载了119个配置类,实际上并非全部会使用。
过滤掉之后还剩31个生效的
简单理解:来来回回,兜兜转转绕了这么多地方,又回到了EnableAutoConfiguration身上,其实就是为了将启动类所需的所有资源导入,需要啥生效啥。
3)自动配置生效
@ConditionalOnBean:当容器里存在指定bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类路径下有指定类的条件下。
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
4)mybatis自动配置
以mybatis为例来看一下:
任何一个springboot应用,都会引入spring-boot-autoconfigure,而spring.factories文件就在该包下面。spring.factories文件是Key=Value形式,多个Value时使用,隔开,该文件中定义了关于初始化,监听器等信息,这是Spring的SPI机制。真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration,看如下mybatis的spring.factories文件内容
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {
- @Configuration,表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件。
- @ConditionalOnClass 判断当前项目有没有这个类,这个类需要在classpath中有SqlSessionFactory和SqlSessionFactoryBean定义时才会起效,并且要有一个DataSource的candidate注册到spring容器中
- @ConditionalOnBean 必须存在DataSource信息源
- @EnableConfigurationProperties将配置文件中对应的值和 MybatisProperties绑定起来;并把 MybatisProperties加入到 IOC 容器中。并注册ConfigurationPropertiesBindingPostProcessor用于将@ConfigurationProperties的类和配置进行绑定。