@Import导入普通类分析
@Import导入普通类分析
复习下知识吧 这里只分析导入普通类 不实现那两个接口
分析以下代码中 ImportTestInner
是否会成为 Spring 容器中的 Bean:
@Configuration
public class ImportTest {
@Import({ImportTestBean.class})
class ImportTestInner{
}
}
关键机制
- 外层类触发扫描
@Configuration
(本质是@Component
)使 Spring 递归处理所有非静态内部类。 - 内部类的处理条件
满足以下任一条件即注册为 Bean: // 只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类- 直接标注
@Component
或派生注解(如@Service
) - 标注
@Configuration
(Full/Lite 模式)
- 直接标注
详细的源码分析在语雀:
@Import导入普通类分析
常见:
很显然 这两个都被加载到容器中了 ImportTest加了容器注解;ImportTestBean是被@import处理为配置类 也是一个bean;
这也是@import常见的写法

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import({ImportTestBean.class})
@Configuration
public class ImportTest {
}
public class ImportTestBean {
}
启动类:
@SpringBootApplication
public class MainClient {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(MainClient.class, args);
System.out.println(Arrays.toString(applicationContext.getBeanNamesForType(ImportTestBean.class)));
System.out.println(Arrays.toString(applicationContext.getBeanNamesForType(ImportTest.class)));
}
}
那这样写呢:
@Configuration
public class ImportTest implements ApplicationRunner {
@Autowired
ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(Arrays.toString(applicationContext.getBeanNamesForType(ImportTestBean.class)));
System.out.println(Arrays.toString(applicationContext.getBeanNamesForType(ImportTest.class)));
System.out.println(Arrays.toString(applicationContext.getBeanNamesForType(ImportTest.ImportTestInner.class)));
}
@Import({ImportTestBean.class})
class ImportTestInner{
}
}
很明显 三个都是bean,前两个没问题:其实在很早就是这样的逻辑 但是问了chatgpt也是说不会将第三个作为bean,垃圾

解析配置类步骤:
肯定是先解析springboot的启动类,因为有@ComponentScan
所以找到了其他的配置类 :也就是(这里省略其他的配置类)ImportTest,解析ImportTest,因为他是一个Configuration,走到processMemberClasses,在这里processMemberClasses将内部类解析出来
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first // 处理内部类 // 在解析一个配置类时,如果类上有@Component,其实不止这一个注解 则会判断内部类是不是lite配置类并进行解析,并且会记录为被导入的 processMemberClasses(configClass, sourceClass, filter); }
再次解析内部类:
do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass);
处理processImports方法时,又发现内部类ImportTestInner上有@import注解 ,处理import导入的类ImportTestBean,只是一个普通类所以->
解析ImportTestBean,终于sourceClass ==null了 ;依次将ImportTestBean、ImportTestInner、ImportTest作为一个配置类放入configurationClasses中
debug看源码的方式是很愚蠢的 要学会静态分析

关键点
就在第二个步骤的processMemberClasses的ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata())
/**
* 处理配置类中的成员类(内部类),递归解析符合条件的内部类配置
*
* @param configClass 当前正在处理的配置类
* @param sourceClass 配置类的源类表示(用于获取成员类)
* @param filter 类名过滤器,用于排除不需要处理的类
* @throws IOException 如果读取类信息时发生I/O错误
*/
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
// 获取当前配置类的所有成员类(内部类)
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
// 如果存在成员类则进行处理
if (!memberClasses.isEmpty()) {
// 候选配置类列表,用于存储符合条件的成员类
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
// 遍历所有成员类,筛选出需要处理的候选类
for (SourceClass memberClass : memberClasses) {
// 只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
// 对候选类进行排序(根据@Order注解或Ordered接口)
OrderComparator.sort(candidates);
// 处理每个候选成员类
for (SourceClass candidate : candidates) {
// 检查是否存在循环导入
if (this.importStack.contains(configClass)) {
// 如果检测到循环依赖,报告错误
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 将当前配置类压入导入栈(用于循环依赖检测)
this.importStack.push(configClass);
try {
// 递归处理成员类(将其转换为配置类并处理)
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
// 处理完成后从导入栈弹出当前配置类
this.importStack.pop();
}
}
}
}
}
ConfigurationClassUtils##
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
return hasBeanMethods(metadata);
}
那么接下来就会将配置类 加载为bean定义了:然后也会处理@Bean方法
ConfigurationClassPostProcessor#
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//...
this.reader.loadBeanDefinitions(configClasses);
//...
}
ConfigurationClassBeanDefinitionReader#
/**
* 为配置类加载相关的Bean定义
*
* @param configClass 要处理的配置类
* @param trackedConditionEvaluator 条件评估器,用于检查是否应该跳过该配置类
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 1. 检查是否应该跳过该配置类(根据@Conditional等条件注解)
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return; // 跳过后续处理
}
// 2. 前面说了导入的**关键点**,那几个注解 ,所以不只是@import 会走这里
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 3. 处理配置类中所有@Bean方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 4. 处理通过@ImportResource导入的XML配置资源
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 5. 处理通过ImportBeanDefinitionRegistrar注册的Bean定义
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
至于在主类包下的ImportTest 这些个类 当然是在解析启动类的@componentScan的时候 然后就开始在这里:this.componentScanParser.parse,doScan
啦 加载为bean定义的啦
ConfigurationClassParser#
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//...
// Process any @ComponentScan annotations
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
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();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
//...
}