• Spring @ComponentScans注解的解析


    @ComponentScans注解是组成@SpringBootApplication注解的之一。主要用于扫描项目中的组件,比如@Compoent``@Service等等,默认扫描路径是应用程序启动类的同级路径,可以添加应用的自定义路径。

    @ComponentScans注解是在 @Configuration前被解析。

    @ComponentScans注解是被ComponentScanAnnotationParser#parse()方法解析的。

    ComponentScanAnnotationParser

    先简单了解ComponentScanAnnotationParser这个类。
    成员变量:

    1. Environment environment 系统环境
    2. ResourceLoader resourceLoader 资源加载器
    3. BeanNameGenerator beanNameGenerator beanName生成策略
    4. BeanDefinitionRegistry registry bean工厂(DefaultListableBeanFactory)

    核心方法

    1. parse(AnnotationAttributes componentScan, final String declaringClass)
      componentScan@ComponentScan注解的11个属性。
      declaringClass是系统启动类的全限定名。
      -w787

    parse()方法主要是在构建一个ClassPathBeanDefinitionScanner类,在方法的最后会调用ClassPathBeanDefinitionScanner#doScan()方法扫描项目。

    其中doScan()方法的传参是扫描路径,默认是启动类的路径,也可以自定义,支持多个路径

        //basePackages扫描路径,如:com.kafkaproducer
    	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    		Assert.notEmpty(basePackages, "At least one base package must be specified");
    		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    		for (String basePackage : basePackages) {
    		      //1.这里会扫描路径下的所有类。如:/Users/XX/kafka-test/kafkaproducer/target/classes/com/kafkaproducer/KafkaProducerTest.class
    		      //2.把资源封装成一个SimpleMetadataReader
    		      //3.根据@ComponentScan的filter判断资源是否满足条件
    		      //4. 满足步骤3的资源,再进行一次筛选。最终筛选出来的类的注解都包含了@Component
    			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    			for (BeanDefinition candidate : candidates) {
    				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    				candidate.setScope(scopeMetadata.getScopeName());
    				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
    				//对扫描出来的candidate的BeanDefinition,设置一些参数。
    				if (candidate instanceof AbstractBeanDefinition) {
    					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
    				}
    				if (candidate instanceof AnnotatedBeanDefinition) {
    					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
    				}
    				
    				//检查beanName在注册表中是否存在(可能会有冲突)
    				//true:可以注册;false:bean已存在,(beanName相同,beanDefinition也相同)
    				if (checkCandidate(beanName, candidate)) {
    					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
    					   //是否通过代理创建
    					definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    					beanDefinitions.add(definitionHolder);
    					
    					//注册beanDefinition(注册表缓存)
    					registerBeanDefinition(definitionHolder, this.registry);
    				}
    			}
    		}
    		return beanDefinitions;
    	}
    

    总结

    扫描注解@ComponentScans默认会根据应用程序启动类所在的根路径为默认扫描路径(参考:classpath:com/kafkaproducer/**/.class),也可以通过指定其他路径(比如项目中的DAO层是通过jar包的形式引入进来的,可以指定路径扫描DAO的组件)。负责扫描的类--ClassPathBeanDefinitionScanner会扫描项目的所有class文件,再经过Filter过滤,得到所有被@Component注解修饰的类。最后把相关类的beanDefiniton存入缓存中。

  • 相关阅读:
    服务器启动jar包详解
    linux环境安装Go环境
    crt修改默认颜色
    java调用python脚本没有返回值问题
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of typeavailable
    markdown个人使用总结
    windows配置免密登录linux
    idea远程debug项目
    dubbo图形化控制台
    java.lang.IllegalArgumentException: Could not resolve placeholder
  • 原文地址:https://www.cnblogs.com/-1007813544/p/spring-componentscans-zhu-jie-de-jie-xi.html
Copyright © 2020-2023  润新知