@EnableAutoConfiguration介绍
自动装配在Spring Boot中是通过@EnableAutoConfiguration注解来开启的,这个注解的声明在启动类注解@SpringBootApplication内。 进入@SpringBootApplication注解,可以看到@EnableAutoConfiguration注解的声明。
其实Spring 3.1版本就已经支持@Enable注解了,它的主要作用把相关组件的Bean装配到IoC容器中。
**@Enable注解对JavaConfig的进一步完善,为使用Spring Framework的开发者减少了配置代码量,降低了使用的难度。**比如常见的@Enable注解有@EnableWebMvc、@EnableScheduling等。
如果基于JavaConfig的形式来完成Bean的装载,则必须要使用@Configuration注解及@Bean注解。而@Enable本质上就是针对这两个注解的封装,所以大家如果仔细关注过这些注解,就不难发现这些注解中都会携带一个@Import注解,比如@EnableScheduling:因此,使用@Enable注解后,Spring会解析到@Import导入的配置类,从而根据这个配置类中的描述来实现Bean的装配,那么@EnableAutoConfiguraion实现原理是不是也一样呢?
进入@EnableAutoConfiguration注解里,可以看到除@Import注解之外,还多了一个@AutoConfigurationPackage注解(它的作用是把使用了该注解的类所在的包及子包下所有组件扫描到Spring IoC容器中)。并且,**@Import注解中导入的并不是一个Configuration的配置类,而是一个AutoConfigurationImportSelector类。**从这一点来看,它就和其他的@Enable注解有很大的不同。
@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 {
};
}
AutoConfigurationImportSelector
AutoConfigurationImportSelector源码:
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
private static final String[] NO_IMPORTS = {
};
private static final Log logger = LogFactory
.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
....
}
它实现了DeferredImportSelector,接口,而DeferredImportSelector又继承了ImportSelector,
package org.springframework.context.annotation;
import org.springframework.core.type.AnnotationMetadata;
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
它只有一个selectImports抽象方法,并且返回一个String数组,在这个数组中可以指定需要装配到IoC容器的类,当在@Import中导入一个ImportSelector的实现类之后,会把该实现类中返回的Class名称都装载到IoC容器中。 和@Configuration不同的是,ImportSelector可以实现批量装配,并且还可以通过逻辑处理来实现Bean的选择性装配,也就是可以根据上下文来决定哪些类能够被IoC容器初始化。
其中DeferredImportSelector有一个方法:
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
其执行逻辑是,当实现DeferredImportSelector类重写了
public Class<? extends Group> getImportGroup()
方法时,则在此类被@Import注解导入时执行getImportGroup方法,如果没有实现getImportGroup方法,则执行
public String[] selectImports(AnnotationMetadata importingClassMetadata)
例子:使用importSelector
1.先创建两个类
public class FirstClass {
}
public class SecondClass {
}
2.我们需要把这两个类装配到IoC容器中。 ·
创建一个ImportSelector的实现类,在实现类中把定义的两个Bean加入String数组,这意味着这两个Bean会装配到IoC容器中。 ·
/**
* @Author
* @Description importselector实现类,用于把自定义的类加入bean数组
* @Date
**/
public class GpImportSelector implements ImportSelector {
@Override
public String [] selectImports(AnnotationMetadata metadata)
{
return new String[]{
FirstClass.class.getName(),SecondClass.class.getName()};
}
}
3.为了模拟EnableAutoConfiguration,我们可以自定义一个类似的注解,通过@Import导入GpImportSelector。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(GpImportSelector.class)
public @interface MyEnableleImport {
}
4.· 创建一个启动类,在启动类上使用@EnableAutoImport注解后,即可通过getBean从IoC容器中得到FirstClass对象实例。
@MyEnableleImport
@SpringBootApplication
public class HwMusicApplication {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ca = SpringApplication.run(HwMusicApplication.class, args);
FirstClass firstClass = ca.getBean(FirstClass.class);
firstClass.get();//自己随意写的一个方法
}
}
这种实现方式相比@Import(*Configuration.class)的好处在于装配的灵活性,还可以实现批量。比如GpImportSelector还可以直接在String数组中定义多个Configuration类,由于一个配置类代表的是某一个技术组件中批量的Bean声明,所以在自动装配这个过程中只需要扫描到指定路径下对应的配置类即可。
自动装配原理分析
自动装配的核心是扫描约定目录下的文件进行解析,解析完成之后把得到的Configuration配置类通过ImportSelector进行导入,从而完成Bean的自动装配过程。那么接下来我们通过分析AutoConfigurationImportSelector的实现来求证这个猜想是否正确。
定位到AutoConfigurationImportSelector中的selectImports方法,它是ImportSelector接口的实现,这个方法中主要有两个功能:
· AutoConfigurationMetadataLoader.loadMetadata
从META-INF/spring-autoconfigure-metadata.properties中加载自动装配的条件元数据,简单来说就是只有满足条件的Bean才能够进行装配。
· 收集所有符合条件的配置类
autoConfigurationEntry.getConfigurations(),完成自动装配。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
该类的其他方法:
· getAttributes获得@EnableAutoConfiguration注解中的属性exclude、excludeName等。 ·
getCandidateConfigurations获得所有自动装配的配置类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), 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;
}
· removeDuplicates去除重复的配置项。
· getExclusions根据@EnableAutoConfiguration注解中配置的exclude等属性,把不需要自动装配的配置类移除。 ·
fireAutoConfigurationImportEvents广播事件。
· 最后返回经过多层判断和过滤之后的配置类集合。
总的来说,它先获得所有的配置类,通过去重、exclude排除等操作,得到最终需要实现自动装配的配置类。这里需要重点关注的是getCandidateConfigurations,它是获得配置类最核心的方法。 这里用到了SpringFactoriesLoader,它是Spring内部提供的一种约定俗成的加载方式,类似于Java中的SPI。简单来说,它会扫描classpath下的META-INF/spring.factories文件,spring.factories文件中的数据以Key=Value形式存储,而SpringFactoriesLoader.loadFactoryNames会根据Key得到对应的value值。因此,在这个场景中,Key对应为EnableAutoConfiguration,Value是多个配置类,也就是getCandidateConfigurations方法所返回的值。
总结
▶▶▶▶@EnableAutoConfiguraion上有一个@import注解,@Import注解中导入的并不是一个Configuration的配置类,而是一个AutoConfigurationImportSelector类,AutoConfigurationImportSelectori 实现了DeferredImportSelector, DeferredImportSelector继承了importSelector接口,实现了importSelector接口 重写了selectImports的类可以批量装载bean到ioc容器中
▶▶▶▶· 通过Spring提供的SpringFactoriesLoader机制,扫描classpath路径下的META-INF/spring.factories,读取需要实现自动装配的配置类。 ·
▶▶▶▶通过条件筛选的方式,把不符合条件的配置类移除,最终完成自动装配。 (@Conditional条件装配)