Spring IoC component-scan 節點詳解

前言

咱們在瞭解 Spring 容器的擴展功能 (ApplicationContext) 以前,先介紹下 context:component-scan 標籤的解析過程,其做用很大是註解能生效的關鍵所在。html

正文

咱們這次直接從 BeanDefinitionParseDelegate#parseCustomElement() 開始往下分析,不知道前面流程的能夠看一下 [Spring XML Bean 定義的加載和註冊](https://leisurexi.github.io/category/2020/04/14/Spring IoC/Spring XML Bean 定義的加載和註冊.html) 這篇文章,以前的流程都有解析。java

BeanDefinitionParseDelegate#parseCustomElement()

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    // 獲取自定義標籤的命名空間
    String namespaceUri = getNamespaceURI(ele); 
    if (namespaceUri == null) {
        return null;
    } 
    // 獲取自定義標籤的處理器,這裏解析的是context:component-scan,因此獲取的是ContextNamespaceHandler
    NamespaceHandler handler =
        this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    } 
    // 進行標籤的解析,見下文詳解
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

ComponentScanBeanDefinitionParser#parse

public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 獲取屬性basePacket的值
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    // 解析佔位符,例如${basePackage}
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    // 解析base-package(容許經過 ",; \t\n" 中的任一符號填寫多個),例如: com.leisurexi.one;com.leisurexi.two
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

    // Actually scan for bean definitions and register them.
    // 構建和配置ClassPathBeanDefinitionScanner,見下文詳解
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    // 使用scanner在指定的包路徑進行掃描,返回註冊後的BeanDefinition,見下文詳解
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    // 註冊組件(包括一些內部註解的後置處理器),見下文詳解
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

    return null;
}

ComponetScanBeanDefinitionParser#configureScanner

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
    // 是否使用默認的過濾器,默認爲true
    boolean useDefaultFilters = true;
    // 若是設置了use-default-filters屬性,則使用設置的值
    if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
        useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
    }

    // Delegate bean definition registration to scanner class.
    // 構建ClassPathBeanDefinitionScanner,將bean定義註冊委託給scanner類,見下文詳解
    ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
    scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
    scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
    // 解析resource-pattern屬性
    if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
        scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
    }

    try { 
        // 解析name-generator屬性
        parseBeanNameGenerator(element, scanner);
    }
    catch (Exception ex) {
        parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }

    try { 
        // 解析scope-resolver、scoped-proxy屬性
        parseScope(element, scanner);
    }
    catch (Exception ex) {
        parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
    // 解析類型過濾器,見下文詳解
    parseTypeFilters(element, scanner, parserContext);

    return scanner;
}

ComponentScanBeanDefinitionParser#createScanner

protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
    return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,readerContext.getEnvironment(), readerContext.getResourceLoader());
}

// ClassPathBeanDefinitionScanner.java
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
    // 若是使用默認的過濾器,註冊默認的過濾器,見下文詳解
    if (useDefaultFilters) {
        registerDefaultFilters();
    }
    setEnvironment(environment);
    setResourceLoader(resourceLoader);
}

ClassPathScanningCandidateComponentProvider#registerDefaultFilters

protected void registerDefaultFilters() { 
    // 將註解@Component添加到includeFilters中,這將隱式的註冊全部@Component的派生註解,例如@Repository、@Service、@Controller
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try { 
        // 若是當前環境中有@ManagedBean,添加到includeFilters中,不然會catch未找到類的異常並忽略
        this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }
    try { 
        // 若是當前環境中有@Named,添加到includeFilters中,不然會catch未找到類的異常並忽略
        this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

ClassPathBeanDefinitionScanner#doScan

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) {
        // 尋找包路徑下符合要求的bean定義(最多見的就是使用@Component標註的類)
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            // 解析@Scope註解,沒有標註默認爲singleton做用域
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成bean的名稱,見下文詳解
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                // beanDefinition的後置處理,就是設置beanDefinition的一些默認屬性,如autowireMode、initMethod等,而且設置AutowireCandidate,
                // 通常爲true表明能夠自動裝配到其餘bean中
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                // 處理公共註解,見下文詳解
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 若是beanDefinition經過檢查(大多時候是檢查是否是第一次註冊),則將definitionHolder添加進註冊中心中,若是有同名的bean在這裏會直接拋出異常
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                    AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

ClassPathScanningCandidateComponentProvider#findCandidateComponents

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else { 
        // 掃描符合條件的組件
        return scanCandidateComponents(basePackage);
    }
}

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // 將要掃描的包的路徑拼成完整的,例如:com.leisurexi.ioc.context 會被拼成 classpath*:com/leisurexi/ioc/context/**/*.class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 獲取路徑下的全部類的資源
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 
        for (Resource resource : resources) {
            // 若是資源是可讀的
            if (resource.isReadable()) {
                try {
                    // 使用metadataReader讀取資源,MetadataReader是專門用來訪問元數據的類(包括: 類元數據ClassMetadata、註解元數據AnnotationMetadata等)
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    // 判斷該類是否是候選的組件
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        // 判斷是不是候選組件,默認條件是class不是接口而且不依賴於內部類
                        if (isCandidateComponent(sbd)) { 
                            candidates.add(sbd);
                        }	
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
}

上面代碼中判斷該類是否是候選的組件的方法 isCandidateComponent(metadataReader) 通常狀況下 是判斷該類是否標註了上文中 ClassPathScanningCandidateComponentProvider#registerDefaultFilters() 中的註解 @Component@ManagedBean@Named@Component 註解是確定會存在與當前環境中的,因此標註了 @Component 或者其派生的註解如:@Repository@Service@Controller 都會被當作候選組件。git

AnnotationBeanNameGenerator#generateBeanName

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    // 若是BeanDefinition是AnnotatedBeanDefinition類型
    if (definition instanceof AnnotatedBeanDefinition) { 
        // 獲取自定義的beanName,如 @Component註解的value值
        String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
        // 若是不爲空,而且不是空字符串
        if (StringUtils.hasText(beanName)) { 
            // Explicit bean name found.
            // 返回明確的beanName
            return beanName; 
        }
    }
    // Fallback: generate a unique default bean name. 
    // 沒有手動指定beanName,默認生成一個,是當前類名的首字母小寫,如User類的beanName是user
    return buildDefaultBeanName(definition, registry);
}

AnnotationConfigUtils#processCommonDefinitionAnnotations

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    // 若是bean標註了@Lazy註解,進行解析
    AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); 
    if (lazy != null) {
        abd.setLazyInit(lazy.getBoolean("value"));
    }
    else if (abd.getMetadata() != metadata) {
        lazy = attributesFor(abd.getMetadata(), Lazy.class);
        if (lazy != null) {
            abd.setLazyInit(lazy.getBoolean("value"));
        }
    }
    // 解析@Primary註解,自動裝配時當出現多個Bean都匹配時,標註了@Primary註解的Bean將做爲首選者
    if (metadata.isAnnotated(Primary.class.getName())) {
        abd.setPrimary(true);
    } 
    // 解析@DependsOn註解,代表當前bean須要依賴的bean,會保證依賴的bean會在當前bean前實例化
    AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
    if (dependsOn != null) {
        abd.setDependsOn(dependsOn.getStringArray("value"));
    }
    // 解析@Role註解,用於標識bean的分類,實際用的比較少
    AnnotationAttributes role = attributesFor(metadata, Role.class);
    if (role != null) {
        abd.setRole(role.getNumber("value").intValue());
    } 
    // 解析@Description註解,bean的描述,實際用的比較少
    AnnotationAttributes description = attributesFor(metadata, Description.class);
    if (description != null) {
        abd.setDescription(description.getString("value"));
    }
}

ComponentScanBeanDefinitionParser#registerComponents

protected void registerComponents(XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {

    Object source = readerContext.extractSource(element);
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
    // 將掃描的全部BeanDefinition添加到compositeDef中
    for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
        compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
    }

    // Register annotation config processors, if necessary.
    boolean annotationConfig = true;
    // 獲取annotation-config屬性,默認爲true
    if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
        annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
    }
    if (annotationConfig) {
        // 向註冊中心中註冊用於註解的後置處理器,如AutowireAnnotationProcessor、CommonAnnotationProcessor
        Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
        // 將註冊的註解後置處理器的BeanDefinition添加到compositeDef的nestedComponents屬性中
        for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
            compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
        }
    }
    // 觸發註冊事件,默認實現爲EmptyReaderEventListener(空實現,沒有具體操做)
    readerContext.fireComponentRegistered(compositeDef);
}

AnnotationConfigUtils#registerAnnotationConfigProcessors

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
    // 獲取beanFactory
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
        if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
            // 設置dependencyComparator屬性
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
        }
        if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
            // 設置autowireCandidateResolver屬性(設置自動注入候選對象的解析器,用於判斷BeanDefinition是否爲候選對象)
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        }
    }

    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
    // 註冊用於處理@Configuration註解的後置處理器
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        // 建立一個ConfigurationClassPostProcessor的BeanDefinition
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        // 將def添加進註冊中心
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // 註冊用於處理@Autowired、@Value、@Inject註解的後置處理器
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
    // 註冊用於處理@Resource、@PostConstructor、@PostDestroy註解的後置處理器
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
    // 註冊用於處理JPA註解的後置處理器,如@PersistenceContext、@PersistenceUnit
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition();
        try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
						AnnotationConfigUtils.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
        }
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // 註冊用於處理@EventListener註解的後置處理器
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }
    // 註冊內部管理用於生產ApplicationListener對象的EventListenerFactory對象
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}

總結

本文主要介紹了 Spring 解析 context:component-scan 標籤的解析流程,咱們能夠從新整理一下思路:github

  1. 首先添加默認的過濾器,也就是斷定哪些類會被做爲組件來註冊;好比默認過濾器添加後,會將標註了 @Component 註解以及它的派生註解當作組件註冊。
  2. 找到指定包路徑下全部符合條件的類並封裝成 BeanDefinition ;解析 @Scope 註解,沒有默認爲 singleton;生成 bean 的名稱,首先會用 @Componentvalue 屬性,若是爲空或空字符串則默認使用類名,並把首字母轉成小寫;而後對一些註解的解析,如 @Lazy@Primary@DependsOn等;最後檢查註冊中心中是否包含當前 beanName,若是沒有直接添加進註冊中心,不然若是不是同一個類會拋出異常。好比 com.leisurexi.a.Usercom.leisurexi.b.User 同時添加 @Component 註解而且不手動指定不一樣的 beanName 在啓動時就會拋出異常。
  3. 最後註冊中心註冊一些註解的後置處理器,以下:
    • 處理 @Configuration 註解的 ConfigurationClassPostProcessor
    • 處理 @Autowired@Value@Inject 註解的 AutowiredAnnotationBeanPostProcessor
    • 處理 @Resource@PostConstructor@PostDestroy 註解的 CommonAnnotationBeanPostProcessor
    • 處理JPA註解的 PersistenceAnnotationBeanPostProcessor
    • 處理 @EventListener 註解的 EventListenerMethodProcessor

最後,我模仿 Spring 寫了一個精簡版,代碼會持續更新。地址:https://github.com/leisurexi/tiny-spring。訪問新博客地址,觀看效果更佳 https://leisurexi.github.io/spring

相關文章
相關標籤/搜索