1. 程式人生 > >《Spring技術內幕》---------IoC容器的實現

《Spring技術內幕》---------IoC容器的實現

1.Spring的整體架構

Spring IoC:包含了最基本的IoC容器的BeanFactory的介面與實現。BeanFactory系列容器只實現了最基本的IoC容器的功能;ApplicationContext應用上下文,作為容器的高階形態存在,增加了許多面向框架的特性,包括國際化和應用支援。

Spring Aop:整合AspectJ作為AOP的一個特定實現,同時還在JVM動態代理和CGLIB的基礎上,實現了AOP框架;宣告式事務,日誌處理,許可權判斷。

Spring MVC:以DispatcherServlet為核心,實現了MVC模式,包括怎樣與Web容器整合,Web請求的攔截、轉發、處理和ModelAndView資料的返回。

Spring JDBC/ORM:對JDBC進行封裝,使得通過JDBC完成的對資料庫的操作更加方便,還提供了JdbcTemplate作為模板類,封裝了基本資料庫的操作方法。還提供了許多ORM工具的封裝,例如HibernateTemplate等。

Spring事務處理:通過Spring AOP實現自身功能增強的典型模組。

Spring 遠端呼叫:通過Spring的封裝從Spring應用到Spring應用之間的端到端的呼叫。在這個過程中,通過Spring的封裝,為應用遮蔽了各種通訊和呼叫的細節,從而可以使用不同的遠端呼叫來實現,HTTP呼叫器已經傳統的RMI呼叫。

Spring 應用:通過這個模組使得Spring應用能夠簡潔的容納第三方的技術實現,豐富了Spring的應用功能。

2.Spring IoC容器的概述

控制反轉,控制權的反轉,控制權從具體業務物件手中轉到IoC容器中;如果相關物件的引用或依賴關係的管理由具體物件來完成,會導致程式碼的高度耦合。物件的依賴關係往往體現在對資料和方法的依賴上,這些依賴關係可以通過把物件的依賴注入交給框架或者IoC容器來完成,這種控制權的轉移能夠降低耦合提高可測試性。

控制反轉的實現有很多種方式,可以在物件初始化時,就進行資料注入,也可以通過物件引用注入到資料域中實現。物件之間的相互依賴關係由IoC容器進行管理,並由IoC容器完成物件的注入。

2.1IoC容器的設計

①BeanFactory-HierachicalBeanFactory-ConfigurableBeanFactory,HierachicalBeanFactory繼承了BeanFactory,增加了getParentBeanFactory的介面功能,ConfigurableBeanFactory介面,主要定義了一些對BeanFactory的配置功能。

②BeanFactory-ListableBeanFactory-ApplicationContext-ConfigurableApplicationContext,ListableBeanFactory細化了好多BeanFactory的介面功能,如定義了getBeanDefinitionNames介面方法,ApplicationContext通過整合MessageSource、ResourceLoader、ApplicationEventPublisher介面,添加了許多對高階容器的特性支援。

BeanFactory與FactoryBean,BeanFactory是IoC容器或者物件工廠,所有的Bean都是由BeanFactory進行管理的。FactoryBean是一個能夠產生物件的工廠Bean,使用&可以得到FactoryBean本身,不會得到FactoryBean產生的物件

 

2.1.1BeanFactory的應用場景

BeanFactory的getBean方法,可以取得IoC容器中管理的Bean,通過指定名字來索引,還可以帶Bean的型別,或者prototype型別的引數。

2.1.2 BeanFactory容器的設計原理

以XmlBeanFactory為例,簡單說明IoC容器的設計原理

只提供最基本的IoC容器的功能。XmlBeanFactory繼承了DefaultListableBeanFactory這個類,DefaultListableBeanFactory是經常需要用到的一個IoC的實現,ApplicationContext就會使用到該類,它包含了基本IoC容器所具有的重要功能。XmlBeanFactory在DefaultListableBeanFactory的基礎上增加了可以讀取xml檔案定義的BeanDefinition的功能。實現過程如下

XmlBeanFactory中初始化了一個XmlBeanDefinitionReader物件,該物件那個可以處理xml方式定義的BeanDefinition。構造XmlBeanFactory的時候,需要指定BeanDefinition的資訊來源(某個xml檔案),而這個資訊來源需要封裝成Resource類(用來封裝IO的類),例如ClassPathResource res = new ClassPathResource(“beans.xml”);將resource作為構造引數傳入到XmlBeanFactory的構造器中,呼叫loadBeanDefinition方法,從Resource中載入BeanDefinition。

理解程式設計方式使用IoC容器

①建立IoC配置檔案的抽象資源Resource,包含了BeanDefinition的定義

②建立一個BeanFactory,這裡使用DefaultListableBeanFactory

③建立一個載入BeanFactory的讀取器,使用XmlBeanDefinitionReader來載入xml檔案形式的BeanDefinition。

④呼叫loadBeanDefinition,完成載入資訊操作,載入和註冊bean後,就可以使用IoC容器了。

2.1.3ApplicationContext的應用場景

ApplicationContext是一個高階形態意義的IoC容器,提供了一些附加功能

①繼承了MessageSource介面,支援國際化實現

②繼承了ResourceLoader介面,可以從不同地方得到Bean定義資源

③繼承了ApplicationEventPublisher介面,引入了事件機制,與Bean生命週期結合為Bean的管理提供便利。

2.1.4 ApplicationContext容器的設計原理

以FileSystemXmlApplicationContext的實現為例

ApplicationContext主要功能已經在AbstractXmlApplicationContext中實現了,FileSystemXmlApplicationContext只需要實現與其自身設計相關的功能即可

①如果直接使用FileSystemXmlApplicationContext,對於例項化ApplicationContext的支援,同時啟動IoC容器的refresh過程

②怎樣從檔案系統中載入xml的bean定義資源

從xml中得到BeanDefinition的抽象資源,然後返回Resource

2.2 IoC容器的初始化過程

IoC的初始化過程是由refresh來啟動,這個方法標誌著IoC容器的正式啟動。啟動過程包括BeanDefinition的Resource定位、BeanDefinition的載入和BeanDefinition的註冊。

Spring把三個過程分開,並使用不同模組來完成,通過這樣的設計方式,可以讓使用者更加靈活地對這三個過程進行裁剪和擴充套件。

①Resource定位,由ResourceLoader通過統一的Resource介面來完成,可以使用上述getResourceByPath完成,也可以使用ClassPathResource來完成。

②BeanDefinition載入,使用者定義好的Bean表示成IoC容器內部的資料結構,而這個容器的內部資料結構就是BeanDefinition。

③註冊BeanDefinition,呼叫BeanDefinitionRegistry介面實現。IoC容器內部將BeanDefinition注入到一個HashMap中去,通過HashMap持有這些BeanDefinition

注意:一般來說初始化和依賴注入是兩個分開的過程,依賴注入是在第一次呼叫getBean時進行的,即預設的lazyinit屬性是true,有個例外,對bean設定了lazyinit屬性為false,並且是singleton之後,那麼這個Bean的依賴注入就會在容器初始化時就預先完成了。如果是prototype型別,那麼即使lazyinit是false,那麼也不會在初始化就進行依賴注入,詳情見DefaultListableBeanFactory中的preInstantiateSingletons()方法

2.2.1 Resource定位

以FileSystemXmlApplicationContext為例,通過繼承AbstractApplicationContext具備了ResourceLoader從Resource中讀入BeanDefinition的能力;

getResourceBypath方法用於BeanDefinition的定位,構造器中的refresh方法用於實現BeanDefinition的載入過程。FileSystemXmlApplicationContext的refresh方法呼叫AbstractRefreshableApplicationContext的refreshBeanFactory方法。refreshBeanFactory通過createBeanFactory構建了一個IoC容器供ApplicationContext使用,就是defaultlistableBeanfactory,然後呼叫loadBeanDefinitions來載入BeanDefinition,方法引數就是defaultlistableBeanfactory;該過程實際上呼叫的是AbstractApplicationContext裡面的loadBeanDefinitions,然後初始化了XmlBeanDefinitionReader,呼叫loadBeanDefinitions(XmlBeanDefinitionReader reader)方法,裡面有獲取getConfigLocations,之後就會在AbstractBeanDefinitionReader裡面的loadBeanDefinitions(String location, Set<Resource> actualResources)方法getResourceLoader,此時get到的ResourceLoader就是FileSystemXmlApplicationContext,然後就會呼叫ResourceLoader的getResource方法,由於FileSystemXmlApplicationContext繼承了DefaultResourceLoader,所以呼叫其中的getResource方法;如下。

由於FileSystemXMLApplicationContext設定了configLocation,所以用的是reader.loadBeanDefinitions(configLocations);這個方法,此方法在getResource得到Resource之後就會進行BeanDefinition的載入

由於AbstractBeanDefinitionReader沒有實現loadBeanDefinitions(resource);因此呼叫xmlBeanDefinitionReader類中的loadBeanDefinitions(resource);進行載入;

2.2.2 BeanDefinition的載入和解析

Refresh方法的實現,是在AbstractApplicationContext中,程式碼如下,BeanFactory的更新,此方法在AbstractRefreshableApplicationContext類的refreshBeanFactory方法

如果有舊的beanFactory,就銷燬和關閉並且建立新的BeanFactory;MessageSource和PostProcessor的註冊等。

真正BeanDefinition的載入過程在refreshBeanFactory方法中

loadBeanDefinitions是一個抽象方法,在AbstractXmlApplicationContext中實現,在該方法中,初始化了XmlBeanDefinitionReader,然後把這個讀取器在IoC容器中設定好,在loadBeanDefinitions(beanDefinitionReader);呼叫reader.loadBeanDefinitions(configLocations)完成BeanDefinition的載入

然後就是loadBeanDefinitions呼叫,首先得到Resource定位,具體由子類進行,此時由於FileSystemXmlApplicationContext不是ClassPathXmlApplicationContext,所以configResources返回null,進行getConfigLocation載入。

reader.loadBeanDefinitions(configLocations)在AbstractBeanDefinitionReader中,然後進行資源的定位以及載入;

上面已經分析過,得到resource之後,進行真正的載入過程,此時的loadBeanDefinitions(resource)在XmlBeanDefinitionReader中,

得到輸入流之後,就可以採用doLoadBeanDefinitions方法進行xml檔案解析

獲取Document物件,採用SAX解析xml;Spring的BeanDefinition是怎麼樣按照Spring的Bean語義要求進行解析並轉化為內部資料結構的,這個過程由registerBeanDefinitions完成;

parseBeanDefinition到下面的方法

建立一個BeanDefinitionDocumentReader,預設是DefaultBeanDefinitionDocumentReader,使用BeanDefinitionParserDelegate進行解析Document物件,解析後的結果

放在BeanDefinition中,並將beanName,aliase作為引數儲存到BeanDefinitionHolder中。

一層一層呼叫解析,這樣xml中定義的BeanDefinition就被載入到IoC容器中了,並在容器中建立了資料的對映。這個時候容器還沒有完全起作用,要完全發揮容器作用,還需要進行註冊

2.2.3 BeanDefinition在IoC中的註冊

載入和解析過程,已經將使用者定義的BeanDefinition資訊在IoC容器中建立起了自己的資料結構,但是還需要進行註冊才能使用。在DefaultListableBeanFactory中,通過一個ConcurrentHashMap來持有載入的BeanDefinition

在xmlBeanDefinitionReader傳入的DefaultListableBeanFactory,就是設定AbstractBeanDefinitionReader中的registry為DefaultListableBeanFactory。因此在DefaultBeanDefinitionDocumentReader的processBeanDefinition方法中BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());就是用來註冊BeanDefinition;

呼叫的是DefaultListableBeanFactory的registerBeanDefinition(String, BeanDefinition)方法

 

註冊之後,BeanDefinition就可以被使用了。

2.3 依賴注入

依賴注入過程是使用者第一次呼叫getBean時觸發的,例外就是lazy-init屬性的預例項化,後面會講。GetBean呼叫完之後會觸發doGetBean;就是用於依賴注入;getBean-dogetBean-createbean;

AbstractBeanFactory中的dogetBean方法會呼叫AbstractAutowireCapableBeanFactory的CreateBean方法;該方法生成了需要的bean,還對bean初始化進行了處理,定義後置處理器,定義init-method屬性等;createBean中呼叫了doCreateBean方法

該方法中主要就是createBeanInstance和populateBean方法,createBeanInstance生成了Bean所包含的java物件,這個物件的生成有很多種不同的方式,可以通過工廠方法生成,也可以通過容器的autowire特性生成。

使用不同的方式進行bean的例項化

使用cglib進行bean例項化,cglib是一個常用的位元組碼生成器的類庫,提供了生成和轉換位元組碼的功能。Cglib如何生成bean物件需要看simpleInstantiationStrategy。提供了兩種例項化物件的方法,一種是BeanUtils,使用JVM的反射功能,一種使用cglib生成

例項化物件後,需要為依賴關係進行處理,即使用populateBean方法,在AbstractAutowireCapableBeanFactory中;

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {

獲取BeanDefinitionproperty屬性值

       PropertyValues pvs = mbd.getPropertyValues();

 

       if (bw == null) {

           if (!pvs.isEmpty()) {

              throw new BeanCreationException(

                     mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");

           }

           else {

              // Skip property population phase for null instance.

              return;

           }

       }

       boolean continueWithPropertyPopulation = true;

開始依賴注入過程,先處理autowired的注入

       if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||

              mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {

           MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

 

           autowired注入的處理,可以根據名字或者型別來完成bean注入

           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;

       }

 

       boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();

       boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

 

       if (hasInstAwareBpps || needsDepCheck) {

           PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);

           if (hasInstAwareBpps) {

              for (BeanPostProcessor bp : getBeanPostProcessors()) {

                  if (bp instanceof InstantiationAwareBeanPostProcessor) {

                     InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;

                     pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);

                     if (pvs == null) {

                         return;

                     }

                  }

              }

           }

           if (needsDepCheck) {

              checkDependencies(beanName, mbd, filteredPds, pvs);

           }

       }

         然後對屬性進行注入

       applyPropertyValues(beanName, mbd, bw, pvs);

    }

 

通過applyPropertyvalues瞭解具體的對屬性行進行解析然後注入的過程

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {

       if (pvs == null || pvs.isEmpty()) {

           return;

       }

 

       MutablePropertyValues mpvs = null;

       List<PropertyValue> original;

 

       if (System.getSecurityManager() != null) {

           if (bw instanceof BeanWrapperImpl) {

              ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());

           }

       }

 

       if (pvs instanceof MutablePropertyValues) {

           mpvs = (MutablePropertyValues) pvs;

           if (mpvs.isConverted()) {

              // Shortcut: use the pre-converted values as-is.

              try {

                  bw.setPropertyValues(mpvs);

                  return;

              }

              catch (BeansException ex) {

                  throw new BeanCreationException(

                         mbd.getResourceDescription(), beanName, "Error setting property values", ex);

              }

           }

           original = mpvs.getPropertyValueList();

       }

       else {

           original = Arrays.asList(pvs.getPropertyValues());

       }

 

       TypeConverter converter = getCustomTypeConverter();

       if (converter == null) {

           converter = bw;

       }

       

        這個BeanDefinitionValueResolverBeanDefinition的解析是在這個valueResolver中完成的

       BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

為解析之後的屬性值建立一個副本,副本資料將會被注入到bean

       // Create a deep copy, resolving any references for values.

       List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());

       boolean resolveNecessary = false;

       for (PropertyValue pv : original) {

           if (pv.isConverted()) {

              deepCopy.add(pv);

           }

           else {

              String propertyName = pv.getName();

              Object originalValue = pv.getValue();

根據需要解析的內容,originalValuepropertyName判斷是reference還是list還是其他集合

              Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);

              Object convertedValue = resolvedValue;

              boolean convertible = bw.isWritableProperty(propertyName) &&

                      !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);

              if (convertible) {

                  convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);

              }

              // Possibly store converted value in merged bean definition,

              // in order to avoid re-conversion for every created bean instance.

              if (resolvedValue == originalValue) {

                  if (convertible) {

                     pv.setConvertedValue(convertedValue);

                  }

                  deepCopy.add(pv);

              }

              else if (convertible && originalValue instanceof TypedStringValue &&

                     !((TypedStringValue) originalValue).isDynamic() &&

                     !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {

                  pv.setConvertedValue(convertedValue);

                  deepCopy.add(pv);

              }

              else {

                  resolveNecessary = true;

                  deepCopy.add(new PropertyValue(pv, convertedValue));

              }

           }

       }

       if (mpvs != null && !resolveNecessary) {

           mpvs.setConverted();

       }

這裡是依賴注入發生的地方,會在beanWrapperImpl中完成

       // Set our (possibly massaged) deep copy.

       try {

           bw.setPropertyValues(new MutablePropertyValues(deepCopy));

       }

       catch (BeansException ex) {

           throw new BeanCreationException(

                  mbd.getResourceDescription(), beanName, "Error setting property values", ex);

       }

    }

 

 

大致過程:第一個呼叫getBean的時候,getBean會呼叫AbstractBeanFactory的createBean方法;createBean方法會呼叫doCreateBean方法得到一個Bean的引用;doCreateBean方法中定義了一個BeanWrapper用來持有創建出來bean物件,主要有兩個過程,一是例項化bean,二是設定bean的依賴關係;createInstance方法中有兩種方法對bean進行例項化,通過BeanUtils,使用jvm反射,一種是使用cglib生成;例項化物件之後,就需要設定bean的依賴關係;populateBean方法中先取得載入解析過程的property的值;先處理autowire的注入,根據bean的名字,型別等;然後處理property的注入;呼叫applyPropertyValues方法;方法中定義了一個BeanDefinitionValueResolver,設定一個需要注入的屬性值的副本deepcopy,遍歷屬性值,使用解析器BeanDefinitionValueResolver進行屬性值的解析;然後新增到deepcopy中。

上述的過程是為解析準備條件,真正把bean物件設定到另外一個依賴bean是在bw.setPropertyValues(new MutablePropertyValues(deepCopy));具體是在子類中實現了注入過程;通過幾個地方的遞迴將,集合map,list和非集合的屬性值注入到bean中,然後返回到BeanWapper,最終得到注入完成的Bean的例項。

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

2.4.1 ApplicationContext和Bean的初始化及銷燬

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

容器的實現是通過IoC管理Bean的生命週期實現的。

 

Bean的生命週期:

①bean的初始化和例項化

②設定bean的屬性,依賴注入

③應用可以通過IoC容器使用bean

④當容器關閉時,呼叫bean的銷燬方法

Bean的初始化方法呼叫在initializeBean中實現:該方法在AbstractAutowireCapableBeanFactory類中

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

       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()) {

           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;

    }

在呼叫invokeInitMethods方法進行初始化之前,會先呼叫一系列的aware介面實現,把相關的BeanClassLoader,BeanFactory注入到Bean中。如果有後置處理器BeanPostProcessor,就將其與bean進行關聯,呼叫postProcessBeforeInitialization()方法;接著才會呼叫invokeInitMethods的呼叫,方法中還會呼叫afterPropertiesSet回撥方法,最後判斷是否配置了initMethod,如果有通過invokeCustomInitMethod呼叫,該方法中首先需要得到bean定義的initMethod,然後通過jdk反射得到Method物件,然後使用invoke呼叫該初始化方法。如果有後置處理器BeanPostProcessor,在呼叫postProcessAfterInitialization方法,接下來就可以使用Bean了;關閉過程由容器呼叫

protected void doClose() {

       boolean actuallyClose;

       synchronized (this.activeMonitor) {

&