Spring IOC-基於註解配置的容器

Spring中提供了基於註解來配置bean的容器,即AnnotationConfigApplicationContextjava

1. 開始

先看看在Spring家族中,AnnotationConfigApplicationContext在一個什麼樣的地位,看看繼承圖spring

能夠看到Spring提供了基於Xml配置的容器以外,還提供了基於註解和Groovy的容器,今天咱們來看看基於註解配置的容器緩存

2. 方法窺探

看看AnnotationConfigApplicationContext中提供了哪些方法app

3. 從構造方法開始

咱們從構造方法開始,分析基於註解的容器,是如何獲取BeanDefinition並註冊beanDefinitionMap中的函數

public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
}

接下來一步一步分析下去post

this()

調用了本類中的一個無參構造函數ui

public AnnotationConfigApplicationContext() {
    //註解bean讀取器
    this.reader = new AnnotatedBeanDefinitionReader(this);
    //註解bean掃描器
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotationConfigApplicationContext繼承自GenericApplicationContext,因此GenericApplicationContext的無參構造方法也會被調用this

/**
     * Create a new GenericApplicationContext.
     * @see #registerBeanDefinition
     * @see #refresh
     */
public GenericApplicationContext() {
    this.beanFactory = new DefaultListableBeanFactory();
}

能夠看到父類拆功能鍵spa

scan(basePackages)

public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    this.scanner.scan(basePackages);
}

調用了scanner.scan(),scannerClassPathBeanDefinitionScanner的一個實例code

/**
     * Perform a scan within the specified base packages.
     * @param basePackages the packages to check for annotated classes
     * @return number of beans registered
     */
public int scan(String... basePackages) {
    // 原來的beanDefinition數量
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    doScan(basePackages);
    
    // 下面是註冊配置處理器
    // 這個是啥呢,就是之前在xml中配置的<context:annotation-config> 
    // 這裏會註冊四個註解處理器,分別是
    // AutowiredAnnotationBeanPostProcessor,
    // CommonAnnotationBeanPostProcessor
    // PersistenceAnnotationBeanPostProcessor
    // RequiredAnnotationBeanPostProcessor
    // 這四個都是BeanPostProccessor,在每一個Bean建立的時候都會調用它們
    
    // 既然是註解處理器,他們處理什麼註解呢?
    // AutowiredAnnotationBeanPostProcessor 處理@AutoWired註解
    // CommonAnnotationBeanPostProcessor 處理@ Resource 、@ PostConstruct、@ PreDestroy
    // PersistenceAnnotationBeanPostProcessor 處理@PersistenceContext
    // RequiredAnnotationBeanPostProcessor 處理@Required
    
    
    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
    // 返回本次掃描註冊的beanDefinition數量
    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

這個ClassPathBeanDefinitionScanner是幹什麼的呢,經過查看源碼註釋

/ * A bean definition scanner that detects bean candidates on the classpath,
 * registering corresponding bean definitions with a given registry ({@code BeanFactory}
 * or {@code ApplicationContext}).
 *
 * <p>Candidate classes are detected through configurable type filters. The
 * default filters include classes that are annotated with Spring's
 * {@link org.springframework.stereotype.Component @Component},
 * {@link org.springframework.stereotype.Repository @Repository},
 * {@link org.springframework.stereotype.Service @Service}, or
 * {@link org.springframework.stereotype.Controller @Controller} stereotype.
 */

意思就是掃描類路徑下的被@Component,@Repository,@Service,@Controller註解的的類,而後註冊BeanDefinition到給定的BeanFactory

重點戲就在doScan()方法中

/**
     * Perform a scan within the specified base packages,
     * returning the registered bean definitions.
     * 掃描指定的包,反正註冊後的Bean Definition
     * <p>This method does <i>not</i> register an annotation config processor
     * but rather leaves this up to the caller.
     * 這個方法不會註冊註解處理器,而是留給調用者去作這件事
     * @param basePackages the packages to check for annotated classes
     * @return set of beans registered if any for tooling registration purposes (never {@code null})
     */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    // 遍歷給定的packages
    for (String basePackage : basePackages) {
        // findCandidateComponents是獲取一個包下的知足條件的類,下面會介紹
        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);
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            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;
}

findCandidateComponents(String basePackage)

這個方法能夠獲取一個包下的知足條件的BeanDefinition

/**
     * Scan the class path for candidate components.
     * @param basePackage the package to check for annotated classes
     * @return a corresponding Set of autodetected bean definitions
     */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    // 是否使用Filter,不掃描指定的包
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        // 掃描包
        return scanCandidateComponents(basePackage);
    }
}

這個scanCandidateComponents()裏面就是獲取資源判斷是否知足條件,可是Spring判斷的條件比較複雜,就先不看了

再回到doScan()方法裏面:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
        // 遍歷給定的packages
        for (String basePackage : basePackages) {
            // findCandidateComponents是獲取一個包下的知足條件的類,下面會介紹
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
                // 綁定scope(解析@Scope)
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                // 設置beanName
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                ////檢查beanName否存在
                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;
    }

registerBeanDefinition(definitionHolder,registry)

/**
     * Register the specified bean with the given registry.
     * <p>Can be overridden in subclasses, e.g. to adapt the registration
     * process or to register further bean definitions for each scanned bean.
     * @param definitionHolder the bean definition plus bean name for the bean
     * @param registry the BeanDefinitionRegistry to register the bean with
     */
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
/**
     * 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.
    // 若是有別名,遍歷別名註冊到容器的aliasMap
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

上面的registry.registerBeanDefinition()就是DefaultListableBeanFactory中的方法了

如今scan()方法已經走完了,回到構造方法中,還剩最後一個refresh()

refresh()

這裏的refreshXml的容器中調用的refresh是同一個方法,都來自AbstractApplicationContext

public void refresh() throws BeansException, IllegalStateException {
 
   synchronized (this.startupShutdownMonitor) {

      // 記錄啓動時間,標記狀態,檢查變量
      prepareRefresh();

      // 初始化BeanFactory容器
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 添加BeanPostProcessor,手動註冊幾個特殊的 bean
      prepareBeanFactory(beanFactory);

      try {
         // 子類擴展點
         postProcessBeanFactory(beanFactory);
         // 調用 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);
         // 註冊 BeanPostProcessor 的實現類
         registerBeanPostProcessors(beanFactory);
         // 初始化MessageSource
         initMessageSource();
         // 初始化事件廣播器
         initApplicationEventMulticaster();
         // 子類擴展點
         onRefresh();
         // 註冊事件監聽器
         registerListeners();

         // 初始化全部的 singleton beans
         finishBeanFactoryInitialization(beanFactory);

         // 完成refresh(),發佈廣播事件
         finishRefresh();
      }
      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }
         // 銷燬已經初始化的的Bean
         destroyBeans();
         // 設置 active爲false
         cancelRefresh(ex);
         throw ex;
      }
      finally {
         // 清除緩存
         resetCommonCaches();
      }
   }
}

這裏也有一點不一樣就是第二步obtainFreshBeanFactory(),這個方法裏面的調用getBeanFactory是留給子類實現的,基於註解的AnnotationConfigApplicationContextClassPathXmlApplicationContext是不同的。

具體就是調用refresh方法屢次,AnnotationConfigApplicationContext類的BeanFactory始終都是同一個,不會從新建立,可是ClassPathXmlApplicationContext會從新建立

相關文章
相關標籤/搜索