天天看點

Spring4.3.x 淺析xml配置的解析過程(8)——解析context命名空間之component-scan标簽

概述

Spring context命名空間有property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export和mbean-server 8個标簽。

property-placeholder和property-override标簽的解析見property-placeholder和property-override标簽的解析,annotation-config标簽的解析見解析context命名空間之annotation-config标簽。這一節來探讨component-scan标簽的解析與用途。

解析component-scan标簽

component-scan标簽擁有同annotation-config标簽一樣的作用,但它比annotation-config标簽更強大。annotation-config标簽主要作用是注冊後處理來對已建立的BeanDefintion對象和執行個體化的bean做加工,component-scan标簽作用不僅于此,還可以把特定包下被指定注解類标注的類對象封裝成BeanDefinition對象并注冊到BeanDefinitionRegistry對象中,這樣大大簡化了xml檔案的内容。

component-scan标簽的解析器類為ComponentScanBeanDefinitionParser類,它直接實作了BeanDefinitionParser接口,下面是它實作的parse方法的源代碼。

@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {

        String basePackage = element.getAttribute("base-package");
        // 處理base-package屬性值中被“${”、“}”包圍的變量
        // 比如,base-package="${package}",如果上下文環境對象中有一個Properties對象的key為package
        // ->那麼,base-package的值就為這個key對應的value值
        basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
        // 如果有多個根包需要掃描,那麼以“,; \t\n”中的一個隔開
        String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,",; \t\n");

        // Actually scan for bean definitions and register them.
        // 建立ClassPathBeanDefinitionScanner對象,這個對象用于掃描base-package指定的包下面的類對象
        // ->并把比對的類對象使用BeanDefinition對象封裝,然後注冊到BeanDefinitionRegistry中
        ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);

        // 注冊ComponentDefinition和注解配置處理器(和annotation-config标簽的一樣)
        registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

        return null;
    }
           

ComponentScanBeanDefinitionParser的parser方法,首先建立并初始化ClassPathBeanDefinitionScanner對象;然後使用前面scanner對象掃描指定包下比對的類對象并注冊相應BeanDefinition對象;最後調用registerComponents方法,如果屬性annotation-config的值為true,則同annotation-config标簽一樣會注冊處理注解配置的各種後處理器。我們先看看最後一步中的registerComponents方法,後面我們在來詳細探讨spring如何掃描和比對包下的類對象。

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

        Object source = readerContext.extractSource(element);
        CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);

        for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
            compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
        }

        // Register annotation config processors, if necessary.
        boolean annotationConfig = true;
        if (element.hasAttribute("annotation-config")) {
            annotationConfig = Boolean.valueOf(element.getAttribute("annotation-config"));
        }
        if (annotationConfig) {
            Set<BeanDefinitionHolder> processorDefinitions =
                    AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
            for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
                compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
            }
        }

        readerContext.fireComponentRegistered(compositeDef);
    }
           

registerComponents方法代碼很簡單,它主要是拿去component-scan标簽的annotation-config屬性值,這個屬性值預設為true的,如果annotation-config值為true,則注冊注解配置的各個處理器,這個注冊過程在上一節——“解析annotation-config标簽”中已經探讨了,這裡就不用再說了。

這一節我們詳細讨論spring如何使用component-scan标簽來掃描和比對包下的類對象。

(1)建立并初始化ClassPathBeanDefinitionScanner對象

ComponentScanBeanDefinitionParser的parser方法調用configureScanner(ParserContext parserContext, Element element)方法來建立初始化并傳回一個ClassPathBeanDefinitionScanner對象,這個方法的源碼如下。

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {

        // 設定是否自動檢測被@Component、@Repository、@Service或者@Controller注解标注的類
        // 預設為true。如果為false,那麼上面4個注解将沒有作用,是以一般都不會設定這個屬性
        boolean useDefaultFilters = true;
        if (element.hasAttribute("use-default-filters")) {
            useDefaultFilters = Boolean.valueOf(element.getAttribute("use-default-filters"));
        }

        // 把注冊BeanDefinition的任務委托給scanner
        ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
        scanner.setResourceLoader(parserContext.getReaderContext().getResourceLoader());
        scanner.setEnvironment(parserContext.getReaderContext().getEnvironment());
        // 讓scanner持有component-scan标簽的父節點<beans>的屬性預設值
        scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
        scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());

        // 設定資源比對模式,預設為**/*.class
        if (element.hasAttribute("resource-pattern")) {
            scanner.setResourcePattern(element.getAttribute("resource-pattern"));
        }

        try {
            // 設定Bean名稱生成器
            parseBeanNameGenerator(element, scanner);
        } catch (Exception ex) {
            parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
        }

        try {
            // 設定作用域
            parseScope(element, scanner);
        } catch (Exception ex) {
            parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
        }

        // 設定過濾器,即用于指定哪些類需要被處理,哪些類需要被忽略
        parseTypeFilters(element, scanner, parserContext);

        return scanner;
    }
           

configureScanner方法有4步,第一步是調用createScanner方法來建立一個ClassPathBeanDefinitionScanner 對象,并使用ParserContext對象來初始化它;第二步是調用parseBeanNameGenerator方法;第三步是調用parseScope方法,第四步是調用parseTypeFilters方法。下面我們分别介紹這4個方法。

1)調用createScanner方法建立ClassPathBeanDefinitionScanner 對象,代碼如下。

/**
    * 建立并傳回ClassPathBeanDefinitionScanner對象
    **/
    protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
        return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters);
    }
           

2)調用parseBeanNameGenerator方法設定Bean名稱生成器

protected void parseBeanNameGenerator(Element element, ClassPathBeanDefinitionScanner scanner) {
        if (element.hasAttribute("name-generator")) {
            // 生成一個BeanNameGenerator對象
            BeanNameGenerator beanNameGenerator = (BeanNameGenerator) instantiateUserDefinedStrategy(
                    element.getAttribute("name-generator"), BeanNameGenerator.class,
                    scanner.getResourceLoader().getClassLoader());
            scanner.setBeanNameGenerator(beanNameGenerator);
        }
    }
           

parseBeanNameGenerator為scanner對象設定一個BeanNameGenerator接口對象。spring提供了兩個這樣的對象,其一是AnnotationBeanNameGenerator,它被用于擷取被@Component等注解的類對象的bean名稱。其二是DefaultBeanNameGenerator,它被用于擷取一般bean的名稱。

生成BeanNameGenerator對象調用了ClassPathBeanDefinitionScanner類的私有方法instantiateUserDefinedStrategy,這個方法用于執行個體化一個指定類型的對象,源碼如下。

/**
    * 根據指定的class名稱執行個體化一個對象,這個對象必須為指定的strategyType類型。
    **/
    private Object instantiateUserDefinedStrategy(String className, Class<?> strategyType, ClassLoader classLoader) {
        Object result;
        try {
            result = classLoader.loadClass(className).newInstance();
        } catch (ClassNotFoundException ex) {
            throw new IllegalArgumentException("Class [" + className + "] for strategy [" +
                    strategyType.getName() + "] not found", ex);
        } catch (Exception ex) {
            throw new IllegalArgumentException("Unable to instantiate class [" + className + "] for strategy [" +
                    strategyType.getName() + "]: a zero-argument constructor is required", ex);
        }

        if (!strategyType.isAssignableFrom(result.getClass())) {
            throw new IllegalArgumentException("Provided class name must be an implementation of " + strategyType);
        }
        return result;
    }
           

3)調用parseScope方法設定作用域解析器或者作用域的預設代理模式

protected void parseScope(Element element, ClassPathBeanDefinitionScanner scanner) {
        // 如果scope-resolver有值,則注冊ScopeMetadataResolver
        if (element.hasAttribute("scope-resolver")) {
            if (element.hasAttribute("scoped-proxy")) {
                throw new IllegalArgumentException(
                        "Cannot define both 'scope-resolver' and 'scoped-proxy' on <component-scan> tag");
            }
            ScopeMetadataResolver scopeMetadataResolver = (ScopeMetadataResolver) instantiateUserDefinedStrategy(
                    element.getAttribute("scope-resolver"), ScopeMetadataResolver.class,
                    scanner.getResourceLoader().getClassLoader());
            scanner.setScopeMetadataResolver(scopeMetadataResolver);
        }

        // 設定作用域的預設代理模式ScopedProxyMode
        if (element.hasAttribute("scoped-proxy")) {
            String mode = element.getAttribute("scoped-proxy");
            if ("targetClass".equals(mode)) {
                scanner.setScopedProxyMode(ScopedProxyMode.TARGET_CLASS);
            } else if ("interfaces".equals(mode)) {
                scanner.setScopedProxyMode(ScopedProxyMode.INTERFACES);
            } else if ("no".equals(mode)) {
                scanner.setScopedProxyMode(ScopedProxyMode.NO);
            } else {
                throw new IllegalArgumentException("scoped-proxy only supports 'no', 'interfaces' and 'targetClass'");
            }
        }
    }
           

注:scope-resolver和scoped-proxy不能同時定義。

4)調用parseTypeFilters方法設定類型過濾器

parseTypeFilters方法主要是用來component-scan标簽的include-filter和exclude-filter子節點。include-filter标簽用來表示必須包含的類,不管這個類是否被預設的注解類标注。exclude-filter标簽用來表示掃描時必須忽略的類,即使這個類被預設的注解标注了。但include-filter和exclude-filter兩個元素不能共存。

protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
        // 解析exclude和include過濾器元素
        ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
        NodeList nodeList = element.getChildNodes();
        // 周遊component-scan标簽下的子節點
        for (int i = ; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                String localName = parserContext.getDelegate().getLocalName(node);
                try {
                    if ("include-filter".equals(localName)) {
                        // 解析include元素
                        TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
                        scanner.addIncludeFilter(typeFilter);
                    } else if ("exclude-filter".equals(localName)) {
                        解析exclude元素
                        TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
                        scanner.addExcludeFilter(typeFilter);
                    }
                } catch (Exception ex) {
                    parserContext.getReaderContext().error(
                            ex.getMessage(), parserContext.extractSource(element), ex.getCause());
                }
            }
        }
    }
           

parseTypeFilters方法首先判斷區分出include-filter和exclude-filter标簽,然後調用createTypeFilter方法來解析他們并傳回TypeFilter,scanner把他們的指代的TypeFilter對象儲存到它的includeFilters和excludeFilters清單中。

/**
    * 解析component-scan标簽的include-filter和exclude-filter子節點,并傳回TypeFilter對象
    **/
    protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader, ParserContext parserContext) {
        String filterType = element.getAttribute("type");
        String expression = element.getAttribute("expression");
        expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
        try {
            if ("annotation".equals(filterType)) {
                // 指定過濾的注解。expression=類全名稱
                return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression));
            } else if ("assignable".equals(filterType)) {
                // 指定過濾的類,它的子類或者實作類也包括。expression=類全名稱
                return new AssignableTypeFilter(classLoader.loadClass(expression));
            } else if ("aspectj".equals(filterType)) {
                // 指定aspectj表達式來過濾類。expression=aspectj表達式字元串
                return new AspectJTypeFilter(expression, classLoader);
            } else if ("regex".equals(filterType)) {
                // 通過指定的正規表達式來過濾類。expression=正規表達式字元串
                return new RegexPatternTypeFilter(Pattern.compile(expression));
            } else if ("custom".equals(filterType)) {
                // 使用者自定義過濾器類型
                Class<?> filterClass = classLoader.loadClass(expression);
                if (!TypeFilter.class.isAssignableFrom(filterClass)) {
                    throw new IllegalArgumentException(
                            "Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
                }
                return (TypeFilter) BeanUtils.instantiateClass(filterClass);
            } else {
                throw new IllegalArgumentException("Unsupported filter type: " + filterType);
            }
        } catch (ClassNotFoundException ex) {
            throw new FatalBeanException("Type filter class not found: " + expression, ex);
        }
    }
           

(2)使用ClassPathBeanDefinitionScanner對象的doScan(String… basePackages)方法開始掃描包,這個方法的源碼如下。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
        // 周遊指定的包
        for (String basePackage : basePackages) {
            // 擷取包下的所有候選BeanDefinition對象
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            // 周遊所有候選BeanDefinition對象
            for (BeanDefinition candidate : candidates) {
                // 擷取一個ScopeMetadata對象,預設為AnnotationScopeMetadataResolver
                // 如果目标類未被@Scope注解,則傳回一個預設的ScopeMetadata
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());

                // 使用bean名稱生成器生成bean名稱,預設生成器為AnnotationBeanNameGenerator
                // 首先是以注解的value為bean名稱,如果注解的value沒有值,則使用預設的名稱
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    // 進一步處理BeanDefinition對象,比如,bean是否可以被用于自動注入的備選bean
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    // 處理定義在目标類上的注解,包括@Lazy, @Primary, @DependsOn, @Role, @Description
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                // 檢查beanName是否已經存在BeanDefinitionRegistry中存在
                if (checkCandidate(beanName, candidate)) {
                    // beanName還沒被使用過
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    // 如果有必要,則建立作用域代理
                    // 如果建立了代理,則傳回表示代理對象的BeanDefinitionHolder
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    // 注冊BeanDefinitionHolder對象
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }
           

關于doScan方法所調用的方法,我隻探讨其中的三個,其一是ClassPathBeanDefinitionScanner的findCandidateComponents方法;其二是AnnotationConfigUtils的processCommonDefinitionAnnotations靜态方法;其三是AnnotationConfigUtils的applyScopedProxyMode靜态方法。

1) findCandidateComponents方法繼承自ClassPathScanningCandidateComponentProvider類,是用于查找指定包及其子包下所有比對的類,并傳回這些類對應的BeanDefinition對象。它的源碼如下。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
        try {
            // 聲明有CLASSPATH_ALL_URL_PREFIX="classpath*:"
            // resourcePattern預設為"**/*.class"
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + "/" + this.resourcePattern;
            // 根據模式比對來搜尋包下面比對的所有類
            // 預設的ResourcePatternResolver實作類為PathMatchingResourcePatternResolver類
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            boolean traceEnabled = logger.isTraceEnabled();
            boolean debugEnabled = logger.isDebugEnabled();
            // 周遊所有比對的類
            for (Resource resource : resources) {
                if (traceEnabled) {
                    logger.trace("Scanning " + resource);
                }
                if (resource.isReadable()) {
                    try {
                        // 預設的MetadataReaderFactory實作類為CachingMetadataReaderFactory
                        // 預設傳回的是SimpleMetadataReader對象
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                        // 通過過濾器來判斷類對象是否為候選類
                        if (isCandidateComponent(metadataReader)) {
                            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                            sbd.setResource(resource);
                            sbd.setSource(resource);
                            // 候選類必須是一個具體的實作類,并且它的執行個體化必須是獨立的
                            if (isCandidateComponent(sbd)) {
                                if (debugEnabled) {
                                    logger.debug("Identified candidate component class: " + resource);
                                }
                                candidates.add(sbd);
                            } else {
                                if (debugEnabled) {
                                    logger.debug("Ignored because not a concrete top-level class: " + resource);
                                }
                            }
                        } else {
                            if (traceEnabled) {
                                logger.trace("Ignored because not matching any filter: " + resource);
                            }
                        }
                    } catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to read candidate component class: " + resource, ex);
                    }
                } else {
                    if (traceEnabled) {
                        logger.trace("Ignored because not readable: " + resource);
                    }
                }
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return candidates;
    }
           

findCandidateComponents方法首先擷取指定包及其子包下所有類的資源對象,然後使用CachingMetadataReaderFactory對象擷取一個SimpleMetadataReader的MetadataReader對象,SimpleMetadataReader使用ClassReader對象從Resource對象持有的類檔案輸入流中讀取類資訊,ClassReader允許通過ClassVisitor對象來通路它持有的類資訊。而SimpleMetadataReader使用AnnotationMetadataReadingVisitor通路器把ClassReader存有的類資訊分解成類中繼資料ClassMetadata對象和注解中繼資料AnnotationMetadata對象。

是以findCandidateComponents方法才可以調用isCandidateComponent(MetadataReader metadataReader)方法來通過過濾器判斷目前類是否會被排除在候選類之外,下面是isCandidateComponent方法的源碼。

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        // 首先使用excludeFilters中的過濾器檢查目前類是否應該排除
        // 通過exclude-filter标簽配置
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return false;
            }
        }
        // 最後使用includeFilters來檢視目前類是否候選類
        // 通過include-filter标簽配置。
        // 預設為注解類型的過濾器,注解類為spring定義的@Component
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }
           

如果目前類是候選類,那麼為目前類生成一個ScannedGenericBeanDefinition對象,ScannedGenericBeanDefinition繼承自GenericBeanDefinition并實作了AnnotatedBeanDefinition接口。

如果類不是一個具體且獨立的類,比如抽象類、接口或者非靜态内部類,通過反射執行個體化一個對象的時候是會抛出異常的。是以spring還會判斷一次,在findCandidateComponents方法中調用isCandidateComponent(AnnotatedBeanDefinition beanDefinition)方法,它的源碼如下。

/**
    * 判斷類是否是一個具體類,并且是一個可以獨立執行個體化的類。
    **/
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
    }
           

通過以上判斷排除,最終獲得一個ScannedGenericBeanDefinition對象。現在我們第2點ClassPathBeanDefinitionScanner類的 doScan(String… basePackages)方法拿到這個對象後通過調用AnnotationConfigUtils工具類processCommonDefinitionAnnotations靜态方法還做了些什麼。

2) AnnotationConfigUtils的processCommonDefinitionAnnotations靜态方法的源碼如下。

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
        processCommonDefinitionAnnotations(abd, abd.getMetadata());
    }
           

processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd)方法把解析類上的注解任務轉交給它的另一個重載方法processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata),這是一個隻有包通路權限的靜态方法,源碼如下。

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
        // 解析@Lazy注解,設定是否延遲加載
        if (metadata.isAnnotated(Lazy.class.getName())) {
            abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
        } else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {
            abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));
        }

        // 解析@Primary注解
        if (metadata.isAnnotated(Primary.class.getName())) {
            abd.setPrimary(true);
        }
        // 解析@DependOn注解
        if (metadata.isAnnotated(DependsOn.class.getName())) {
            abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
        }

        if (abd instanceof AbstractBeanDefinition) {
            AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
            // 解析@Role注解
            if (metadata.isAnnotated(Role.class.getName())) {
                absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());
            }
            // 解析@Description注解
            if (metadata.isAnnotated(Description.class.getName())) {
                absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));
            }
        }
    }
           

3) doScan(String… basePackages)方法還調用AnnotationConfigUtils工具類的applyScopedProxyMode靜态方法,這也是一個隻有包通路權限的方法,它主要是處理标注在類上的@Scope注解,源碼如下。

static BeanDefinitionHolder applyScopedProxyMode(
            ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

        ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
        if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
            // 不需要代理模式
            return definition;
        }
        boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
        // 建立一個scope代理
        return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
    }
           

ScopedProxyCreator類的createScopedProxy靜态方法源碼如下

public static BeanDefinitionHolder createScopedProxy(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {

        // 調用ScopedProxyUtils工具類的createScopedProxy方法
        return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
    }
           

工具類ScopedProxyUtils的createScopedProxy靜态方法源碼如下。

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
            BeanDefinitionRegistry registry, boolean proxyTargetClass) {

        String originalBeanName = definition.getBeanName();
        BeanDefinition targetDefinition = definition.getBeanDefinition();
        // targetBeanName格式為scopedTarget. + originalBeanName
        String targetBeanName = getTargetBeanName(originalBeanName);

        // Create a scoped proxy definition for the original bean name,
        // "hiding" the target bean in an internal target definition.
        // 建立一個ScopedProxyFactoryBean類對應BeanDefinition對象
        RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
        proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
        proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
        proxyDefinition.setSource(definition.getSource());
        proxyDefinition.setRole(targetDefinition.getRole());

        proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
        if (proxyTargetClass) {
            targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        } else {
            // 設定為根據接口做做代理
            proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
        }

        // 根據代理目标BeanDefinition設定是否可以為自動注入的候選bean
        proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
        proxyDefinition.setPrimary(targetDefinition.isPrimary());
        if (targetDefinition instanceof AbstractBeanDefinition) {
            proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
        }

        // 隐藏被代理的bean
        targetDefinition.setAutowireCandidate(false);
        targetDefinition.setPrimary(false);

        // 注冊被代理的bean的BeanDefinition對象
        registry.registerBeanDefinition(targetBeanName, targetDefinition);

        // 傳回代理bean的BeanDefinitionHolder對象
        return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
    }
           

總結

(1)component-scan不隻是擁有annotation-config的作用,它還用于掃描注冊指定包下特定的bean。是以,在xml配置檔案中定義了component-scan就不需要再定義annotation-config。

(2)component-scan大大簡化了我們的xml配置。如果我們需要把一個對象托管給Spring容器,隻需要在類上添加@Component注解或者被@Component标注的注解(比如,@Controller、@Service、@Repository)

(3)component-scan的兩個子标簽include-filter和exclude-filter不能共存。這個并不是從spring的java源碼中展現的,而是在schema檔案中展現的。(spring為什麼這樣設計,我實在是不懂的了)