context:component-scan

默認情況下,<context:component-scan>查找使用構造型(stereotype)註解所標註的類,如@Component(組件),@Service(服務),@Controller(控制器),@Repository(數據倉庫)

我們具體看下<context:component-scan>的一些屬性,以下是一個比較具體的<context:component-scan>配置

<context:component-scan

     <!-- 掃描的基本包路徑 -->
     base-package="com.wjx.betalot" 

     <!-- 是否激活屬性注入註解 -->
     annotation-config="true" 

     <!-- Bean的ID策略生成器 -->
     name-generator="org.springframework.context.annotation.AnnotationBeanNameGenerator"
 
    <!-- 對資源進行篩選的正則表達式,這邊是個大的範疇,具體細分在include-filter與exclude-    filter中進行 -->
     resource-pattern="**/*.class" 

     <!-- scope解析器 ,與scoped-proxy只能同時配置一個 -->
     scope-resolver="org.springframework.context.annotation.AnnotationScopeMetadataResolver" 
                        scoped-proxy="no" <!-- scope代理,與scope-resolver只能同時配置一個 -->
                        use-default-filters="false"  <!-- 是否使用默認的過濾器,默認值true -->
                                  >
            <!-- 注意:若使用include-filter去定製掃描內容,要在use-default-filters="false"的情況下,不然會“失效”,被默認的過濾機制所覆蓋 -->                   
            <!-- annotation是對註解進行掃描 -->
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> 
            <!-- assignable是對類或接口進行掃描 -->
            <context:include-filter type="assignable" expression="com.wjx.betalot.performer.Performer"/>
            <context:include-filter type="assignable" expression="com.wjx.betalot.performer.impl.Sonnet"/>
            
            <!-- 注意:在use-default-filters="false"的情況下,exclude-filter是針對include-filter裏的內容進行排除 -->
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:exclude-filter type="assignable" expression="com.wjx.betalot.performer.impl.RainPoem"/>
            <context:exclude-filter type="regex" expression=".service.*"/> 

</context:component-scan>

以上配置註釋已經很詳細了,當然因爲這些註釋,你若是想複製去驗證,你得刪掉註釋。我們具體再說明一下這些註釋:

back-package:標識了<context:component-scan>元素所掃描的包,可以使用一些通配符進行配置

annotation-config:<context:component-scan>元素也完成了<context:annotation-config>元素的工作,開關就是這個屬性,false則關閉屬性注入註解功能

name-generator:這個屬性指定你的構造型註解,註冊爲Bean的ID生成策略,這個生成器基於接口BeanNameGenerator實現generateBeanName方法,你可以自己寫個類去自定義策略。這邊,我們可不顯示配置,它是默認使用org.springframework.context.annotation.AnnotationBeanNameGenerator生成器,也就是類名首字符小寫的策略,如Performer類,它註冊的Bean的ID爲performer.並且可以自定義ID,如@Component("Joy").這邊簡單貼出這個默認生成器的實現。

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            if (StringUtils.hasText(beanName)) {
                // Explicit bean name found.
                return beanName;
            }
        }
        // Fallback: generate a unique default bean name.
        return buildDefaultBeanName(definition, registry);
}

Spring除了實現了AnnotationBeanNameGenerator生成器外,還有個org.springframework.beans.factory.support.DefaultBeanNameGenerator生成器,它爲了防止Bean的ID重複,它的生成策略是類路徑+分隔符+序號

 ,如com.wjx.betalot.performer.impl.Sonnet註冊爲Bean的ID是com.wjx.betalot.performer.impl.Sonnet#0,這個生成器不支持自定義ID,否則拋出異常。同樣貼出代碼,有興趣的可以去看下。

public static String generateBeanName(
            BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
            throws BeanDefinitionStoreException {

        String generatedBeanName = definition.getBeanClassName();
        if (generatedBeanName == null) {
            if (definition.getParentName() != null) {
                generatedBeanName = definition.getParentName() + "$child";
            }
            else if (definition.getFactoryBeanName() != null) {
                generatedBeanName = definition.getFactoryBeanName() + "$created";
            }
        }
        if (!StringUtils.hasText(generatedBeanName)) {
            throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
                    "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
        }

        String id = generatedBeanName;
        if (isInnerBean) {
            // Inner bean: generate identity hashcode suffix.
            id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
        }
        else {
            // Top-level bean: use plain class name.
            // Increase counter until the id is unique.
            int counter = -1;
            while (counter == -1 || registry.containsBeanDefinition(id)) {
                counter++;
                id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
            }
        }
        return id;
    }

resource-pattern:對資源進行篩選的正則表達式,這邊是個大的範疇,具體細分在include-filter與exclude-filter中進行。

scoped-proxy: scope代理,有三個值選項,no(默認值),interfaces(接口代理),targetClass(類代理),那什麼時候需要用到scope代理呢,舉個例子,我們知道Bean的作用域scope有singleton,prototype,request,session,那有這麼一種情況,當你把一個session或者request的Bean注入到singleton的Bean中時,因爲singleton的Bean在容器啓動時就會創建A,而session的Bean在用戶訪問時纔會創建B,那麼當A中要注入B時,有可能B還未創建,這個時候就會出問題,那麼代理的時候來了,B如果是個接口,就用interfaces代理,是個類則用targetClass代理。這個例子出處:http://www.bubuko.com/infodetail-1434289.html。

scope-resolver:這個屬性跟name-generator有點類似,它是基於接口ScopeMetadataResolver的,實現resolveScopeMetadata方法,目的是爲了將@Scope(value="",proxyMode=ScopedProxyMode.NO,scopeName="")的配置解析成爲一個ScopeMetadata對象,Spring這裏也提供了兩個實現,我們一起看下。首先是org.springframework.context.annotation.AnnotationScopeMetadataResolver中

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
        ScopeMetadata metadata = new ScopeMetadata();
        if (definition instanceof AnnotatedBeanDefinition) {
            AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
            AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
            if (attributes != null) {
                metadata.setScopeName(attributes.getAliasedString("value", this.scopeAnnotationType, definition.getSource()));
                ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
                if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
                    proxyMode = this.defaultProxyMode;
                }
                metadata.setScopedProxyMode(proxyMode);
            }
        }
        return metadata;
    }

對比一下org.springframework.context.annotation.Jsr330ScopeMetadataResolver中的實現:

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
        ScopeMetadata metadata = new ScopeMetadata();
        metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
        if (definition instanceof AnnotatedBeanDefinition) {
            AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
            Set<String> annTypes = annDef.getMetadata().getAnnotationTypes();
            String found = null;
            for (String annType : annTypes) {
                Set<String> metaAnns = annDef.getMetadata().getMetaAnnotationTypes(annType);
                if (metaAnns.contains("javax.inject.Scope")) {
                    if (found != null) {
                        throw new IllegalStateException("Found ambiguous scope annotations on bean class [" +
                                definition.getBeanClassName() + "]: " + found + ", " + annType);
                    }
                    found = annType;
                    String scopeName = resolveScopeName(annType);
                    if (scopeName == null) {
                        throw new IllegalStateException(
                                "Unsupported scope annotation - not mapped onto Spring scope name: " + annType);
                    }
                    metadata.setScopeName(scopeName);
                }
            }
        }
        return metadata;
    }

ps:scope-resolver與scoped-proxy只能配置一個,配置了scope-resolver後你要使用代理,可以配置@Scope總的proxyMode屬性項

use-default-filters:是否使用默認的掃描過濾。

<context:include-filter> :用來告知哪些類需要註冊成Spring Bean,使用type和expression屬性一起協作來定義組件掃描策略。type有以下5種

過濾器類型 描述
annotation 過濾器掃描使用註解所標註的那些類,通過expression屬性指定要掃描的註釋
assignable 過濾器掃描派生於expression屬性所指定類型的那些類
aspectj 過濾器掃描與expression屬性所指定的AspectJ表達式所匹配的那些類
custom 使用自定義的org.springframework.core.type.TypeFliter實現類,該類由expression屬性指定
regex 過濾器掃描類的名稱與expression屬性所指定正則表示式所匹配的那些類

要注意的是:若使用include-filter去定製掃描內容,要在use-default-filters="false"的情況下,不然會“失效”,被默認的過濾機制所覆蓋

<context:exclude-filter>:與<context:include-filter> 相反,用來告知哪些類不需要註冊成Spring Bean,同樣注意的是:在use-default-filters="false"的情況下,exclude-filter是針對include-filter裏的內容進行排除。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章