控制反轉(Inversion of Control,英文縮寫為IoC)是一個重要的面向對象編程的法則來削減電腦程式的耦合問題,也是輕量級的Spring框架的核心。 控制反轉一般分為兩種類型,依賴註入(Dependency Injection,簡稱DI)和依賴查找。依賴註入應用比較廣泛。把控制權從具 ...
控制反轉(Inversion of Control,英文縮寫為IoC)是一個重要的面向對象編程的法則來削減電腦程式的耦合問題,也是輕量級的Spring框架的核心。 控制反轉一般分為兩種類型,依賴註入(Dependency Injection,簡稱DI)和依賴查找。依賴註入應用比較廣泛。把控制權從具體的對象手中交給平臺或者是框架。
2 BeanFactory是基本的功能介面
public interface BeanFactory { //這裡是對FactoryBean的轉義定義,因為如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象, //如果需要得到工廠本身,需要轉義 String FACTORY_BEAN_PREFIX = "&"; //這裡根據bean的名字,在IOC容器中得到bean實例,這個IOC容器就是一個大的抽象工廠。 Object getBean(String name) throws BeansException; //這裡根據bean的名字和Class類型來得到bean實例,和上面的方法不同在於它會拋出異常:如果根據名字取得的bean實例的Class類型和需要的不同的話。 Object getBean(String name, Class requiredType) throws BeansException; //這裡提供對bean的檢索,看看是否在IOC容器有這個名字的bean boolean containsBean(String name); //這裡根據bean名字得到bean實例,並同時判斷這個bean是不是單件 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; //這裡對得到bean實例的Class類型 Class getType(String name) throws NoSuchBeanDefinitionException; //這裡得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來 String[] getAliases(String name); }
BeanFactory只是對IOC容器中的基本行為作了定義,但是並沒有管理如何載入baen.XmlBeanFactory和ApplicationContext都實現了BeanFactory
spring提供了一個BeanFactory的基本實現----xmlBaenFactory.AbstractBeanFactory,DefaultListableBeanFactory這些抽象類為其提供模板服務.Resource介面來抽象Bean的數據
對XML定義文件的解釋委托給XmlBeanDefinitionReader來完成。
XmlBeanFacotry的使用過程。
ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);
- 創建IOC配置文件的抽象資源
- 創建一個BeanFactory
- 把XmlBeanDefinitionReader配置給BeanFactory
- XmlBeanDefinitionReader完成資源的解釋,完成對IOC容器的載入
XmlBeanFactory的源代碼
public class XmlBeanFactory extends DefaultListableBeanFactory { //這裡為容器定義了一個預設使用的bean定義讀取器 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } //在初始化函數中使用讀取器來對資源進行讀取,得到bean定義信息。 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }
XmlBeanFactoy和ApplicationContext的區別,XmlBeanFactory不具備定義資源的能力,
ApplicationContext
ApplicationContext = new FileSystemXmlApplicationContext(xmlPath);
繼承Messagesource,可以支持不同的信息源
訪問資源,ResourceLoader和Resource,可以從不同的地方來獲取Bean的定義資源
支持應用事件,繼承了ApplicationEvnetPublisher介面
ApplicationContext允許上下文嵌套 - 通過保持父上下文可以維持一個上下文體系 - 這個體系我們在以後對Web容器中的上下文環境的分析中可以清楚地看到。對於bean的查找可以在這個上下文體系中發生,首先檢查當前上下文,其次是父上下文,逐級向上,這樣為不同的Spring應用提供了一個共用的bean定義環境。這個我們在分析Web容器中的上下文環境時也能看到。
ApplicationContext提供IoC容器的主要介面,在其體系中有許多抽象子類比如AbstractApplicationContext為具體的BeanFactory的實現,比如FileSystemXmlApplicationContext和 ClassPathXmlApplicationContext提供上下文的模板,使得他們只需要關心具體的資源定位問題。當應用程式代碼實例化 FileSystemXmlApplicationContext的時候,得到IoC容器的一種具體表現 - ApplicationContext,從而應用程式通過ApplicationContext來管理對bean的操作。
BeanFactory 是一個介面,在實際應用中我們一般使用ApplicationContext來使用IOC容器,它們也是IOC容器展現給應用開發者的使用介面。對應用程式開發者來說,可以認為BeanFactory和ApplicationFactory在不同的使用層面上代表了SPRING提供的IOC容器服務。
FileSystemXmlApplicationContext簡歷IOC容器
ApplicationContext = new FileSystemXmlApplicationContext(xmlPath); public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); this.configLocations = configLocations; if (refresh) { //這裡是IoC容器的初始化過程,其初始化過程的大致步驟由AbstractApplicationContext來定義 refresh(); } }
refresh的模板在AbstractApplicationContext:refresh的模板在AbstractApplicationContext:refresh的模板在AbstractApplicationContext:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { synchronized (this.activeMonitor) { this.active = true; } // 這裡需要子類來協助完成資源位置定義,bean載入和向IOC容器註冊的過程 refreshBeanFactory(); ............ }
這個方法包含了整個BeanFactory初始化的過程,對於特定的FileSystemXmlBeanFactory,我們看到定位資源位置由refreshBeanFactory()來實現:
在AbstractXmlApplicationContext中定義了對資源的讀取過程,預設由XmlBeanDefinitionReader來讀取:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { // 這裡使用XMLBeanDefinitionReader來載入bean定義信息的XML文件 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //這裡配置reader的環境,其中ResourceLoader是我們用來定位bean定義信息資源位置的 ///因為上下文本身實現了ResourceLoader介面,所以可以直接把上下文作為ResourceLoader傳遞給XmlBeanDefinitionReader beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); //這裡轉到定義好的XmlBeanDefinitionReader中對載入bean信息進行處理 loadBeanDefinitions(beanDefinitionReader);
轉到beanDefinitionReader中進行處理:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { //調用XmlBeanDefinitionReader來載入bean定義信息。 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
轉到beanDefinitionReader中進行處理:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { //調用XmlBeanDefinitionReader來載入bean定義信息。 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
而在作為其抽象父類的AbstractBeanDefinitionReader中來定義載入過程:
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { //這裡得到當前定義的ResourceLoader,預設的我們使用DefaultResourceLoader ResourceLoader resourceLoader = getResourceLoader(); .........//如果沒有找到我們需要的ResourceLoader,直接拋出異常 if (resourceLoader instanceof ResourcePatternResolver) { // 這裡處理我們在定義位置時使用的各種pattern,需要ResourcePatternResolver來完成 try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); return loadCount; } ........ } else { // 這裡通過ResourceLoader來完成位置定位 Resource resource = resourceLoader.getResource(location); // 這裡已經把一個位置定義轉化為Resource介面,可以供XmlBeanDefinitionReader來使用了 int loadCount = loadBeanDefinitions(resource); return loadCount; } }
當我們通過ResourceLoader來載入資源,別忘了了我們的GenericApplicationContext也實現了ResourceLoader介面:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { public Resource getResource(String location) { //這裡調用當前的loader也就是DefaultResourceLoader來完成載入 if (this.resourceLoader != null) { return this.resourceLoader.getResource(location); } return super.getResource(location); } ....... }
而我們的FileSystemXmlApplicationContext就是一個DefaultResourceLoader - GenericApplicationContext()通過DefaultResourceLoader:
public Resource getResource(String location) { //如果是類路徑的方式,那需要使用ClassPathResource來得到bean文件的資源對象 if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // 如果是URL方式,使用UrlResource作為bean文件的資源對象 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // 如果都不是,那我們只能委托給子類由子類來決定使用什麼樣的資源對象了 return getResourceByPath(location); } } }
我們的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的實現類,他實現了以下的介面
protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } //這裡使用文件系統資源對象來定義bean文件 return new FileSystemResource(path); }
這樣代碼就回到了FileSystemXmlApplicationContext中來,他提供了FileSystemResource來完成從文件系統得到配置文件的資源定義。這樣,就可以從文件系統路徑上對IOC配置文件進行載入 - 當然我們可以按照這個邏輯從任何地方載入,在Spring中我們看到它提供的各種資源抽象,比如ClassPathResource, URLResource,FileSystemResource等來供我們使用。上面我們看到的是定位Resource的一個過程,而這隻是載入過程的一部分 - 我們回到AbstractBeanDefinitionReaderz中的loadDefinitions(resource)來看看得到代表bean文件的資源定義以後的載入過程,預設的我們使用XmlBeanDefinitionReader:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ....... try { //這裡通過Resource得到InputStream的IO流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //從InputStream中得到XML的解析源 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //這裡是具體的解析和註冊過程 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { //關閉從Resource中得到的IO流 inputStream.close(); } } ......... } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); //通過解析得到DOM,然後完成bean在IOC容器中的註冊 Document doc = this.documentLoader.loadDocument( inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware); return registerBeanDefinitions(doc, resource); } ....... }
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }