上回展現了IOC的大體實現的原型,那麼在Spring框架中具體是怎麼實現這個容器根據metadata元信息配置加載POJO的過程的呢?在整個Spring IOC容器的工做過程當中有不少地方是設計地至關靈活的,供給使用者不少空間去完成本身的任務,而不是一味地只是完成容器的機械過程。spring
這是整個IOC容器工做過程的過程圖:安全
加載配置文件信息app
解析配置文件信息框架
裝配BeanDefinitionide
後處理函數
首先配置文件或者註解等元信息和JavaBean的類信息被加載到IOC容器中,容器讀取到xml格式的配置文件,這個配置文件是使用者聲明的依賴關係和裝配中須要特別關注的地方,是裝配Bean的早期「外部圖紙」,容器中的解析引擎能夠把咱們寫入的文本形式的字符元信息解析成容器內部能夠識別的BeanDefinition,能夠把BeanDefinition理解成爲相似反射機制的類結構,這個經過對JavaBean和配置文件進行分析獲得的BeanDefinition獲取了組裝一個符合要求的JavaBean的基本結構,若是須要除了BeanDefinition以後還要對這個BeanDefinition再作修改的話則執行這個後處理,後處理通常是經過Spring框架內的BeanFactoryPostProcessor處理的。post
咱們仍然使用上次使用過的例子來講明這個BeanDefinition的運做原理:有三個bean,主模塊MainModule和依賴模塊DependModuleA,DependModuleB,前者依賴後面兩個模塊構成,在配置文件裏咱們通常會這麼進行依賴的聲明:this
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="mainModule" class="com.rocking.demo.MainModule"> <property name="moduleA"> <ref bean="moduleA"/> </property> <property name="moduleB"> <ref bean="moduleB"/> </property> </bean> <bean id="moduleA" class="com.rocking.demo.DependModuleAImpl"></bean> <bean id="moduleB" class="com.rocking.demo.DependModuleBImpl"></bean> </beans>
這是咱們的程序演示一個標準的BeanFactory容器(Spring IOC容器的實現之一)對上面配置文件的裝配:spa
class MainModule { private DependModuleA moduleA; private DependModuleB moduleB; public DependModuleA getModuleA() { return moduleA; } public void setModuleA(DependModuleA moduleA) { this.moduleA = moduleA; } public DependModuleB getModuleB() { return moduleB; } public void setModuleB(DependModuleB moduleB) { this.moduleB = moduleB; } } interface DependModuleA { public void funcFromModuleA(); } interface DependModuleB { public void funcFromModuleB(); } class DependModuleAImpl implements DependModuleA { @Override public void funcFromModuleA() { System.out.println("This is func from Module A"); } } class DependModuleBImpl implements DependModuleB { @Override public void funcFromModuleB() { System.out.println("This is func from Module B"); } } public class SimpleIOCDemo { public static void main(String[] args) throws ClassNotFoundException { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions("Beans.xml"); MainModule mainModule = (MainModule) beanFactory.getBean("mainModule"); mainModule.getModuleA().funcFromModuleA(); mainModule.getModuleB().funcFromModuleB(); } }
這裏咱們的配置文件和JavaBean被加載讀取並被解析,這裏的BeanDefinition生成使用過程掩藏在其中,這是實際上在IOC內部發生的大體過程:設計
public class SimpleIOCDemo { public static void main(String[] args) throws ClassNotFoundException { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); AbstractBeanDefinition mainModule = new RootBeanDefinition(MainModule.class); AbstractBeanDefinition moduleA = new RootBeanDefinition(DependModuleAImpl.class); AbstractBeanDefinition moduleB = new RootBeanDefinition(DependModuleBImpl.class); beanFactory.registerBeanDefinition("mainModule", mainModule); beanFactory.registerBeanDefinition("moduleA", moduleA); beanFactory.registerBeanDefinition("moduleB", moduleB); MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.add("moduleA", moduleA); propertyValues.add("moduleB", moduleB); mainModule.setPropertyValues(propertyValues); MainModule module = (MainModule) beanFactory.getBean("mainModule"); module.getModuleA().funcFromModuleA(); module.getModuleB().funcFromModuleB(); } }
對xml的元信息進行加載讀取後,IOC解析引擎會將其中提到的模塊依據其真實類型建立成BeanDefinition,這個BeanDefinition能夠當作是一種反射或者代理的過程,目的是爲了讓IOC容器清楚之後要建立的實例對象的bean結構,而後將這些bean結構註冊到BeanFactory中去,以後將主模塊的依賴以setter注入的形式加入到主模塊的屬性中去,(這一點要看主模塊提供的是setter方法仍是初始化方法),這個過程結束後註冊完全部「圖紙」上規定的bean的Definition後,BeanFactory就已經成型。以後只要調用getBean方法便可將符合要求的bean生產出來,這是下一階段的過程,咱們以後再說。
在將BeanDefinition這一「圖紙」上的信息註冊到BeanFactory完畢後,咱們仍然能夠對已經註冊完的BeanDefinition進行改動的操做,這就是咱們前面提到的Spring爲使用者設計的靈活的地方之一,不是說全部的過程不可控,而是在不少地方留了不少使用者能夠發揮的餘地。具體的辦法是使用BeanFactory處理器BeanFactoryPostProcessor來介入對BeanFactory的處理以進一步改寫咱們須要修改的BeanDefinition部分。這個過程對應流程裏的「後處理」過程。
以常見的處理器之一:屬性佔位符配置處理器爲例,就是在已經構建完成已註冊完畢的BeanFactory以後再對它處理,以使得BeanDefinition相應屬性裏的內容修改成配置處理器指定配置文件裏的信息:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions( new ClassPathResource( "Beans.xml")); PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); configurer.setLocation( new ClassPathResource( "about.properties")); configurer.postProcessBeanFactory( beanFactory);
BeanFactoryPostProcessor將對BeanFactory處理,處理的結果就是把BeanDefinition中定義的某些屬性改爲BeanFactoryPostProcessor定義位置處的某些信息。
有了通過處理的BeanDefinition的「內部圖紙」的指導下,容器能夠進一步把BeanDefifnition經過反射或CGLIB動態字節碼生產的方式化爲存在於內存中的活化實例對象,再將BeanDefinition規定的依賴對象經過setter注入或者初始化注入的方式裝配進新建立的實例對象中,這裏是實實在在地將依賴對象的引用賦給須要依賴的對象屬性中。
可是這裏須要注意的是建立的實例不只僅是一個簡單的bean定義的實例,而是一個通過Spring包裝的BeanWrapper實例,這裏爲何要採用BeanWrapper的方式來包裝bean呢?是由於BeanWrapper提供了統一訪問bean屬性的接口,在建立完了基本的bean的框架後要對其中的屬性進行設置,每一個bean的setter方法都不同,因此若是直接用反射設置的話會很是複雜,因此spring提供這種包裝來簡化屬性設置:
BeanWrapper beanWrapper = new BeanWrapperImpl(Class.forName("com.rocking.demo.MainModule"));
beanWrapper.setPropertyValue( "moduleA", Class.forName("com.rocking.demo.DepModuleAImpl").newInstance());
beanWrapper.setPropertyValue( "moduleB", Class.forName("com.rocking.demo.DepModuleBImpl").newInstance());
MainModule mainModule= (MainModule) beanWrapper.getWrappedInstance();
mainModule.getModuleA().funcFromA();
mainModule.getModuleB().funcFromB();
以上的過程展現了在Spring內部,經過獲取類的反射容器瞭解未來包裝的實例bean的結構並做出包裝,使用統一的屬性設置方法setPropertyValue來對這個包裝的實例設置屬性,最後獲得的bean實例經過getWrappedInstance拿到,能夠發現已經成功將其屬性賦值。
這個時候的bean實例其實已經徹底可使用了,可是Spring一樣在實例化階段也爲咱們準備了靈活的策略以完成使用者對這個階段的介入,和容器啓動階段的BeanFactoryPostProcessor控制BeanDefinition相似,在實例化階段,Spring提供了BeanPostProcessor處理器來對已經裝配好的實例進行操做,以完成可能須要的改動:、
這裏舉個例子來講明,定義一個BeanPostProcessor的實現類,實現其中的方法postProcessAfterInitialization和postProcessBeforeInitialization來定義對在bean實例裝配以後和以前分別進行的操做,在BeanFactory添加了這個處理器後就會在每次調用getBean方法裝配實例的時候,都會傳入根據「圖紙」裝配出的bean實例(包括裝配過程當中建立的依賴實例bean)調用這兩個方法,這些方法能夠對這些bean實例實施修改。
下面是一個這樣的例子(MainModule及其依賴關係和本文以前的例子相同):
class ModuleC { private String x; public String getX() { return x; } public void setX(String x) { this.x = x; } } class ModulePostProcessor implements BeanPostProcessor{ @Override public Object postProcessAfterInitialization(Object object, String string) throws BeansException { System.out.println(string); if(object instanceof ModuleC){ System.out.println(string); ((ModuleC)object).setX("after"); } return object; } @Override public Object postProcessBeforeInitialization(Object object, String string) throws BeansException { if(object instanceof ModuleC){ ((ModuleC)object).setX("before"); } return object; } } public class VerySimpleIOCKernal { public static void main(String[] args) throws ClassNotFoundException, BeansException, InstantiationException, IllegalAccessException { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions(new ClassPathResource("Beans.xml")); ModulePostProcessor postProcessor = new ModulePostProcessor(); beanFactory.addBeanPostProcessor(postProcessor); MainModule module = (MainModule) beanFactory.getBean("mainModule"); ModuleC moduleC = (ModuleC) beanFactory.getBean("moduleC"); System.out.println(moduleC.getX()); } }
這是bean的依賴關係配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="mainModule" class="com.rocking.demo.MainModule"> <property name="moduleA"> <ref bean="moduleA"/> </property> <property name="moduleB"> <ref bean="moduleB"/> </property> </bean> <bean id="moduleA" class="com.rocking.demo.DepModuleAImpl"> <property name="infoA"> <value>${moduleA.infoA}</value> </property> </bean> <bean id="moduleB" class="com.rocking.demo.DepModuleBImpl"> <property name="infoB"> <value>info of moduleB</value> </property> </bean> <bean id="moduleC" class="com.rocking.demo.ModuleC"> </bean> </beans>
從最終的結果咱們能夠看出,每次調用getBean方法獲得的bean實例(包括因依賴關係生成的)都將被BeanPostProcessor獲取進行前置和後置處理。
除了相似上面的BeanPostProcessor的辦法對裝配好的bean再作處理外,Spring還能夠經過配置init-method和destroy-method來對bean的初始化和銷燬過程設置回調函數,這些回調函數也還能夠靈活地提供更改bean實例的機會。
整個Spring IOC的過程其實整體來講和咱們本身寫的IOC原型在本質上是同樣的,只不過經過複雜的設計使得IOC的過程可以更靈活有效地提供給使用者更多的發揮空間,除此以外,Spring的IOC也在安全性、容器的穩定性、metadata到bean轉換的高效性上作到了精美的設計,使得IOC這一Spring容器的基礎得以穩固。