官术网_书友最值得收藏!

2.5 容器其他相關特性的設計與實現

在前面的IoC原理分析中,我們對IoC容器的主要功能進行了分析,比如BeanDefinition的載入和解析,依賴注入的實現,等等。為了更全面地理解IoC容器特性的設計,下面對容器的一些其他相關特性的設計原理也進行簡要的分析。這些特性都是在使用IoC容器的時候會經常遇到的。這些特性其實很多,在這里只選擇了幾個例子供讀者參考。在了解了IoC容器的整體運行原理以后,對這些特性的分析已經不再是一件困難的事情。如果讀者對其他IoC容器的特性感興趣,也可以按照同樣的思路進行分析。

2.5.1 ApplicationContext和Bean的初始化及銷毀

對于BeanFactory,特別是ApplicationContext,容器自身也有一個初始化和銷毀關閉的過程。下面詳細看看在這兩個過程中,應用上下文完成了什么,可以讓我們更多地理解應用上下文的工作,容器初始化和關閉過程可以簡要地通過圖2-16來表現。

圖2-16 容器初始化和關閉過程

從圖中可以看到,對ApplicationContext啟動的過程是在AbstractApplicationContext中實現的。在使用應用上下文時需要做一些準備工作,這些準備工作在prepareBeanFactory()方法中實現。在這個方法中,為容器配置了ClassLoader、PropertyEditor和BeanPost-Processor等,從而為容器的啟動做好了必要的準備工作。

        protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            beanFactory.setBeanClassLoader(getClassLoader());
            beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
            beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this));
            beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
            beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
            beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
            beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
            beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
            beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
            beanFactory.registerResolvableDependency(ResourceLoader.class, this);
            beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
            beanFactory.registerResolvableDependency(ApplicationContext.class, this);
            if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader
            (beanFactory.getBeanClassLoader()));
            }
            if (!beanFactory.containsBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
                beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME,
                System.getProperties());
            }
            if (!beanFactory.containsBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
                beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME,
            System.getenv());
            }
        }

同樣,在容器要關閉時,也需要完成一系列的工作,這些工作在doClose( )方法中完成。在這個方法中,先發出容器關閉的信號,然后將Bean逐個關閉,最后關閉容器自身。

        protected void doClose() {
            if (isActive()) {
                  if (logger.isInfoEnabled()) {
                      logger.info("Closing " + this);
                  }
                  try {
                      publishEvent(new ContextClosedEvent(this));
                  }
                  catch (Throwable ex) {
                      logger.error("Exception thrown from ApplicationListener handling
                      ContextClosedEvent", ex);
                  }
                      Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
                  for (String beanName : new LinkedHashSet<String>
                  (lifecycleBeans.keySet())) {
                      doStop(lifecycleBeans, beanName);
                  }
                  destroyBeans();
                  closeBeanFactory();
                  onClose();
                  synchronized (this.activeMonitor) {
                      this.active = false;
                  }
              }
        }

以上是容器的初始化和銷毀的設計與實現。在這個過程中需要區分Bean的初始化和銷毀過程。在應用開發中,常常需要執行一些特定的初始化工作,這些工作都是相對比較固定的,比如建立數據庫連接,打開網絡連接等,同時,在結束服務時,也有一些相對固定的銷毀工作需要執行。為了便于這些工作的設計,Spring IoC容器提供了相關的功能,可以讓應用定制Bean的初始化和銷毀過程。

容器的實現是通過IoC管理Bean的生命周期來實現的。Spring IoC容器在對Bean的生命周期進行管理時提供了Bean生命周期各個時間點的回調。在分析Bean初始化和銷毀過程的設計之前,簡要介紹一下IoC容器中的Bean生命周期。

?Bean實例的創建。

?為Bean實例設置屬性。

?調用Bean的初始化方法。

?應用可以通過IoC容器使用Bean。

?當容器關閉時,調用Bean的銷毀方法。

Bean的初始化方法調用是在以下的initializeBean方法中實現的:

        protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader
                (getBeanClassLoader());
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            Object wrappedBean = bean;
            if (mbd == null || !mbd.isSynthetic()) {
                wrappedBean = applyBeanPostProcessorsBeforeInitialization
                (wrappedBean, beanName);
            }
            try {
                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()) {
                wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            }
            return wrappedBean;
        }

在調用Bean的初始化方法之前,會調用一系列的aware接口實現,把相關的BeanName、BeanClassLoader,以及BeanFactoy注入到Bean中去。接著會看到對invokeInitMethods的調用,這時還會看到啟動afterPropertiesSet的過程,當然,這需要Bean實現InitializingBean的接口,對應的初始化處理可以在InitializingBean接口的afterPropertiesSet方法中實現,這里同樣是對Bean的一個回調。

        protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd)
            throws Throwable {
            boolean isInitializingBean = (bean instanceof InitializingBean);
            if (isInitializingBean && (mbd == null || !mbd.isExternally
                ManagedInitMethod("afterPropertiesSet"))) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Invoking afterPropertiesSet() on bean with name '" +
                    beanName + "'");
                    }
                    ((InitializingBean) bean).afterPropertiesSet();
            }
            if (mbd != null) {
                String initMethodName = mbd.getInitMethodName();
                if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".
                equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                    invokeCustomInitMethod(beanName, bean, mbd);
                }
            }
        }

最后,還會看到判斷Bean是否配置有initMethod,如果有,那么通過invokeCustom-InitMethod方法來直接調用,最終完成Bean的初始化。

        protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition
        mbd) throws Throwable {
            String initMethodName = mbd.getInitMethodName();
            Method initMethod = (mbd.isNonPublicAccessAllowed() ?
                    BeanUtils.findMethod(bean.getClass(), initMethodName) :
                    ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
            if (initMethod == null) {
                if (mbd.isEnforceInitMethod()) {
                    throw new BeanDefinitionValidationException("Couldn't find an init
                    method named '" +
                            initMethodName + "' on bean with name '" + beanName + "'");
                }
                else {
                    if (logger.isDebugEnabled()) {
                          logger.debug("No default init method named '" + initMethodName +
                                  "' found on bean with name '" + beanName + "'");
                    }
                    return;
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking init method  '" + initMethodName + "' on bean with name '"
                + beanName + "'");
            }
            ReflectionUtils.makeAccessible(initMethod);
            try {
                initMethod.invoke(bean, (Object[]) null);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }

在這個對initMethod的調用中,可以看到首先需要得到Bean定義的initMethod,然后通過JDK的反射機制得到Method對象,直接調用在Bean定義中聲明的初始化方法。

與Bean初始化類似,當容器關閉時,可以看到對Bean銷毀方法的調用。Bean銷毀過程是這樣的:

        protected void doClose() {
            if (isActive()) {
                  if (logger.isInfoEnabled()) {
                      logger.info("Closing " + this);
                  }
                  try {
                      publishEvent(new ContextClosedEvent(this));
                  }
                  catch (Throwable ex) {
                      logger.error("Exception thrown from ApplicationListener handling
                      ContextClosedEvent", ex);
                  }
                  Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
                  for (String beanName : new LinkedHashSet<String>
                  (lifecycleBeans.keySet())) {
                      doStop(lifecycleBeans, beanName);
                  }
                  destroyBeans();
                  closeBeanFactory();
                  onClose();
                  synchronized (this.activeMonitor) {
                      this.active = false;
                  }
              }
        }

其中的destroy方法,對Bean進行銷毀處理。最終在DisposableBeanAdapter類中可以看到destroy方法的實現。

        public void destroy() {
            if (this.beanPostProcessors != null && !this.beanPostProcessors.isEmpty()) {
                for (int i = this.beanPostProcessors.size() -1; i >= 0; i--) {
                      this.beanPostProcessors.get(i).postProcessBeforeDestruction(this.bean,
                      this.beanName);
                }
            }
            if (this.invokeDisposableBean) {
                if (logger.isDebugEnabled()) {
                      logger.debug("Invoking destroy() on bean with name '" + this.
                      beanName + "'");
                }
                try {
                      ((DisposableBean) this.bean).destroy();
                }
                catch (Throwable ex) {
                      String msg = "Invocation of destroy method failed on bean with name '" +
                      this.beanName + "'";
                      if (logger.isDebugEnabled()) {
                          logger.warn(msg, ex);
                      }
                      else {
                            logger.warn(msg + ": " + ex);
                      }
                }
            }
            if (this.destroyMethod != null) {
                invokeCustomDestroyMethod(this.destroyMethod);
            }
            else if (this.destroyMethodName != null) {
                this.destroyMethod = (this.nonPublicAccessAllowed ?
                        BeanUtils.findMethodWithMinimalParameters(this.bean.getClass(),
                        this.destroyMethodName) :
                        BeanUtils.findMethodWithMinimalParameters(this.bean.getClass().
                        getMethods(),
                        this.destroyMethodName));
                invokeCustomDestroyMethod(this.destroyMethod);
            }
        }

這里可以看到對Bean的銷毀過程,首先對postProcessBeforeDestruction進行調用,然后調用Bean的destroy方法,最后是對Bean的自定義銷毀方法的調用,整個過程和前面的初始化過程很類似。

2.5.2 lazy-init屬性和預實例化

正如前面所述,在IoC容器的初始化過程中,主要的工作是對BeanDefinition的定位、載入、解析和注冊。此時依賴注入并沒有發生,依賴注入發生在應用第一次向容器索要Bean時。向容器索要Bean是通過getBean的調用來完成的,該getBean是容器提供Bean服務的最基本的接口。在前面的分析中也提到,對于容器的初始化,也有一種例外情況,就是用戶可以通過設置Bean的lazy-init屬性來控制預實例化的過程。這個預實例化在初始化容器時完成Bean的依賴注入。毫無疑問,這種容器的使用方式會對容器初始化的性能有一些影響,但卻能夠提高應用第一次取得Bean的性能。因為應用在第一次取得Bean時,依賴注入已經結束了,應用可以取得已有的Bean。

我們回過頭頭看看在上下文的初始化過程,也就是refresh中的代碼實現,可以看到預實例化是整個refresh初始化IoC容器的一個步驟。在AbstractApplicationContext中看一下refresh的實現。這個初始化過程在前面分析IoC容器初始化時已經從載入和注冊BeanDefinition的角度分析過。

下面將從lazy-init屬性配置實現的角度進行分析。對這個屬性的處理也是容器refresh的一部分。在finishBeanFactoryInitialization的方法中,封裝了對lazy-init屬性的處理,實際的處理是在DefaultListableBeanFactory這個基本容器的preInstantiateSingletons方法中完成的。該方法對單件Bean完成預實例化,這個預實例化的完成巧妙地委托給容器來實現。如果需要預實例化,那么就直接在這里采用getBean去觸發依賴注入,與正常依賴注入的觸發相比,只有觸發的時間和場合不同。在這里,依賴注入發生在容器執行refresh的過程中,也就是發生在IoC容器初始化的過程中,而不像一般的依賴注入一樣發生在IoC容器初始化完成以后,第一次向容器執行getBean時。具體的實現脈絡清晰而簡潔,如代碼清單2-31所示。

代碼清單2-31 refresh中的預實例化

        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                  prepareRefresh();
                  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
                  prepareBeanFactory(beanFactory);
                  try {
                      postProcessBeanFactory(beanFactory);
                      invokeBeanFactoryPostProcessors(beanFactory);
                      registerBeanPostProcessors(beanFactory);
                      initMessageSource();
                      initApplicationEventMulticaster();
                      onRefresh();
                      registerListeners();
                      // 這里是對lazy-init屬性進行處理的地方
                      finishBeanFactoryInitialization(beanFactory);
                            finishRefresh();
                      }
                  catch (BeansException ex) {
                      destroyBeans();
                      cancelRefresh(ex);
                      throw ex;
                  }
            }
        }
        //finishBeanFactoryInitialization中進行具體的處理過程
        protected void finishBeanFactoryInitialization(ConfigurableListable
        BeanFactory beanFactory) {
            beanFactory.setTempClassLoader(null);
            beanFactory.freezeConfiguration();
            /* 這里調用的是BeanFactory preInstantiateSingletons,
            這個方法是由DefaultListableBeanFactory實現的 */
            beanFactory.preInstantiateSingletons();
        }
        //DefaultListableBeanFactory中的preInstantiateSingletons是這樣的
        public void preInstantiateSingletons() throws BeansException {
            if (this.logger.isInfoEnabled()) {
                this.logger.info("Pre-instantiating singletons in " + this);
            }
            //在這里就開始getBean,也就是去觸發Bean的依賴注入
            /*這個getBean和前面分析的觸發依賴注入的過程是一樣的,只是發生的地方不同。如果不設置
              lazy-init屬性,那么這個依賴注入是發生在容器初始化結束以后。第一次向容器發出getBean時,
              如果設置了l a z y - i n i t 屬性,那么依賴注入發生在容器初始化的過程中,會對
              beanDefinitionMap中所有的Bean進行依賴注入,這樣在初始化過程結束以后,容器執行
              getBean得到的就是已經準備好的Bean,不需要進行依賴注入*/
            synchronized (this.beanDefinitionMap) {
                  for (String beanName : this.beanDefinitionNames) {
                      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                          if (isFactoryBean(beanName)) {
                                  FactoryBean factory = (FactoryBean) getBean
                                  (FACTORY_BEAN_PREFIX + beanName);
                                  if (factory instanceof SmartFactoryBean &&
                                  ((SmartFactoryBean) factory).isEagerInit()) {
                                          getBean(beanName);
                                  }
                          }
                          else {
                                  getBean(beanName);
                          }
                      }
                  }
            }
        }

根據上面的分析得知,可以通過lazy-init屬性來對整個IoC容器的初始化和依賴注入過程進行一些簡單的控制。這些控制是可以由容器的使用者來決定的,具體來說,可以通過在BeanDefinition中設置lazy-init屬性來進行控制。了解了這些控制原理,可以幫助我們更好地利用這些特性。

2.5.3 FactoryBean的實現

下面來介紹常見的FactoryBean是怎樣實現的。這些FactoryBean為應用生成需要的對象,這些對象往往是經過特殊處理的,如ProxyFactoryBean這樣的特殊Bean。FactoryBean的生產特性是在getBean中起作用的,看下面的調用:

        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

getObjectForBeanInstance做了哪些處理?整個調用過程中涉及的方法如圖2-17所示。在getObjectForBeanInstance的實現方法中可以看到在FactoryBean中常見的getObject方法的接口,詳細的實現過程如代碼清單2-32所示。

圖2-17 FactoryBean生產方法的調用

代碼清單2-32 FactoryBean特性的實現

        protected Object getObjectForBeanInstance(
              Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
              //如果這里不是對FactoryBean的調用,那么結束處理
              if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof
              FactoryBean)) {
              throw new BeanIsNotAFactoryException(transformedBeanName(name),
              beanInstance.getClass());
              }
              if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.
                isFactoryDereference(name)) {
              return beanInstance;
              }
              Object object = null;
              if (mbd == null) {
                    object = getCachedObjectForFactoryBean(beanName);
              }
              if (object == null) {
                    FactoryBean factory = (FactoryBean) beanInstance;
                    if (mbd == null && containsBeanDefinition(beanName)) {
                    mbd = getMergedLocalBeanDefinition(beanName);
                    }
                    boolean synthetic = (mbd != null && mbd.isSynthetic());
                    //這里從FactoryBean中得到bean
                    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
              }
              return object;
        }
        protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName,
        boolean shouldPostProcess) {
            if (factory.isSingleton() && containsSingleton(beanName)) {
                synchronized (getSingletonMutex()) {
                      Object object = this.factoryBeanObjectCache.get(beanName);
                      if (object == null) {
                            object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
                            this.factoryBeanObjectCache.put(beanName, (object != null ?
                            object : NULL_OBJECT));
                      }
                      return (object != NULL_OBJECT ? object : null);
                  }
              }
              else {
                  return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
              }
        }
        private Object doGetObjectFromFactoryBean(
                  final FactoryBean factory, final String beanName, final boolean
                  shouldPostProcess)
                  throws BeanCreationException {
                  AccessControlContext acc = AccessController.getContext();
                  return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                  public Object run() {
                      Object object;
                      //這里調用factorygetObject方法來從FactoryBean中得到Bean
                      try {
                            object = factory.getObject();
                      }
                      catch (FactoryBeanNotInitializedException ex) {
                            throw new BeanCurrentlyInCreationException(beanName, ex.toString());
                      }
                      catch (Throwable ex) {
                            throw new BeanCreationException(beanName, "FactoryBean threw
                            exception on object creation", ex);
                      }
                      if (object == null && isSingletonCurrentlyInCreation(beanName)) {
                            throw new BeanCurrentlyInCreationException(
                                    beanName, "FactoryBean which is currently in creation returned
                                    null from getObject");
                      }
                      if (object != null && shouldPostProcess) {
                          try {
                                object = postProcessObjectFromFactoryBean(object, beanName);
                          }
                          catch (Throwable ex) {
                                throw new BeanCreationException(beanName, "Post-processing of the
                                FactoryBean's object failed", ex);
                          }
                      }
                      return object;
                  }
              }, acc);
        }

這里返回的已經是作為工廠的FactoryBean生產的產品,而不是FactoryBean本身。這種FactoryBean的機制可以為我們提供一個很好的封裝機制,比如封裝Proxy、RMI、JNDI等。通過對FactoryBean實現過程的原理進行分析,相信讀者會對getObject方法有很深刻的印象。這個方法就是主要的FactoryBean的接口,需要實現特定的工廠的生產過程,至于這個生產過程是怎樣和IoC容器整合的,就是在上面分析的內容。

如圖2-18所示是一個典型的工廠模式的使用。在這里我們復習一下設計模式中的工廠模式,做一個對比,以加深對這些代碼的理解。

圖2-18 工廠模式

對比兩者的實現,可以看到FactoryBean類似于AbstractFactory抽象工廠,getObjectForBeanInstance( )方法類似于createProductA()這樣的生產接口,而具體的FactoryBean實現,如TransactionProxyFactoryBean,就是具體的工廠實現,其生成出的TransactionProxy就是“抽象工廠”模式中對應的ConcreteProduct。有了抽象工廠設計模式的參考和對比,對FactoryBean的設計和實現就更容易理解一些了。

2.5.4 BeanPostProcessor的實現

BeanPostProcessor是使用IoC容器時經常會遇到的一個特性,這個Bean的后置處理器是一個監聽器,它可以監聽容器觸發的事件。將它向IoC容器注冊后,容器中管理的Bean具備了接收IoC容器事件回調的能力。BeanPostProcessor的使用非常簡單,只需要通過設計一個具體的后置處理器來實現。同時,這個具體的后置處理器需要實現接口類BeanPostProcessor,然后設置到XML的Bean配置文件中。這個BeanPostProcessor是一個接口類,它有兩個接口方法,一個是postProcessBeforeInitialization,在Bean的初始化前提供回調入口;一個是postProcessAfterInitialization,在Bean的初始化后提供回調入口,這兩個回調的觸發都是和容器管理Bean的生命周期相關的。這兩個回調方法的參數都是一樣的,分別是Bean的實例化對象和Bean的名字。BeanPostProcessor為具體的處理提供基本的回調輸入,如代碼清單2-33所示。

代碼清單2-33 BeanPostProcessor接口定義

        public interface BeanPostProcessor {
        Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
        /**
        * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
        * initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
        * or a custom init-method). The bean will already be populated with property values.
        * The returned bean instance may be a wrapper around the original.
        * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
        * instance and the objects created by the FactoryBean (as of Spring 2.0). The
        * post-processor can decide whether to apply to either the FactoryBean or created
        * objects or both through corresponding <code>bean instanceof FactoryBean</code> checks.
        * <p>This callback will also be invoked after a short-circuiting triggered by a
        * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
        * in contrast to all other BeanPostProcessor callbacks.
        * /
        Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
        }

對于這些接口是在什么地方與IoC結合在一起的,可以查看一下以getBean方法為起始的調用關系,其調用過程如圖2-19所示。

圖2-19 以getBean方法為起始的調用過程

如果需要從源代碼實現的角度去了解以上過程,可以參考圖2-20所示的方法調用棧。

圖2-20 IoC容器觸發對postProcessBeforeInitialization接口的調用

postProcessBeforeInitialization是在populateBean完成之后被調用的。從BeanPostProcessor中的一個回調接口入手,對另一個回調接口postProcessAfterInitialization方法的調用,實際上也是在同一個地方封裝完成的,這個地方就是populateBean方法中的initializeBean調用。關于這一點,讀者會在接下來的分析中了解得很清楚。在前面對IoC的依賴注入進行分析時,對這個populateBean有過分析,這個方法實際上完成了Bean的依賴注入。在容器中建立Bean的依賴關系,是容器功能實現的一個很重要的部分。節選doCreateBean中的代碼就可以看到postProcessBeforeInitialization調用和populateBean調用的關系,如下所示。

        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            /*在完成對Bean的生成和依賴注入以后,開始對Bean進行初始化,這個初始化過程包含了對后置處理
              postProcessBeforeInitialization的回調 */
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }

具體的初始化過程也是IoC容器完成依賴注入的一個重要部分。在initializeBean方法中,需要使用Bean的名字,完成依賴注入以后的Bean對象,以及這個Bean對應的BeanDefinition。在這些輸入的幫助下,完成Bean的初始化工作,這些工作包括為類型是BeanNameAware的Bean設置Bean的名字,類型是BeanClassLoaderAware的Bean設置類裝載器,類型是BeanFactoryAware的Bean設置自身所在的IoC容器以供回調使用,當然,還有對postProcess-BeforeInitialization/postProcessAfterInitialization的回調和初始化屬性init-method的處理等。經過這一系列的初始化處理之后,得到的結果就是可以正常使用的由IoC容器托管的Bean了。具體的實現過程如代碼清單2-34所示。

代碼清單2-34 IoC容器對Bean的初始化

        //初始化Bean實例,調用在容器的回調方法和Bean的初始化方法
        protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            //這里是對后置處理器BeanPostProcessorspostProcessBeforeInitialization
            //回調方法的調用
            Object wrappedBean = bean;
            if (mbd == null || !mbd.isSynthetic()) {
                wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
            }
            //調用Bean的初始化方法,這個初始化方法是在BeanDefinition中通過定義init-method屬性指定的
            //同時,如果Bean實現了InitializingBean接口,那么這個BeanafterPropertiesSet
            //實現也會被調用
            try {
                invokeInitMethods(beanName, wrappedBean, mbd);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(
                        (mbd != null ? mbd.getResourceDescription() : null),
                        beanName, "Invocation of init method failed", ex);
            }
            //這里是對后置處理器BeanPostProcessorspostProcessAfterInitialization的回調方
            //法的調用
            if (mbd == null || !mbd.isSynthetic()) {
                wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            }
            return wrappedBean;
        }
        /*這里是對設置好的BeanPostProcessorspostProcessBeforeInitialization回調進行依次調
          用的地方*/
        public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean,
        String beanName)
                throws BeansException {
            Object result = existingBean;
            for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
                result = beanProcessor.postProcessBeforeInitialization(result, beanName);
            }
            return result;
        }
        //這里是對設置好的BeanPostProcessorspostProcessAfterInitialization回調進行
        //依次調用的地方
        public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {
            Object result = existingBean;
            for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
                result = beanProcessor.postProcessAfterInitialization(result, beanName);
            }
            return result;
        }

從以上的代碼實現中可以看到,這兩個Bean后置處理器定義的接口方法,一前一后,圍繞著Bean定義的init-method方法調用,與IoC容器對Bean的管理有機結合起來了。對這個特性的理解,離不開對IoC容器基本實現原理的理解。了解了Bean后置處理器的實現原理后,就能更靈活地使用它。IoC容器的附加特性還有很多,它們代表了容器的一些特色和高級的使用技巧,掌握這些特性對應用開發有很大的幫助。下面再分析一下容器的autowiring特性是怎樣實現的。

2.5.5 autowiring(自動依賴裝配)的實現

在前面對IoC容器實現原理的分析中,一直是通過BeanDefinition的屬性值和構造函數以顯式的方式對Bean的依賴關系進行管理的。在Spring中,相對這種顯式的依賴管理方式,IoC容器還提供了自動依賴裝配的方式,為應用使用容器提供更大的方便。在自動裝配中,不需要對Bean屬性做顯式的依賴關系聲明,只需要配置好autowiring屬性,IoC容器會根據這個屬性的配置,使用反射自動查找屬性的類型或者名字,然后基于屬性的類型或名字來自動匹配IoC容器中的Bean,從而自動地完成依賴注入。

這是一個很有誘惑力的功能特性,使用它可以完成依賴關系管理的自動化,但是使用時一定要注意,計算機只是在自動執行,它是不會思考的。使用這個特性的優點是能夠減少用戶配置Bean的工作量,但它是一把雙刃劍,如果使用不當,也會為應用帶來不可預見的后果,所以,使用時需要多一些小心和謹慎。

從autowiring使用上可以知道,這個autowiring屬性在對Bean屬性進行依賴注入時起作用。對Bean屬性依賴注入的實現原理,在前面已經做過分析?;仡櫮遣糠謨热荩浑y發現,對autowirng屬性進行處理,從而完成對Bean屬性的自動依賴裝配,是在populateBean中實現的。節選AbstractAutowireCapableBeanFactory的populateBean方法中與autowiring實現相關的部分,可以清楚地看到這個特性在容器中實現的入口。也就是說,對屬性autowiring的處理是populateBean處理過程的一個部分。在populateBean的實現中,在處理一般的Bean之前,先對autowiring屬性進行處理。如果當前的Bean配置了autowire_by_name和autowire_by_type屬性,那么調用相應的autowireByName方法和autowireByType方法。這兩個方法很巧妙地應用了IoC容器的特性。例如,對于autowire_by_name,它首先通過反射機制從當前Bean中得到需要注入的屬性名,然后使用這個屬性名向容器申請與之同名的Bean,這樣實際又觸發了另一個Bean的生成和依賴注入的過程。實現過程如代碼清單2-35所示。

代碼清單2-35 populateBean對autowiring屬性的處理

        //開始進行依賴注入過程,先處理autowiring的注入
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
            mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            // 這里是對autowire注入的處理,根據Bean的名字或者type進行autowire的過程
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                    autowireByName(beanName, mbd, bw, newPvs);
            }
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }

在對autowiring類型做了一些簡單的邏輯判斷以后,通過調用autowireByName和autowireByType來完成自動依賴裝配。以autowireByName為例子來看看容器的自動依賴裝配功能是怎樣實現的。對autowireByName來說,它首先需要得到當前Bean的屬性名,這些屬性名已經在BeanWrapper和BeanDefinition中封裝好了,然后是對這一系列屬性名進行匹配的過程。在匹配的過程中,因為已經有了屬性的名字,所以可以直接使用屬性名作為Bean名字向容器索取Bean,這個getBean會觸發當前Bean的依賴Bean的依賴注入,從而得到屬性對應的依賴Bean。在執行完這個getBean后,把這個依賴Bean注入到當前Bean的屬性中去,這樣就完成了通過這個依賴屬性名自動完成依賴注入的過程。autowireByType的實現和autowireByName的實現過程是非常類似的,感興趣的讀者可以自己進行分析。這些autowiring的實現如代碼清單2-36所示。

代碼清單2-36 autowire_by_name的實現

        protected void autowireByName(
        String beanName, AbstractBeanDefinition mbd, BeanWrapper bw,
        MutablePropertyValues pvs) {
            String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
            for (String propertyName : propertyNames) {
                if (containsBean(propertyName)) {
                      //使用取得的當前Bean的屬性名作為Bean的名字,向IoC容器索取Bean
                      //然后把從容器得到的Bean設置到當前Bean的屬性中去
                      Object bean = getBean(propertyName);
                      pvs.addPropertyValue(propertyName, bean);
                      registerDependentBean(propertyName, beanName);
                      if (logger.isDebugEnabled()) {
                            logger.debug(
                                  "Added autowiring by name from bean name '" + beanName + "'
                                  via property '" + propertyName +
                                  "' to bean named '" + propertyName + "'");
                      }
                }
                else {
                      if (logger.isTraceEnabled()) {
                            logger.trace("Not autowiring property '" + propertyName + "' of
                            bean '" + beanName +
                                  "' by name: no matching bean found");
                      }
                }
            }
        }

2.5.6 Bean的依賴檢查

在使用Spring的時候,如果應用設計比較復雜,那么在這個應用中,IoC管理的Bean的個數可能非常多,這些Bean之間的相互依賴關系也會非常復雜。在一般情況下,Bean的依賴注入是在應用第一次向容器索取Bean的時候發生,在這個時候,不能保證注入一定能夠成功,如果需要重新檢查這些依賴關系的有效性,會是一件很繁瑣的事情。為了解決這樣的問題,在Spring IoC容器中,設計了一個依賴檢查特性,通過它,Spring可以幫助應用檢查是否所有的屬性都已經被正確設置。在具體使用的時候,應用只需要在Bean定義中設置dependency-check屬性來指定依賴檢查模式即可,這里可以將屬性設置為none、simple、object、all四種模式,默認的模式是none。如果對檢查模式進行了設置,通過下面的分析,可以更好地理解這個特性的使用。具體的實現代碼是在AbstractAutowireCapableBeanFactory實現createBean的過程中完成的。在這個過程中,會對Bean的Dependencies屬性進行檢查,如果發現不滿足要求,就會拋出異常通知應用。

        lt@span b=1>protected voidlt@span b=1> checkDependencies(Stringlt@span b=1> beanName,lt@span b=1> AbstractBeanDefinitionlt@span b=1> mbd,
        PropertyDescriptor[] pds, PropertyValues pvs)
            throws UnsatisfiedDependencyException {
            int dependencyCheck = mbd.getDependencyCheck();
            for (PropertyDescriptor pd : pds) {
                  if (pd.getWriteMethod() != null && !pvs.contains(pd.getName())) {
                      boolean isSimple = BeanUtils.isSimpleProperty
                      (pd.getPropertyType());
                      boolean unsatisfied = (dependencyCheck == RootBeanDefinition.
                      DEPENDENCY_CHECK_ALL) ||
                            (isSimple && dependencyCheck == RootBeanDefinition.
                            DEPENDENCY_CHECK_SIMPLE) ||
                            (!isSimple && dependencyCheck == RootBeanDefinition.
                            DEPENDENCY_CHECK_OBJECTS);
                      if (unsatisfied) {
                            throw new UnsatisfiedDependencyException
                            (mbd.getResourceDescription(), beanName, pd.getName(),
                            "Set this property value or disable dependency checking for this bean.");
                      }
                  }
            }
        }

2.5.7 Bean對IoC容器的感知

容器管理的Bean一般不需要了解容器的狀態和直接使用容器,但在某些情況下,是需要在Bean中直接對IoC容器進行操作的,這時候,就需要在Bean中設定對容器的感知。Spring IoC容器也提供了該功能,它是通過特定的aware接口來完成的。aware接口有以下這些:

? BeanNameAware,可以在Bean中得到它在IoC容器中的Bean實例名稱。

? BeanFactoryAware,可以在Bean中得到Bean所在的IoC容器,從而直接在Bean中使用IoC容器的服務。

? ApplicationContextAware,可以在Bean中得到Bean所在的應用上下文,從而直接在Bean中使用應用上下文的服務。

? MessageSourceAware,在Bean中可以得到消息源。

? ApplicationEventPublisherAware,在Bean中可以得到應用上下文的事件發布器,從而可以在Bean中發布應用上下文的事件。

? ResourceLoaderAware,在Bean中可以得到ResourceLoader,從而在Bean中使用ResourceLoader加載外部對應的Resource資源。

在設置Bean的屬性之后,調用初始化回調方法之前,Spring會調用aware接口中的setter方法。以ApplicationContextAware為例,分析對應的設計和實現。這個接口定義得很簡單。

        public interface ApplicationContextAware {
            void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
        }

這里只有一個方法setApplicationContext(ApplicationContext applicationContext),它是一個回調函數,在Bean中通過實現這個函數,可以在容器回調該aware接口方法時使注入的applicationContext引用在Bean中保存下來,供Bean需要使用ApplicationContext的基本服務時使用。這個對setApplicationContext方法的回調是由容器自動完成的??梢钥吹?,一個ApplicationContextAwareProcessor作為BeanPostProcessor的實現,對一系列的aware回調進行了調用,比如對ResourceLoaderAware接口的調用,對ApplicationEventPublisherAware接口的調用,以及對MessageSourceAware和ApplicationContextAware的接口調用等。

        class ApplicationContextAwareProcessor implements BeanPostProcessor {
            private final ApplicationContext applicationContext;
            public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
                this.applicationContext = applicationContext;
            }
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws
        BeansException {
            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) bean).setResourceLoader(this. applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher
                (this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).
                setApplicationContext(this.applicationContext);
            }
            return bean;
        }
        public Object postProcessAfterInitialization(Object bean, String name) {
              return bean;
        }
        }

而作為依賴注入的一部分,postProcessBeforeInitialization會在initializeBean的實現過程中被調用,從而實現對aware接口的相關注入。關于initializeBean的詳細過程,感興趣的讀者可以參閱前面的章節進行回顧。

主站蜘蛛池模板: 天津市| 阿城市| 方城县| 伊通| 丰台区| 阜宁县| 邵阳市| 名山县| 临城县| 儋州市| 叙永县| 普宁市| 芦山县| 台前县| 祁东县| 进贤县| 肥东县| 桐乡市| 绥芬河市| 青田县| 岑巩县| 杭锦旗| 澜沧| 临夏市| 思茅市| 贺州市| 宁陵县| 台中县| 龙游县| 镇宁| 肇庆市| 花莲县| 蒙城县| 临夏市| 宁波市| 镶黄旗| 江都市| 凭祥市| 阿克苏市| 衢州市| 宁安市|