经常面试的时候会被问到了解SpringBoot的自动配置原理吗?大部分都是向下面这边解释其实并不了解其中的原理
@SpringBootApplication ---> @EnableAutoConfiguration --> @Import({AutoConfigurationImportSelector.class})
但是如果面试官进一步的追问 知道什么时候处理 @Import 吗 ? 或者AutoConfigurationImportSelector 的selectImports方法是什么时机被执行的吗?
估计就凉凉了
下面来认识一下@Import
@Import是在org.springframework.context.annotation包中的
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
Spring中@Import注解一般是和 ImportSelector 和 ImportBeanDefinitionRegistrar 这两个接口配合使用的,后面会解释原因
当然也是可以单独使用
1 简单定义几个Bean
public class ImportBean1 {
}
public class ImportBean2 {
}
public class ImportBean3 {
}
2 定义配置类
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.xxx.service.ImportBean2"};
}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(ImportUserService.class);
registry.registerBeanDefinition("importUserService",rootBeanDefinition);
}
}
3 定义启动配置类
@Import({ImportBean1.class,MyImportBeanDefinitionRegistrar.class, MyImportSelector.class})
public class AppConfig {
}
4 定义启动类和验证
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
ImportBean3 importBean3 = applicationContext.getBean("importBean3",ImportBean3.class);
System.out.println(importBean3);
ImportBean1 importBean1 = applicationContext.getBean( ImportBean1.class);
System.out.println(importBean1);
ImportBean2 importBean2 = applicationContext.getBean( ImportBean2.class);
System.out.println(importBean2);
}
}
经过验证都是可以获取到ImportBean1/ImportBean2/importBean3
原理解释:
其实@Import能够生效,主要依靠于Spring一个非常重要的BeanFactoryPostProcessor的实现 ConfigurationClassPostProcessor的后置
处理器的核心方法org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); // 寻找配置类对应的BeanDefinition然后进行处理 // 比如开始扫描 processConfigBeanDefinitions(registry); }
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); ......... do { // 对配置BeanDefinition进行解析,解析完后会生成ConfigurationClass parser.parse(candidates); //AppConfig.class parser.validate(); .......... } while (!candidates.isEmpty());
上面省略的一些调用栈,应该很容易跟 protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // 1. 如果配置bean上有@Component注解,递归去解析内部类上的注解 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); } // Process any @PropertySource annotations // 2. 解析@PropertySource注解 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 // 3. 解析@ComponentScan注解,并进行扫描 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 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 // 扫描得到BeanDefinition 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(); } // 检查扫描所得到BeanDefinition是不是配置Bean,基本上都有@Component注解,所以都是配置类 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations // 4. 解析@Import,getImports方法返回AppConfig.class上定义的Import注解中所导入的类的信息 processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations // 5. 解析@ImportResource,得到导入进来的spring的xml配置文件,然后解析 AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods // 6. 解析配置类中的加了@Bean注解的方法 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces // 7. 如果配置类实现了某个接口,那么则解析该接口中的加了@Bean注解的默认方法 processInterfaces(configClass, sourceClass); // Process superclass, if any // 8. 如果有父类,则返回父类给上层遍历进行处理 if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }
上面代码中的第4步就是处理AppConfig上的@Import的,里面的代码相信你稍微跟一些 就应该能够明白,我比较懒所以就不多说了
参考:https://blog.csdn.net/baidu_37107022/article/details/88962331