Spring多種加載Bean方式簡析


1 定義bean的方式

常見的定義Bean的方式有:

  • 通過xml的方式,例如:
    <bean id="dictionaryRelMap" class="java.util.HashMap"/>
  • 通過注解的方式,在Class上使用@Component等注解,例如
    @Component
    public class xxxServicer{
      ....
    }
  • 通過在@Configuration類下的@Bean的方式,例如
    @Configuration
    public class xxxConfiguration{
      @Bean
      public myBean myBean(){
          return new myBean();
      }
    }

雖然這三種定義Bean的方式不一樣,對應的處理細節也不一樣,但是從大的邏輯上來看,都是一樣。主要的流程如下圖: 最關鍵的就是問題就是這么去找到定義Bean的方式,然后生成BeanDefinition后注冊到Spring上下文中,由Spring自動創建Bean的實例。

2 BeanDefinition

BeanDefinition是一個接口,用來描述一個Bean實例,例如是SINGLETON還是PROTOTYPE,屬性的值是什么,構造函數的參數是什么等。簡單來說,通過一個BeanDefinition我們就可以完成一個Bean實例化。 BeanDefinition及其主要的子類:

  • RootBeanDefinition和ChildBeanDefinition: 這2個BeanDefinition是相對的關系,自Spring 2.5 出來以后,已經被GenericBeanDefinition代替。因為這樣強迫我們在編寫代碼的時候就必須知道他們之間的關系。
  • GenericBeanDefinition: 相比於RootBeanDefinition和ChildBeanDefinition在定義的時候就必須硬編碼,GenericBeanDefinition的優點可以動態的為GenericBeanDefinition設置parent。
  • AnnotatedBeanDefinition:看名字就是知道是用來讀取通過注解定義Bean。

3 通過xml文件定義Bean

通過xml定義Bean是最早的Spring定義Bean的方式。因此,怎么把xml標簽解析為BeanDefinition(), 入口是在org.springframework.beans.factory.xml.XmlBeanDefinitionReader這個類,但是實際干活的是在org.springframework.beans.factory.xml.BeanDefinitionParserDelegate。代碼很多,但實際邏輯很簡單,就是解析Spring定義的<bean> <property> 等標簽 。 之前寫過一篇文章介紹過如何自定義Spring標簽 ,並解析后注冊到Spring中——傳送門

4 通過@Component等Spring支持的注解加載Bean

如果要使用@Component等注解定義Bean,一個前提條件是:有<context:component-scan/>或者@ComponentScan注解。但這2個方式還是有一點點區別:

4.1 <context:component-scan/>

由於<context:component-scan/>是一個xml標簽,因此是在解析xml,生成的類org.springframework.context.annotation.ComponentScanBeanDefinitionParser,關鍵代碼:

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
        //獲取base-package標簽
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

    // 實際處理類是ClassPathBeanDefinitionScanner 
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    //掃描basePackage下所有的類,如果有@Component等標簽就是注冊到Spring中
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

4.2 @ComponentScan

注解對應生成的類是org.springframework.context.annotation.ComponentScanAnnotationParser 其實最后實際干活的還是ClassPathBeanDefinitionScanner這個。ComponentScanAnnotationParser類的生成是伴隨着@Configuration這個注解處理過程中(意思說@ComponentScan必須和@Configuration一起使用)。而處理@Configuration其實是org.springframework.context.annotation.ConfigurationClassPostProcessor。是不是感覺有點繞。
其實簡單來說,在處理@Configuration的時候發現有@ComponentScan注解,就會生成ComponentScanAnnotationParser去掃描@Component注解

4.3 ClassPathBeanDefinitionScanner

上面說到了,無論注解還是標簽的方式,最后都會交給ClassPathBeanDefinitionScanner這個類來處理,這個類做的就是1.掃描basePackage下所有class,如果有@Component等注解,讀取@Component相關屬性,生成ScannedGenericBeanDefinition,注冊到Spring中。

5 通過@Bean方式

前面說了@ComponentScan是在@Configuration處理過程中的一環,既然@Bean注解也是必須和@Configuration一起使用,那么說明@Bean的處理也是在@Configuration中,其實最后是交給ConfigurationClassBeanDefinitionReader這個類來處理的,關鍵代碼:

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
        TrackedConditionEvaluator trackedConditionEvaluator) {

       //如果自己是通過@Import注解定義的,那么需要把自己注冊到Spring中
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    //這里就是處理方法上的@Bean
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    //處理@ImportResource,里面解析xml就是上面說到的解析xml的XmlBeanDefinitionReader
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

6 把BeanDefinition實例化

前面分別說了怎么把不同定義Bean的方式轉換為BeanDefinition加入到Spring中去(確切來說是保持在BeanFactory的BeanDefinitionMap中),實例化這些BeanDefinition是在ApplicationContext最后階段,關鍵代碼在DefaultListableBeanFactory中

  @Override
  public void preInstantiateSingletons() throws BeansException {
     for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                boolean isEagerInit;
                if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                    isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                        @Override
                        public Boolean run() {
                            return ((SmartFactoryBean<?>) factory).isEagerInit();
                        }
                    }, getAccessControlContext());
                }
                else {
                    isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                }
                if (isEagerInit) {
                    getBean(beanName);
                }
            }
            else {
                getBean(beanName);
            }
        }
    }
}

通過getBean實例化BeanFactory,代碼是在AbstractAutowireCapableBeanFactory中

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    //處理xxAware接口
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                invokeAwareMethods(beanName, bean);
                return null;
            }
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 調用BeanPostProcessors#postProcessBeforeInitialization
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
       //初始化,先判斷是否是InitializingBean,
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        // 調用BeanPostProcessors#postProcessAfterInitialization
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

 

從上面初始化和注釋內容可以看出,InitializeBean接口的方法(以及Init-method)和BeanPostProcessors的調用順序

7 總結

綜上分析,Spring加載Bean其實大的思想都是一樣的,先讀取相關信息生成BeanDefinition,然后通過BeanDefinition初始化Bean。如果知道了上面了套路以后,就可以清楚怎么自定義Xml標簽或者自定義注解向Spring中注入Bean。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM