1. 程式人生 > >context:component-scan實現原理與例項

context:component-scan實現原理與例項

一、<context:component-scan/>

想必@Component,@Repository,@Service,@Controller幾個常用的Type-Level的Spring MVC註解,大家都很清楚他們的意思跟用途。

標記為@Component的類,在使用註解配置的情況下,系統啟動時會被自動掃描,並新增到bean工廠中去(省去了配置檔案中寫bean定義了),另外三個分別表示MVC三層模式中不同層中的元件,他們都是被@Component標記的,所以也會被自動掃描。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component//這裡。。。 public @interface Repository { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component//這裡。。。 public @interface Service { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented
@Component//這裡。。。 public @interface Controller { String value() default ""; }

為了達到以上效果,我們還需在xml配置檔案中加入如下定義

<context:component-scan base-package="com.springrock..."/>

這樣Spring就可以正確的處理我們定義好的元件了,重要的是這些都是自動的,你甚至不知道他是怎麼做的,做了什麼?如果不瞭解反射,可能真的感到吃驚了,但即便如此,我也想知道它到底做了什麼?什麼時候做的?

二、BeanDefinitionParser

啟動專案,載入spring.xml時,會註冊下來解析器:

    @Override
    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }

經過仔細的原始碼閱讀,我找到了這個介面BeanDefinitionParser,文件描述說,它是一個用來處理自定義,頂級(<beans/>的直接兒子標籤)標籤的介面抽象。可以實現它來將自定義的標籤轉化為 BeanDefinition類。

BeanDefinitionParser介面註釋如下:

 * Interface used by the {@link DefaultBeanDefinitionDocumentReader} to handle custom,
 * top-level (directly under {@code <beans/>}) tags.
 *//介面被用來處理自定義,頂級(`<beans/>`的直接兒子標籤(DefaultBeanDefinitionDocumentReader:通過DTD和XSD讀取bean的定義資訊)
 * <p>Implementations are free to turn the metadata in the custom tag into as many
 * {@link BeanDefinition BeanDefinitions} as required.
 *//實現類可以將標籤裡面的元資料轉化成 bean對應的BeanDefinition(其描述了一個bean擁有哪些屬性,構造器等資訊) 

 * <p>The parser locates a {@link BeanDefinitionParser} from the associated
 * {@link NamespaceHandler} for the namespace in which the custom tag resides.
 * //解析器從標籤隸屬的namespace裡面找出相關聯的bean解析器

/*注意:
①先讀取標籤資訊,然後獲取對應的BeanDefinition;
②找到對應的BeanDefinitionParser!!!*/

下面是它的介面方法定義:

BeanDefinition parse(Element element, ParserContext parserContext);

其中Element是Dom api 中的元素,ParserContext則是用來註冊轉換來的bean 工廠(建立scanner —//使用的是readerContext.getRegistry()和boolean 型別的useDefaultFilters ! readerContext是從parserContext獲取的!!)。

ParserContext 類註釋:

 * Context that gets passed along a bean definition parsing process,
 * encapsulating all relevant configuration as well as state.
 * //bean定義解析過程傳遞的上下文,封裝所有相關的配置以及狀態。
 * Nested inside an {@link XmlReaderContext}.
 * //內部嵌套了一個XmlReaderContext
  • element 示例如下圖所示:

這裡寫圖片描述

  • parserContext 如下圖所示:
    • 擁有屬性readerContext;

這裡寫圖片描述

或許你開始惱火說這麼多跟上面有什麼關係,好吧,下面便是我真正要說的,我們來看下它有哪些實現類:

這裡寫圖片描述

看到了吧,ComponentScanBeanDefinitionParser,正是我們想要的,他就是用來將<context:component-scan/>標籤轉化為bean 的解析類。那他做了什麼呢?

ComponentScanBeanDefinitionParser類註釋如下:

 Parser for the {@code <context:component-scan/>} element.
 //解析該標籤的解析器

其parse()方法註釋如下:

Parse the specified Element and register the resulting BeanDefinition(s) with the BeanDefinitionRegistry embedded in the supplied ParserContext. 
//解析具體的Element 並使用上下文(ParserContext)提供的BeanDefinitionRegistry 註冊結果(BeanDefinition)到容器工廠中。
public BeanDefinition parse(Element element, ParserContext parserContext) {
        /*獲取(BASE_PACKAGE_ATTRIBUTE)屬性值*/
        String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
        //獲取執行環境對basePackage進行解析
        basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
        /*使用分隔符解析為字串陣列*/
        String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

        // Actually scan for bean definitions and register them.
       //根據parserContext和element獲取scanner 
        ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
        /*獲取basePackages下對應的beanDefinitions */
        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
        //註冊到容器工廠
        registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

        return null;
    }

① parserContext.getReaderContext():

這裡寫圖片描述

② getEnvironment():

    • 首先獲取reader,然後獲取Environment :
public final Environment getEnvironment() {
        return this.reader.getEnvironment();
    }
      • readerContext;

這裡寫圖片描述

      • Environment :

這裡寫圖片描述

③ resolvePlaceholders():

    • 獲取完environment後獲取propertyResolver:
@Override
    public String resolvePlaceholders(String text) {
        return this.propertyResolver.resolvePlaceholders(text);
    }

方法註釋如下:

Resolve ${...} placeholders in the given text, replacing them with corresponding property values as resolved by getProperty. Unresolvable placeholders with no default value are ignored and passed through unchanged.
/*意思就是解析你的basePackage,如果使用了${...}形式,就解析為對應的text值*/
@Override
    public String resolvePlaceholders(String text) {
        if (this.nonStrictHelper == null) {
            this.nonStrictHelper = createPlaceholderHelper(true);
        }
        return doResolvePlaceholders(text, this.nonStrictHelper);
    }
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
        return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
            @Override
            public String resolvePlaceholder(String placeholderName) {
                return getPropertyAsRawString(placeholderName);
            }
        });
    }

ClassPathBeanDefinitionScanner 類註釋如下:

A bean definition scanner that detects bean candidates on the classpath, 
registering corresponding bean definitions with a given registry (BeanFactory or ApplicationContext). 

/*一個bean definition 掃描器檢測classpath下的候選bean,並註冊corresponding bean definitions到容器工廠中;*/

Candidate classes are detected through configurable type filters. 

/*通過可配置的 type filters 過濾候選類*/

The default filters include classes that are annotated with Spring's @Component, @Repository, @Service, or @Controller stereotype. 

/*預設的filters 包含那些被@Component, @Repository, @Service, or @Controller 註解的類!!!*/

Also supports Java EE 6's javax.annotation.ManagedBean and JSR-330's javax.inject.Named annotations, if available.
//同樣支援其他的。。。

很明顯他會獲得<component-scan/>的base-package屬性;
然後解析所需解析的包路徑。
然後他會建立一個ClassPathBeanDefinitionScanner物件,並委託它來執行對路徑下檔案的掃描。
然後將獲得的BeanDefinitions註冊到bean工廠中。

是不是很清晰?

首先看獲取scanner程式碼如下:

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
        //預設使用 defaultFilters
        boolean useDefaultFilters = true;
        if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
            useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
        }

        // Delegate(委託) bean definition registration to scanner class.注意這句話,講明瞭scanner的功能
        ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
        scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
        scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());

        if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
            scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
        }
        //後續屬性配置
        try {
            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;
    }
    • 建立Scanner的程式碼:
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
        return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters);
    }
    //使用的是readerContext.getRegistry()和boolean 型別的useDefaultFilters ! readerContext是從parserContext獲取的!!

此時Scanner的值(剛建立完):

這裡寫圖片描述

執行完set後Scanner的值:

這裡寫圖片描述

注意這樣幾個屬性:

beanDefinitionDefaults,beanNameGenerator,
environment,excludeFilters,
includeFilters,metadataReaderFactory,
registry,resourcePattern,
resourcePatternResolver和scopeMetadataResolver!

下面開始進行後續配置:

parseBeanNameGenerator(element, scanner);
//Set the BeanNameGenerator to use for detected bean classes. 
Default is a AnnotationBeanNameGenerator.
parseScope(element, scanner);
// Register ScopeMetadataResolver if class name provided.
parseTypeFilters(element, scanner, parserContext);
//Parse exclude and include filter elements.

parseTypeFilters()詳細程式碼如下:

protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
        // Parse exclude and include filter elements.
        //獲取classLoader
        ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
        //拿到子元素list
        NodeList nodeList = element.getChildNodes();
        //根據子元素分別新增IncludeFilter和ExcludeFilter
        for (int i = 0; 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_ELEMENT.equals(localName)) {
                        TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
                        scanner.addIncludeFilter(typeFilter);
                    }
                    else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
                        TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
                        scanner.addExcludeFilter(typeFilter);
                    }
                }
                catch (Exception ex) {
                    parserContext.getReaderContext().error(
                            ex.getMessage(), parserContext.extractSource(element), ex.getCause());
                }
            }
        }
    }

至此,Scanner可以根據basePackage和IncludeFilter及ExcludeFilter進行掃描class!!!

獲取Scanner後,我想你會急切的知道ClassPathBeanDefinitionScanner 做了什麼?

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //首先判斷不為空
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        //定義各一個空集合
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
        //遍歷basePackages
        for (String basePackage : basePackages) {
            //Scan the class path for candidate components.
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            //遍歷集合中的BeanDefinition 
            for (BeanDefinition candidate : candidates) {
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                //為(BeanDefinition)candidate設定Scope
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    /*Apply further settings to the given bean definition, beyond the contents retrieved from scanning the component class.*/
                        postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                /*Check the given candidate's bean name, determining whether the corresponding bean definition needs to be registered or conflicts with an existing definition*/
                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;
    }

BeanDefinitionHolder 類註釋如下:

Holder for a BeanDefinition with name and aliases. Can be registered as a placeholder for an inner bean. 

Can also be used for programmatic registration of inner bean definitions. If you don't care about BeanNameAware and the like, registering RootBeanDefinition or ChildBeanDefinition is good enough.

① 找到candidates (set 集合)

      • 示例如下:
[Generic bean: class [com.atguigu.springmvc.converters.EmployeeConverter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-6.0.37\webapps\springMVC-2\WEB-INF\classes\com\atguigu\springmvc\converters\EmployeeConverter.class], Generic bean: class [com.atguigu.springmvc.crud.dao.DepartmentDao]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-6.0.37\webapps\springMVC-2\WEB-INF\classes\com\atguigu\springmvc\crud\dao\DepartmentDao.class], Generic bean: class [com.atguigu.springmvc.crud.dao.EmployeeDao]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-6.0.37\webapps\springMVC-2\WEB-INF\classes\com\atguigu\springmvc\crud\dao\EmployeeDao.class], Generic bean: class [com.atguigu.springmvc.crud.handlers.EmployeeHandler]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-6.0.37\webapps\springMVC-2\WEB-INF\classes\com\atguigu\springmvc\crud\handlers\EmployeeHandler.class], Generic bean: class [com.atguigu.springmvc.crud.handlers.TagsController]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-6.0.37\webapps\springMVC-2\WEB-INF\classes\com\atguigu\springmvc\crud\handlers\TagsController.class], Generic bean: class [com.atguigu.springmvc.test.SpringMVCTest]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-6.0.37\webapps\springMVC-2\WEB-INF\classes\com\atguigu\springmvc\test\SpringMVCTest.class], Generic bean: class [com.atguigu.springmvc.test.SpringMVCTestExceptionHandler]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-6.0.37\webapps\springMVC-2\WEB-INF\classes\com\atguigu\springmvc\test\SpringMVCTestExceptionHandler.class]]

② 遍歷candidates–BeanDefinition candidate :

這裡寫圖片描述

③ 使用scopeMetadataResolver獲取candidate的scopeMetadata:

這裡寫圖片描述

④ 根據candidate和registry獲取beanName

這裡寫圖片描述

⑤ 判斷是否是AbstractBeanDefinition例項,若是,則進行後置處理:

postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);

/*Apply further settings to the given bean definition, beyond the contents retrieved from scanning the component class.*/

⑥ 檢測candidate,判斷是否需要註冊和已經存在的相沖突

⑦ 獲取BeanDefinitionHolder definitionHolder

這裡寫圖片描述

⑧ 使用反射,對definitionHolder建立代理;

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);
        return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
    }

⑨ 新增到set集合,並將specified bean 註冊registry(工廠)

Register the specified bean with the given registry. 

Can be overridden in subclasses, e.g. to adapt the registration process or to register further bean definitions for each scanned bean.
    /**
     * Register the given bean definition with the given bean factory.
     * @param definitionHolder the bean definition including name and aliases
     * @param registry the bean factory to register with
     * @throws BeanDefinitionStoreException if registration failed
     */
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

重點是繼承自父類ClassPathScanningCandidateComponentProvider 的findCandidateComponents方法,意思就是找到候選元件,然後註冊到工廠中,那麼它是怎麼找到候選元件的呢?

我們再看看

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
        try {
            //拿到拼接後的路徑
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + "/" + this.resourcePattern;
                    /*Resolve the given location pattern into Resource objects. 
Overlapping resource entries that point to the same physical resource should be avoided, as far as possible. The result should have set semantics.

何為Resource? Interface for a resource descriptor that abstracts from the actual type of underlying resource,
 such as a file or class path resource.
*/
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                //判斷是否為可讀
                if (resource.isReadable()) {
                    try {
                        /*Obtain a MetadataReader(元資料讀取器) for the given resource.*/
                        MetadataReader metadataReader = this.metadataReaderFactory.                                                               getMetadataReader(resource);
                        /*判斷resource生成的metadataReader是否為候選元件*/
                        if (isCandidateComponent(metadataReader)) {
                            ScannedGenericBeanDefinition sbd = 
                                              new ScannedGenericBeanDefinition(metadataReader);
                                              /*判斷ScannedGenericBeanDefinition 是否為候選元件*/
                            if (isCandidateComponent(sbd)){
                                candidates.add(sbd);
                            }
                        }
                    }
                }
            }
        }
        return candidates;
    }
  • 首先獲取路徑下的資源Resource;
  • 然後判斷資源是否可讀,並且獲取可讀資源的MetadataReader物件,
  • 然後再呼叫isCandidateComponent(MetadataReader)判段是否是候選元件,如果是,則生成該metadataReader的ScannedGenericBeanDefinition物件。
  • 最後判斷ScannedGenericBeanDefinition是否為候選元件,如果是則新增到工廠中。

三、includeFilters,excludeFilters

可以看到經歷了兩次篩選,才找到最終的候選Bean,我們來看第一個過濾做了什麼?

  • 判斷是否為候選元件
/**
     * Determine whether the given class does not match any exclude filter
     * and does match at least one include filter.
     * @param metadataReader the ASM ClassReader for the class
     * @return whether the class qualifies as a candidate component
     */
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        /*判斷excludeFilters*/
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return false;
            }
        }
        /*/*判斷includeFilters*/*/
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }

我們看到這裡有兩個例項變數excludeFilters, includeFilters,然後用他們兩個去匹配傳遞進來的MetadataReader,如果與excludeFilter匹配成功返回false, 與includeFilter匹配成功返回true。那麼這兩個filter分別是什麼呢?我們打上斷點,除錯執行發現

這裡寫圖片描述

預設情況下includeFilters是一個含有兩個值得List,分別是@Component註解和@ManageBean註解,而excludeFilter是個空List。

好吧,現在豁然開朗了吧,原來就是它來篩選我們的@Component標記的類。

當然我們可以自定義這兩個filters,只需在<context:component-scan/>標籤下加兩個子標籤即可, 像這樣:

<context:component-scan base-package="com.springrock">
       <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
       <context:include-filter type="annotation" expression="com.springrock.whatever.youcustomized.annotation"/>
</context:component-scan>

四、BeanDefinitionRegistry

上面程式碼中我們看到還有一個isCandidateComponent方法,它主要是判斷當前類是否是具體的,而非抽象類和介面,以及是否是可以獨立建立的沒有依賴的?鑑於與我們目前討論的主題不相關,所以略去,感興趣的話,可以自己檢視下原始碼。

好了,我們既然知道了Spring是怎樣通過<context:component-scan/>來掃描,過濾我們的元件了,但是他是怎樣將我們定義的元件收集起來供後面的請求處理呢?

我們來看下上面doScan方法中有

//註冊到工廠中
registerBeanDefinition(definitionHolder, this.registry);

這樣一行程式碼,很明顯是將beanDefinition註冊到registry中了。

首先進入方法BeanDefinitionReaderUtils.registerBeanDefinition():


    /**
     * Register the given bean definition with the given bean factory.
     * //使用給定的bean Factory註冊bean
     * @param definitionHolder the bean definition including name and aliases
     * @param registry the bean factory to register with
     * @throws BeanDefinitionStoreException if registration failed
     */
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        //重點在這裡!!!!註冊到registry(工廠)
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                //註冊別名
                registry.registerAlias(beanName, alias);
            }
        }
    }

那這個registry是什麼呢?是一個BeanDefinitionRegistry,下面是它的介面定義及繼承結構:

public interface BeanDefinitionRegistry extends AliasRegistry {
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    boolean containsBeanDefinition(String beanName);
    String[] getBeanDefinitionNames();
    int getBeanDefinitionCount();
    boolean isBeanNameInUse(String beanName);
}

這裡寫圖片描述

我們可以看到介面中定義了諸多beandefinition的註冊,刪除,獲取等方法,並且Spring為我們提供了三個內部實現,那麼執行時,使用了那個實現呢?

DefaultListableBeanFactory,是的就是它。

它就是SpringMVC 中管理Bean的工廠了,我們來看下,它的registerBeanDefinition是怎樣實現的?

//---------------------------------------------------------------------
    // Implementation of BeanDefinitionRegistry interface
    //---------------------------------------------------------------------

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                //進行驗證
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;
        //根據beanName衝map中獲取oldBeanDefinition
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        //判斷是否為null
        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + oldBeanDefinition + "] bound.");
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

從上面的程式碼可以看出,所有的beanDefinition都由例項變數beanDefinitionMap來儲存管理,他是一個ConcurrentHashMap,beanName作為鍵,beanDefinition物件作為值。

到這我們知道了我們的bean是怎樣被註冊管理的了。

但是問題又來了,我們的系統是在什麼時候讀取<context:component-scan/>標籤,並且掃描我們的bean元件的呢?

當然是從ContextLoaderListener開始了入手分析了。

五、ContextLoader

首先看幾個介面:

EventListener

/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

② ServletContextListener:

/** 
     * Implementations of this interface receive notifications about
     * changes to the servlet context of the web application they are
     * part of.
     * To receive notification events, the implementation class
     * must be configured in the deployment descriptor for the web
     * application.
     * @see ServletContextEvent
     * @since   v 2.3
     */

public interface ServletContextListener extends EventListener {
    /**
     ** Notification that the web application initialization
     ** process is starting.
     ** All ServletContextListeners are notified of context
     ** initialization before any filter or servlet in the web
     ** application is initialized.
     */

    public void contextInitialized ( ServletContextEvent sce );

    /**
     ** Notification that the servlet context is about to be shut down.
     ** All servlets and filters have been destroy()ed before any
     ** ServletContextListeners are notified of context
     ** destruction.
     */
    public void contextDestroyed ( ServletContextEvent sce );
}

③ ContextLoaderListener:

 /*Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
 * Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
 *
 * <p>This listener should be registered after {@link org.springframework.web.util.Log4jConfigListener}
 * in {@code web.xml}, if the latter is used.
 *
 * <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
 * application context via the {@link #ContextL