《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) { 獲取BeanDefinition的property屬性值 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; }
這個BeanDefinitionValueResolver對BeanDefinition的解析是在這個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(); 根據需要解析的內容,originalValue和propertyName判斷是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) { & |