一、什么是自动装配SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到Spring容器,并执行类中定义的各种操作。对于外部jar包来说,只需要按照SpringBoot定义的标准,就能将自己的功能装配到SpringBoot中。
二、自动装配的实现原理自动装配的实现,离不开SpringBootApplication这个核心注解。查看这个注解的源码,我们会发现在SpringBootApplication注解上,存在着几个注解,其中SpringBootConfiguration、EnableAutoConfiguration、ComponentScan这三个注解是需要我们注意的。 @SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
(1) ComponentScan 扫描被@Component 、@Service注解的bean,该注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如SpringBootApplication注解源码所示,容器中将排除TypeExcludeFilterh和AutoConfigurationExcludeFilter。
(2) EnableAutoConfiguration 启用 SpringBoot 的自动配置机制
(3) Configuration 允许在上下文中注册额外的 bean 或导入其他配置类
2.1 EnableAutoConfiguration详解@EnableAutoConfiguration是实现自动装配的重要注解,在这个注解上存在以下两个注解:AutoConfigurationPackage、Import。 @AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
2.1.1 AutoConfigurationPackage表示对于标注该注解的类的包,应当使用AutoConfigurationPackages注册。实质上,它负责保存标注相关注解的类的所在包路径。使用一个BasePackage类,保存这个路径。然后使用@Import注解将其注入到ioc容器中。这样,可以在容器中拿到该路径。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
}
查看AutoConfigurationPackages中的Registrar这个类的源码,在Registrar#registerBeanDefinitions方法中有这样一句代码new PackageImport(metadata).getPackageName(),查看PackageImport的构造器后不难发现,这里获取的是StandardAnnotationMetadata这个实例所在的包名。
PackageImport(AnnotationMetadata metadata) {
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
}
此时再回去看Registrar#registerBeanDefinitions中调用的AutoConfigurationPackages#register方法
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition
.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0,
addBasePackages(constructorArguments, packageNames));
} else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
2.1.2 Import(AutoConfigurationImportSelector.class)它是利用AutoConfigurationImportSelector机制再来给容器中批量导入一些配置东西的,接下来带大家了解究竟导入了哪些内容。
@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());
}
接下来重点看getAutoConfigurationEntry(annotationMetadata)方法,利用这个方法向容器中批量导入一些默认支持自动配置的类,当你理解了这部分内容之后,就基本了解了Spring Boot是如何进行自动装配的,废话不多说,让我们进入正题。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
|