@ComponentScan组件扫描原理(二)
接着之前的处理,在拿到注解元信息之后,我们可以进一步处理,并生成Bean。下面是完整的组件扫描解析流程。
public static void main(String[] args) throws IOException {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);/* context.registerBean(ConfigurationClassPostProcessor.class);//@ComponentScan @Bean @Import注解 @ImportResource注解context.registerBean(MapperScannerConfigurer.class,beanDefinition -> {beanDefinition.getPropertyValues().add("basePackage","com.example.demo2.b03.mapper");});*/ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class,ComponentScan.class);if (componentScan!= null) {for (String p : componentScan.basePackages()){System.out.println(p);String path = "classpath*:"+p.replace(".","/")+"/**/*.class";System.out.println(path);CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();Resource[] resources = context.getResources(path);for (Resource resource : resources){//System.out.println(resource);MetadataReader reader = factory.getMetadataReader(resource);//System.out.println("类名:"+reader.getClassMetadata().getClassName());AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();/* System.out.println("是否加了@Component注解:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));System.out.println("是否加了@Component派生注解:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));*/if(annotationMetadata.hasAnnotation(Component.class.getName())|| annotationMetadata.hasMetaAnnotation(Component.class.getName())){AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();DefaultListableBeanFactory defaultListableBeanFactory = context.getDefaultListableBeanFactory();String name = generator.generateBeanName(bd, defaultListableBeanFactory);defaultListableBeanFactory.registerBeanDefinition(name,bd);}}}}//初始化容器context.refresh();for (String name:context.getBeanDefinitionNames()){System.out.println(name);}//销毁容器context.close();}
结果输出如下:
bean2和bean3被成功扫描并注入到Spring容器中了。进一步提取主类的处理方法,抽象成一个BeanFactoryPostProcessor。
package com.example.demo2.b03;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;import java.io.IOException;/*** @author zhou* @version 1.0* @description TODO* @date 2025/10/7 21:22*/
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {try {ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class,ComponentScan.class);if (componentScan!= null) {for (String p : componentScan.basePackages()){System.out.println(p);String path = "classpath*:"+p.replace(".","/")+"/**/*.class";System.out.println(path);CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);for (Resource resource : resources){//System.out.println(resource);MetadataReader reader = factory.getMetadataReader(resource);//System.out.println("类名:"+reader.getClassMetadata().getClassName());AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();/* System.out.println("是否加了@Component注解:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));System.out.println("是否加了@Component派生注解:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));*/if(annotationMetadata.hasAnnotation(Component.class.getName())|| annotationMetadata.hasMetaAnnotation(Component.class.getName())){AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();//DefaultListableBeanFactory defaultListableBeanFactory = context.getDefaultListableBeanFactory();if(configurableListableBeanFactory instanceof DefaultListableBeanFactory beanFactory){String name = generator.generateBeanName(bd, beanFactory);beanFactory.registerBeanDefinition(name,bd);}}}}}} catch (IOException e) {e.printStackTrace();}}
}
自定义的ComponentScanPostProcessor类实现BeanFactoryPostProcessor接口并重写方法,里面的逻辑就是我们在主方法里面写的处理逻辑。
public static void main(String[] args) throws IOException {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);/* context.registerBean(ConfigurationClassPostProcessor.class);//@ComponentScan @Bean @Import注解 @ImportResource注解context.registerBean(MapperScannerConfigurer.class,beanDefinition -> {beanDefinition.getPropertyValues().add("basePackage","com.example.demo2.b03.mapper");});*/context.registerBean(ComponentScanPostProcessor.class);//初始化容器context.refresh();for (String name:context.getBeanDefinitionNames()){System.out.println(name);}//销毁容器context.close();}
只要把我们自定义的工厂后处理器注入到Spring容器中,便可以实现组件扫描。