序言
本篇文章主要介绍@Import注解的解析,稍有不同的是我们将要在SpringBoot的启动流程中介绍@Impot注解的原理,并连带着将SpringBoot的自动装配原理也稍加介绍。
SpringBootApplication
这里我是创建了2.3.12版本的SpringBoot,没有额外配置,直接看启动类注解@SpringBootApplication即可。
启动类
我们都知道启动类中核心注解@SpringBootApplication里一共包含3个重要的注解,分别是@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
@SpringBootApplication
public class BootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(BootdemoApplication.class, args);
}
}
// 省略部分注解...
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//省略部分源码...
}
EnableAutoConfiguration
而我们需要着重关注的是@EnableAutoConfiguration注解。其中@Import注解在解析是会引入AutoConfigurationImportSelector.class
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
AutoConfigurationPackage
@AutoConfigurationPackage中也包含@Import注解,会引入AutoConfigurationPackages.Registrar.class,简单介绍一下,不用太关注。
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
流程图
run()
主方法运行前先简单回顾一下我们是如何一步一步创建的ConfigurationClassPostProcessor类。
@ComponentScan注解、context:component-scan解析 ——> 注册BeanDefinition(ConfigurationClassPostProcessor impl BDRPP) ——> invokeBeanFactoryPostProcessors方法 (因为ConfigurationClassPostProcessor 实现了BDRPP类) ——> postProcessBeanDefinitionRegistry(处理@Import、@Bean、@Component等注解)。
run()
回顾完流程,看下SpringBoot的run()是如何调用的。
public static void main(String[] args) {
SpringApplication.run(BootdemoApplication.class, args);
}
省略中间步骤,可以看到run()方法中调用了很多方法,而调用的众多方法中有一个 this.refreshContext(context); 方法。点进去会发现这个方法就是我们Spring中调用的主流程refresh()方法。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var9) {
this.handleRunFailure(context, var9, listeners);
throw new IllegalStateException(var9);
}
try {
listeners.running(context);
return context;
} catch (Throwable var8) {
this.handleRunFailure(context, var8, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var8);
}
}
invokeBeanFactoryPostProcessors()
主流程的调用步骤不再这里过多赘述,我们直接看invokeBeanFactoryPostProcessors()方法,打上断点可以看到获取BeanDefinitionRegistryPostProcessor类型的类中已经包含了ConfigurationClassPostProcessor。
@Import
代码继续向下执行,解析完@Component、@PropertySources等注解后来到了@Import注解的解析。
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//如果是@Component注解,则优先递归处理内部类
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
//获取BeanDefinition中包含ComponentScan注解的集合
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
//1.componentScans不为null
//2.根据@Conditional注解判断是否应该跳过
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
//解析@ComponentScan注解
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
//如果根据@ComponentScan注解获取到的beanDefinition中有@Configuration、@Import、@Bean等注解,则递归处理
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
//@Import注解解析
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
//省略部分代码。
}
getImports
看getImports()方法之前先看看参数sourceCalss是什么。
我们是获取到所有符合条件的BeanDefinition后进行遍历,而我们的启动类的注解嵌套中包含@Configuration等注解,所以一定会获取到启动类。
当前sourceClass为启动类BootdemoApplication,而getImports()会递归处理,收集类中带有@Import注解类的并获取其@Import中value属性值。
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
//创建集合,用来存放包含@Import注解的类
Set<SourceClass> imports = new LinkedHashSet<>();
//创建集合,用来存放处理过的类(防止无限递归)
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
//如果该类没有被处理过,则进行处理
if (visited.add(sourceClass)) {
//遍历该类上的所有注解
for (SourceClass annotation : sourceClass.getAnnotations()) {
//获取注解类名
String annName = annotation.getMetadata().getClassName();
//如果注解类名不是@Import,则继续递归遍历该类上的注解
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
//获取该类上的@Import注解的value属性值
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
处理完成后,SpringBootApplication启动类中包含@Import注解的类共两个,所以imports集合也有两个元素,刚好对应了我们上面流程图中所画的包含@Import的类。
processImports
接着向下看处理@Import的主流程方法。
流程图
源码
在流程图和源码中可以看到在处理@Import的过程中,会根据类的归属不同而有不同的逻辑,在我们getImport方法中添加到imports集合的两个类分别属于**ImportSelector(AutoConfigurationImportSelector.class)**和 ImportBeanDefinitionRegistrar (AutoConfigurationPackages.Registrar.class)
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
//如果该类已经存在于导入栈(importStack)中,则说明存在循环导入
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
//压入importStack,防止循环导入
this.importStack.push(configClass);
try {
//遍历每一个@Import注解
for (SourceClass candidate : importCandidates) {
//如果是ImportSelector的实现类
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
// 如果候选的类是一个ImportSelector , 则委托它来确定是否导入
Class<?> candidateClass = candidate.loadClass();
//反射实例化ImportSelector
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
//获取ImportSelector的排除过滤器
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
//如果是DeferredImportSelector的实现类,放入延迟导入处理器中(预留到所有配置类加载完成后统一处理)
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 获取引入的类,然后使用递归方式将这些类中同样添加了@Import注解引用的类
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归处理,被Import进来的类也有可能@Import注解
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
//如果是ImportBeanDefinitionRegistrar的实现类
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
// 如果候选的类是一个ImportBeanDefinitionRegistrar,则委托它来注册额外的bean定义
Class<?> candidateClass = candidate.loadClass();
//反射实例化ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
//将ImportBeanDefinitionRegistrar和当前类添加到ConfigurationClass中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
// 如果候选的类既不是ImportSelector也不是ImportBeanDefinitionRegistrar,则将其作为@Configuration类进行处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
/**
* 如果Import的类型是普通类,则将其当作带有@Configuration的类一样处理
* 将candidate构造为ConfigurationClass,标注为importedBy,意味着它是通过被@Import进来的
* 后面处理会用到这个判断将这个普通类注册进DefaultListableBeanFactory
*/
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
// 省略catch部分
finally {
this.importStack.pop();
}
}
}
上面的方法中只是获取到了@Import注解中对应value,按照类的实现进行了分类,但并没有对具体的类做任何的处理。
这里我们重点关注 AutoConfigurationImportSelector 类,因为是 DeferredImportSelector 的实现类,所以会添加到deferredImportSelectorHandler集合中。
类图关系
process
添加到deferredImportSelectorHandler中的Import什么时候处理?
parse()方法的底层调用的是doProcessConfigurationClass(),就是我们处理@Component、@Bean、@Import的主方法,所以在此处的调用就是在所有配置类加载完成后统一处理。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
// 省略catch部分
}
this.deferredImportSelectorHandler.process();
}
process
process方法的底层就是SpringBoot自动装配的原理。
顺着 process —》 processGroupImports —》getImports —》 process —》getAutoConfigurationEntry —》getCandidateConfigurations方法的执行顺序看下来,会发现结尾会显式的调用getCandidateConfigurations方法。
所以我们直接看getCandidateConfigurations方法源码。
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach(entry -> {
//省略部分源码....
});
}
}
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
//省略部分源码....
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//省略部分源码...
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//省略部分源码
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
return configurations;
}
自动装配
getSpringFactoriesLoaderFactoryClass
将EnableAutoConfiguration.class作为参数传入loadFactoryNames方法中。
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
loadSpringFactories
加载META-INF/spring.factories路径文件并放入缓存中。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//如果缓存中存在,则直接返回
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//url:META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
//将Url转换成Resource便于加载
UrlResource resource = new UrlResource(url);
//加载properties
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
//遍历properties放入缓存中
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
}
loadFactoryNames
将参数EnableAutoConfiguration.class作为key,从缓存中获取,如果不存在则返回一个默认的空LIst。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
我们来看一看spring.factories文件中都有什么
所以通过EnableAutoConfiguration.class从缓存中获取到的就是要自动装配时加载的类。
此处先卖个关子,更详细的流程等到SpringBoot篇章再展开。接下来我们总结一下整体的流程。
总结
流程图