本文的代码demo全部在springboot-configuration-test.zip中,免积分下载,下载后直接运行。
1. SpringBoot自动装配示例
参考附件代码中com.lan.version0包
在resource下创建META-INF目录,然后在该目录下创建spring.factories文件,然后添加以下内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lan.version0.instance.InstanceA,\
com.lan.version0.instance.InstanceB
其中org.springframework.boot.autoconfigure.EnableAutoConfiguration是Spring的一个注解类,com.lan.version0.instance.InstanceA和com.lan.version0.instance.InstanceB是自定义的类,这两个类都没有加@Component注解。
InstanceA.java:
package com.lan.version0.instance;
public class InstanceA {
public InstanceA() {
System.out.println("version0.InstanceA 实例化");
}
}
InstanceB.java:
package com.lan.version0.instance;
public class InstanceB {
public InstanceB() {
System.out.println("version0.InstanceB 实例化");
}
}
上面的spring.factories配置中,SpringBoot启动后,会自动把com.lan.version0.instance.InstanceA和com.lan.version0.instance.InstanceB实例化成spring中的Bean。
这就是自动装配,自动把spring.factories中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的类实例化成Spring中的bean。为什么是org.springframework.boot.autoconfigure.EnableAutoConfiguration呢?这就需要了解自动装配的原理了。
2. 为什么需要自动装配
默认情况下,SpringBoot启动时,只会扫描到启动类下的包及其子包中的组件。像Apollo client、mybatis等第三方插件,其包名并不会和应用启动类下的包名一样,所以没法扫描到第三方插件包中的组件。这个时候,就需要一个装配机制,应用只需要引入第三方插件包,就能够自动扫描到第三方插件的组件并加载到spring中。以下通过渐进的方式讲解SpringBoot的自动装配机制。
3. BeanDefinition
Spring中的每个bean都会被封装成BeanDefinition对象,然后存放在一个BeanDefinitionMap中。BeanDefinition对象包含了对应bean的class对象、bean的名称、是否懒加载、scope、dependsOn等等属性。
4. @Import注解的作用
@Import注解在自动装配中起着关键作用,先了解一下该注解的几个作用。
4.1 注入bean
参考附件代码中com.lan.version1包
AppConfig.java
package com.lan.version1.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import com.lan.version1.instance.InstanceA;
import com.lan.version1.instance.InstanceB;
/**
* InstanceA和InstanceB没有加@Component,通过Import注解将其实例化成spring容器中的一个bean
* @author lan
*
*/
@Configuration
@Import({InstanceA.class, InstanceB.class})
public class AppConfig {
}
4.2 导入bean注册器ImportBeanDefinitionRegistrar
参考附件代码中的com.lan.version2包
AppConfig.java:
package com.lan.version2.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({AppImportBeanDefinitionRegistrar.class})
public class AppConfig {
}
AppImportBeanDefinitionRegistrar.java
package com.lan.version2.config;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import com.lan.version2.instance.InstanceA;
import com.lan.version2.instance.InstanceB;
public class AppImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// RootBeanDefinition是BeanDefinition的实现类
RootBeanDefinition beanDefinitionA = new RootBeanDefinition(InstanceA.class);
registry.registerBeanDefinition("instanceA", beanDefinitionA);
RootBeanDefinition beanDefinitionB = new RootBeanDefinition(InstanceB.class);
registry.registerBeanDefinition("instanceB", beanDefinitionB);
}
}
4.3 导入选择器ImportSelector
参考附件中的com.lan.version3包
AppConfig.java
package com.lan.version3.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({AppImportSelector.class})
public class AppConfig {
}
AppImportSelector.java
package com.lan.version3.config;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class AppImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 指定需要实例化的类
return new String[]{"com.lan.version3.instance.InstanceA", "com.lan.version3.instance.InstanceB"};
}
}
5. 自定义注解+@Import实现装配
参考附件中的com.lan.version4包
改进一下上面的例子:
将com.lan.version3.config.AppImportSelector的selectImports方法返回的类集合,改成从spring.factories中读取:
package com.lan.version4.config;
import java.util.List;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;
public class AppImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 从当前classpath查找所有的spring.factories文件,并查找key是com.lan.version4.config.EnableInstance的value列表
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableInstance.class, AppImportSelector.class.getClassLoader());
return StringUtils.toStringArray(configurations);
}
}
自定义@EnableInstance,通过@Import注解导入ImportSelector:
package com.lan.version4.config;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
/**
* 定义该注解,用于导入AppImportSelector
* @author lan
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AppImportSelector.class})
public @interface EnableInstance {
}
在启动类中激活@EnableInstance注解,从而导入ImportSelector:
package com.lan.version4;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.lan.version4.config.EnableInstance;
@SpringBootApplication
@EnableInstance // 激活该注解,从而使得导入ImportSelector
public class App {
public static void main(String[] args) {
new SpringApplication(App.class).run(args);
}
}
到这里,再结合org.springframework.boot.autoconfigure.EnableAutoConfiguration注解,自动装配的原理就已经清晰了。
6. @EnableAutoConfiguration注解激活自动装配
查看@SpringBootApplication注解,可以看到,里面包含了@EnableAutoConfiguration注解。
SpringBootApplication.java:
@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 {
// 省略...
}
查看@EnableAutoConfiguration注解,该注解使用@Import,导入了AutoConfigurationImportSelector。
EnableAutoConfiguration.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 省略...
}
查看AutoConfigurationImportSelector,该类实现了ImportSelector接口,其中覆盖的selectImports方法,就是获取所有当前classloader中的所有META-INF/spring.factories文件,并读取该文件的org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的类的集合作为返回值。
AutoConfigurationImportSelector.java:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// 省略...
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) { // 检查AutoConfiguration是否被关闭
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata); // 获取所有spring.factories中的org.springframework.boot.autoconfigure.EnableAutoConfiguration有效的配置类
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
// 省略...
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 获取所有spring.factories中的org.springframework.boot.autoconfigure.EnableAutoConfiguration配置类
configurations = removeDuplicates(configurations); // 去重
Set<String> exclusions = getExclusions(annotationMetadata, attributes); // 读取EnableAutoConfiguration注解的exclude属性
checkExcludedClasses(configurations, exclusions); 去掉classloader中没有的类
configurations.removeAll(exclusions); // 去掉exclude的类
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 读取所有spring.factories文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration值
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;
}
}
7. 总结
SpringBoot自动装配,实际上是通过@SpringBootApplication继承的@EnableAutoConfiguration+@Import导入ImportSelector,ImportSelector中读取了所有的spring.factories得到了需要实例化的bean的类集合,并将类集合实例化成bean。其中spring.factories配置的key就是org.springframework.boot.autoconfigure.EnableAutoConfiguration注解类。