深刻懂得Java的Spring框架中的IOC容器。本站提示廣大學習愛好者:(深刻懂得Java的Spring框架中的IOC容器)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻懂得Java的Spring框架中的IOC容器正文
Spring IOC的原型
spring框架的基本焦點和終點毫無疑問就是IOC,IOC作為spring容器供給的焦點技巧,勝利完成了依附的反轉:從主類的對依附的自動治理反轉為了spring容器對依附的全局掌握。
如許做的利益是甚麼呢?
固然就是所謂的“解耦”了,可使得法式的各模塊之間的關系更加自力,只須要spring掌握這些模塊之間的依附關系並在容器啟動和初始化的進程中將根據這些依附關系創立、治理和保護這些模塊就好,假如須要轉變模塊間的依附關系的話,乃至都不須要轉變法式代碼,只須要將更改的依附關系停止修正便可,spring會在再次啟動和初始化容器的進程中使得這些新的依附關系從新樹立相符新需求的模塊,在這個進程中,須要留意的是代碼自己不須要表現關於模塊詳細依附情況的聲明而只須要界說其所需模塊的接口,所以這是一種典范的面向接口思惟,同時最好將依附關系以設置裝備擺設文件或許注解的情勢表述出來,相干的spring處置類會依據這些內部的設置裝備擺設文件組裝模塊,或許掃描注解挪用外部的注解處置器組裝模塊,以此完成IOC的進程。
IOC的目標是稱為DI的依附注入,經由過程IOC技巧,終究容器將贊助我們完成模塊間的依附注入。
別的,終究的一點是,在spring IOC的進程中,我們必需一直清晰以上這條主線,即時語法和類的構造再龐雜,然則其感化和目標都是一樣的:就是經由過程依附描寫的設置裝備擺設文件這一拆卸“圖紙”去完成模塊的“組裝”,龐雜的語法只是完成這一目標的手腕而已。
所謂的IOC原型,為了展現最簡略的IOC道理圖,我們無妨做一個完整簡略的原型來講明這個進程:
起首是我們界說的幾個模塊,包含主模塊和兩個接口界說的依附模塊:
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"); } }
假如我們不采取IOC,而是依附主模塊自己去掌握其依附模塊的創立,那末會是如許的:
public class SimpleIOCDemo { public static void main(String[] args) throws ClassNotFoundException { MainModule mainModule = new MainModule(); mainModule.setModuleA(new DependModuleAImpl()); mainModule.setModuleB(new DependModuleBImpl()); mainModule.getModuleA().funcFromModuleA(); mainModule.getModuleB().funcFromModuleB(); } }
這是我們經由簡化界說的IOC容器原型,容器在啟動後初始化的時刻會讀取用戶寫入的設置裝備擺設文件,這裡我們以簡略的properties設置裝備擺設文件為例,只要當用戶調取getBean辦法的時刻才會真正地依照設置裝備擺設文件組裝加載響應的bean,在我們界說的容器原型外部保護著一個用於保留拆卸好的bean 的map,假如在個中有知足請求的bean的話就不須要再新建了:
class SimpleIOCContainer{ private Properties properties = new Properties(); private Map<String, Object> moduleMap = new HashMap<>(); { try { properties.load(new FileInputStream(new File("SimpleIOC.properties"))); } catch (Exception e) { e.printStackTrace(); } } public Object getBean(String moduleName) throws ClassNotFoundException { Object instanceObj; if(moduleMap.get(moduleName)!=null){ System.out.println("return old bean"); return moduleMap.get(moduleName); } System.out.println("create new bean"); String fullClassName = properties.getProperty(moduleName); if(fullClassName == null) throw new ClassNotFoundException(); else{ Class<? extends Object> clazz = Class.forName(fullClassName); try { instanceObj = clazz.newInstance(); instanceObj = buildAttachedModules(moduleName,instanceObj); moduleMap.put(moduleName, instanceObj); return instanceObj; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return null; } private Object buildAttachedModules(String modulename , Object instanceObj) { Set<String> propertiesKeys = properties.stringPropertyNames(); Field[] fields = instanceObj.getClass().getDeclaredFields(); for (String key : propertiesKeys) { if(key.contains(modulename)&&!key.equals(modulename)){ try { Class<? extends Object> clazz = Class.forName(properties.getProperty(properties.getProperty(key))); for (Field field : fields) { if(field.getType().isAssignableFrom(clazz)) field.set(instanceObj, clazz.newInstance()); } } catch (Exception e) { e.printStackTrace(); } } } return instanceObj; } }
這是我們應用properties設置裝備擺設文件寫成的依附關系設置裝備擺設文件,這個設置裝備擺設文件是我們拆卸模塊的“圖紙”,這裡的語法個是完整是我們界說的,在真實的spring IOC容器中,為了表達更加龐雜的依附邏輯,會應用更加蓬勃的xml格局設置裝備擺設文件或許更新的注解設置裝備擺設,依附注解處置器來完成圖紙的解析:
mainModule=com.rocking.demo.MainModule mainModule.moduleA=moduleA mainModule.moduleB=moduleB moduleA=com.rocking.demo.DependModuleAImpl moduleB=com.rocking.demo.DependModuleBImpl
這是測試代碼,可以看到的是我們可以完全的經由過程我們界說的IOC容器獲得到相符請求的模塊,同時也能夠發明我們界說的容器可認為我們保護這些bean,當有bean曾經組裝創立出來以後就不須要再創立了。
public class SimpleIOCDemo { public static void main(String[] args) throws ClassNotFoundException { SimpleIOCContainer container = new SimpleIOCContainer(); DependModuleA moduleA = (DependModuleA) container.getBean("moduleA"); moduleA.funcFromModuleA(); DependModuleB moduleB = (DependModuleB) container.getBean("moduleB"); moduleB.funcFromModuleB(); MainModule mainModule = (MainModule) container.getBean("mainModule"); mainModule.getModuleA().funcFromModuleA(); mainModule.getModuleB().funcFromModuleB(); container.getBean("mainModule"); } }
這就是我根據IOC的根本思惟創立的IOC容器原型,spring IOC固然語法龐雜,然則說究竟完成的義務在焦點上都是一樣的,所謂的“萬變不離其宗”。
Spring IOC 的詳細進程
上回展現了IOC的年夜致完成的原型,那末在Spring框架中詳細是怎樣完成這個容器依據metadata元信息設置裝備擺設加載POJO的進程的呢?在全部Spring IOC容器的任務進程中有許多處所是設計地相當靈巧的,供應應用者許多空間去完成本身的義務,而不是一味地只是完成容器的機械進程。
這是全部IOC容器任務進程的進程圖:
1、容器啟動階段
(1)加載設置裝備擺設文件信息
(2)解析設置裝備擺設文件信息
(3)拆卸BeanDefinition
(4)後處置
起首設置裝備擺設文件或許注解等元信息和JavaBean的類信息被加載到IOC容器中,容器讀取到xml格局的設置裝備擺設文件,這個設置裝備擺設文件是應用者聲明的依附關系和拆卸中須要特殊存眷的處所,是拆卸Bean的晚期“內部圖紙”,容器中的解析引擎可以把我們寫入的文本情勢的字符元信息解析成容器外部可以辨認的BeanDefinition,可以把BeanDefinition懂得成為相似反射機制的類構造,這個經由過程對JavaBean和設置裝備擺設文件停止剖析獲得的BeanDefinition獲得了組裝一個相符請求的JavaBean的根本構造,假如須要除BeanDefinition以後還要對這個BeanDefinition再做修正的話則履行這個後處置,後處置普通是經由過程Spring框架內的BeanFactoryPostProcessor處置的。
我們依然應用前次應用過的例子來講明這個BeanDefinition的運作道理:有三個bean,主模塊MainModule和依附模塊DependModuleA,DependModuleB,前者依附前面兩個模塊組成,在設置裝備擺設文件裡我們普通會這麼停止依附的聲明:
<?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容器的完成之一)對下面設置裝備擺設文件的拆卸:
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界說地位處的某些信息。
2、Bean 實例化階段
有了經由處置的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容器的基本得以穩定。