【0.SpringBoot专栏的相关文章都在这里哟,后续更多的文章内容可以点击查看】
【1.SpringBoot初识之Spring注解发展流程以及常用的Spring和SpringBoot注解】
【2.SpringBoot自动装配之SPI机制&SPI案例实操学习&SPI机制核心源码学习】
Redis为案例演示
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
spring:
redis:
host: 127.0.0.1
port: 6379
@Autowired
private RedisTemplate<String,String> redisTemplate;
结论:通过上上面的步骤我们就可以将一些其它服务,插件和我们的SpringBoot很好的融合在一起,步骤简单,操作容器,但是,为什么RedisTemplate可以被直接注入?它是什么时候加入到Ioc容器的呢?好的,接下来我们就带着问题继续向下学习。
EnableAutoConfiguration的主要作用其实就是帮助springboot应用把所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器中。
@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 {};
}
那么问题就来了:从EnableAutoConfiguration上面的Import注解来看,这里面并不是引入另外一个Configuration。而是导入了一个ImportSelector。这个是什么东西呢?不要着急,我们继续往下看。
Enable注解不仅仅可以像前面演示的案例一样很简单的实现多个Configuration的整合,还可以实现一些复杂的场景,比如可以根据上下文来激活不同类型的bean,@Import注解可以配置三种不同的class。
了解了selector的基本原理之后,后续再去分析AutoConfigurationImportSelector的原理就很简单了,它本质上也是对于bean的动态加载。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 从配置文件(spring-autoconfigure-metadata.properties)中加载 AutoConfigurationMetadata
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 获取所有候选配置类EnableAutoConfiguration
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
捕捉关键信息:AutoConfigurationImportSelector类中selectImports()方法中最关键的是getAutoConfigurationEntry()这个方法,接下来还用我教你你吗?直接进入getAutoConfigurationEntry方法一探究竟。
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获取元注解中的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//使用SpringFactoriesLoader 加载classpath路径下META-INF\spring.factories中,
//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的value
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//去重
configurations = removeDuplicates(configurations);
//应用exclusion属性
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不被加载
configurations = filter(configurations, autoConfigurationMetadata);
//广播事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
本质上来说,其实EnableAutoConfiguration会帮助springboot应用把所有符合@Configuration配置都加载到当前SpringBoot创建的IoC容器,而这里面借助了Spring框架提供的一个工具类SpringFactoriesLoader的支持。以及用到了Spring提供的条件注解@Conditional,选择性的针对需要加载的bean进行条件过滤
public final class SpringFactoriesLoader {
//省略大部分代码
}
SpringFactoriesLoader这个工具类的使用。它其实和java中的SPI机制的原理是一样的,不过它比SPI更好的点在于不会一次性加载所有的类,而是根据key进行加载。
首先,SpringFactoriesLoader的作用是从classpath/META-INF/spring.factories文件中,根据key来加载对应的类到spring IoC容器中。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.13.RELEASEversion>
dependency>
创建bean
public class TestCore {
public void testCore(){
System.out.println("testCore.....");
}
}
创建config
@Configuration
public class TestConfig {
@Bean
public TestCore testConfig(){
return new TestCore();
}
}
把前面的工程打包成jar,当前项目依赖该jar包
<dependency>
<groupId>com.ljwgroupId>
<artifactId>test-CoreartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
运行结果会报错,原因是mashibingCore并没有被Spring的IoC容器所加载,也就是没有被EnableAutoConfiguration导入
@SpringBootApplication
public class SpringBootTestCoreApplication {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ac=SpringApplication.run(SpringBootTestCoreApplication.class, args);
TestCore core=ac.getBean(TestConfig.class);
System.out.println(core.testCore());
}
}
在test-Core项目resources下新建文件夹META-INF,在文件夹下面新建spring.factories文件,文件中配置,key为自定配置类EnableAutoConfiguration的全路径,value是配置类的全路径。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ljw.TestConfig
重新打包,重新运行SpringBootStudyApplication这个类。可以发现,我们编写的那个类,就被加载进来了。
1. 在test-Core项目中的META-INF/增加配置文件,spring-autoconfigure-metadata.properties。
com.ljw.TestConfig.ConditionalOnClass=com.test.TestDemo
格式:自动配置的类全名.条件=值
上面这段代码的意思就是,如果当前的classpath下存在TestDemo,则会对TestConfig这个Configuration进行加载
如果不存在,我们需要根据下面的代码进行测试调试。
2. 运行调试
在分析AutoConfigurationImportSelector的源码时,会先扫描spring-autoconfiguration-metadata.properties文件,最后扫描spring.factories对应的类时,会结合前面的元数据spring-autoconfiguration-metadata.properties进行过滤。
原因是很多的@Configuration其实是依托于其他的框架来加载的,如果当前的classpath环境下没有相关联的依赖,则意味着这些类没必要进行加载,所以,通过这种条件过滤可以有效的减少@configuration类的数量从而降低SpringBoot的启动时间。
final class AutoConfigurationMetadataLoader {
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
private AutoConfigurationMetadataLoader() {
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
//........
//省略大部分代码
}
好了,到这里【详细学习SpringBoot自动装配原理分析之核心流程初解析-1】就先到这里,关于SpringBoot自动装配的原理后续不断更新创作中。