装配组件的三种方式:
第三种模块装配主要用于解决注册过多,导致编码成本高,维护不灵活的问题(如果只是用@Component、@Configuration、@Bean这三种注解的话,很容易造成这些问题)
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** 用于导入@Configuration标注的类、ImportSelector实现类、ImportBeanDefinitionRegistrar实现类、普通的类 * {@link Configuration @Configuration}, {@link ImportSelector}, * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import. */ Class<?>[] value(); } 复制代码
从@Import的注释看出,它主要用于导入:
新建一个 @EnableColorConfig,使用@Import标记,在@Import上指定需要导入的配置
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import({Red.class, ColorRegistrarConfiguration.class, ColorImportSelector.class, ColorImportBeanDefinitionRegisrar.class}) public @interface EnableColor { } 复制代码
在这里,Red.class, ColorRegistrarConfiguration.class, ColorImportSelector.class, ColorImportBeanDefinitionRegisrar.class 分别对应着普通类、@Configuration标注的配置类、ImportSelector实现类、ImportBeanDefinitionRegistrar实现类
public class Red { private String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } } @Configuration public class ColorRegistrarConfiguration { @Bean public Yellow yellow() { return new Yellow(); } } public class ColorImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{Blue.class.getName(), Green.class.getName()}; } } public class ColorImportBeanDefinitionRegisrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { registry.registerBeanDefinition("black", new RootBeanDefinition(Black.class)); } } 复制代码
对于一个springboot应用来说,我们只需要定义一个启动类即可,如下
// 1. 添加 @SpringBootApplication 注解标记 @SpringBootApplication public class SpringbootDemoApplication { public static void main(String[] args) { // 2. 传入主配置类(在springboot内部称为 primarySource )调用 SpringApplication.run 方法 SpringApplication.run(SpringbootDemoApplication.class, args); } } 复制代码
这样就构造了一个springboot应用。那么它是如何简化掉传统spring应用的各种配置的呢?我们从注解 @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 { // 省略代码 } 复制代码
发现这是一个复合注解,逐个来看看
SpringBootConfiguration,一个使用 @Configuration 标注的复合注解,用于声明这是一个springboot 应用的配置类
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { } 复制代码
EnableAutoConfiguration,从名字上看,该注解负责启用自动化配置。自身也是一个复合注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { // 省略代码 } 复制代码
由于 @EnableAutoConfiguration 本身又由注解 @AutoConfigurationPackage 标注,而且导入了AutoConfigurationImportSelector 配置类。详细下分析它的作用。
首先是 @AutoConfigurationPackage 注解,通过 @Import 导入了ImportBeanDefinitionRegistrar实现类 AutoConfigurationPackages.Registrar
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { } 复制代码
AutoConfigurationPackages.Registrar
/** * 保存包的根路径 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing * configuration. */ static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 将启动类包名称设置到 BasePackages 类中,并且将 BasePackages 注册到 beanfactory register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } } 复制代码
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); } } 复制代码
BasePackages,对于它来说,自身被注册到 beanfactory 中,并且属性packages保存着应用启动类所在的包路径。
提供了 get 方法来获取这个包路径,方便在后续集成其他第三方组件时可以获取到这个包路径
static final class BasePackages { private final List<String> packages; private boolean loggedBasePackageInfo; BasePackages(String... names) { List<String> packages = new ArrayList<>(); for (String name : names) { if (StringUtils.hasText(name)) { packages.add(name); } } this.packages = packages; } public List<String> get() { if (!this.loggedBasePackageInfo) { if (this.packages.isEmpty()) { if (logger.isWarnEnabled()) { logger.warn("@EnableAutoConfiguration was declared on a class " + "in the default package. Automatic @Repository and " + "@Entity scanning is not enabled."); } } else { if (logger.isDebugEnabled()) { String packageNames = StringUtils.collectionToCommaDelimitedString(this.packages); logger.debug("@EnableAutoConfiguration was declared on a class " + "in the package '" + packageNames + "'. Automatic @Repository and @Entity scanning is " + "enabled."); } } this.loggedBasePackageInfo = true; } return this.packages; } } 复制代码
由 ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry 方法触发 org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process 加载。
// AutoConfigurationImportSelector.AutoConfigurationGroup#process @Override public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) // 加载自动化配置类 .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } } 复制代码
最终会调用到以下方法,经过 SpringFactoriesLoader.loadFactoryNames 方法从类路径中寻找 META-INF/spring.factories 文件,获取到key为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有value。
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; } protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } 复制代码
至于SpringFactoriesLoader的原理,其本质就是使用 ClassLoader 到类路径下加载文件 META-INF/spring.factories,解析为 key-value 的形式。类似于JDK 的 SPI 机制。