Spring之IOC容器
在前面博客中介紹什麽是依賴註入時有提到:依賴註入是組件之間依賴關系由容器在運行期決定,即由容器動態的將某個依賴關系註入到組件之中。那什麽是容器?既然Spring框架實現了IOC,那Spring中的容器是什麽呢?
一、容器介紹
在日常生活中容器是指用以容納物料並以殼體為主的基本裝置,它是用來盛放東西的。在編程中容器是用來存儲和組織其他對象的對象,首先要確定容器也是對象,也可以當做bean,只是這個對象是用來存儲和組織其他對象,那其他對象是什麽呢?其他對象其實就是bean對象,這也是面向對象編程的一種體現,萬物皆對象。在Spring提供了BeanFactory、ApplicationContext兩個IOC容器,來管理眾多的bean對象。
二、BeanFactory
一提到工廠我們生活當中可能會想到富某康,工廠是一類用以生產貨物的大型工業建築物。而BeanFactory不是用來生產貨物的而是用來生產管理bean的。BeanFactory會在bean的生命周期的各個階段中對bean進行各種管理,並且Spring將這些階段通過各種接口暴露給我們,讓我們可以對bean進行各種處理,我們只要讓bean實現對應的接口,那麽Spring就會在bean的生命周期調用我們實現的接口來處理該bean。那它是怎麽實現的呢?它主要分兩個階段。
1)、Bean容器的啟動
工廠要生產貨物那首先得把工廠運轉起來之後才能生產貨物。同樣bean容器要管理bean也需要先把容器啟動起來,獲取到bean的定義信息之後才能管理。
1. 讀取bean的xml配置文件,然後將xml中每個bean元素分別轉換成BeanDefinition對象。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable { private volatile Object beanClass; private String scope = SCOPE_DEFAULT; private boolean abstractFlag = false; private boolean lazyInit = false; private int autowireMode = AUTOWIRE_NO;private int dependencyCheck = DEPENDENCY_CHECK_NONE; private String[] dependsOn; private ConstructorArgumentValues constructorArgumentValues; private MutablePropertyValues propertyValues; private String factoryBeanName; private String factoryMethodName; private String initMethodName; private String destroyMethodName;
BeanClass保存bean的class屬性,scop保存Bean的作用域,abstractFlag保存該bean是否抽象,lazyInit保存是否延遲初始化,autowireMode保存是否自動裝配,dependencyCheck保存是否堅持依賴,dependsOn保存該bean依賴於哪些bean(這些bean必須提取初始化),constructorArgumentValues保存通過構造函數註入的依賴,propertyValues保存通過setter方法註入的依賴,factoryBeanName和factoryMethodName用於factorybean,也就是工廠類型的bean,initMethodName和destroyMethodName分別對應bean的init-method和destory-method屬性。後面會對這些內容進行詳細介紹。
2. 通過BeanDefinitionRegistry將bean註冊到beanFactory中
上面獲取到bean的信息之後,是怎麽註冊到BeanFactory中的呢?其實是通過BeanDefinitionRegistry將bean註冊到beanFactory中。因為BeanFactory的實現類,需要實現BeanDefinitionRegistry 接口。
public interface BeanDefinitionRegistry extends AliasRegistry { void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException; void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; boolean containsBeanDefinition(String beanName); String[] getBeanDefinitionNames(); int getBeanDefinitionCount(); boolean isBeanNameInUse(String beanName); }
BeanDefinitionRegistry接口提供了根據beanName註冊對應beanDefinition的方法,而在DefaultListableBeanFactory中實現了該方法,並將beanDefinition保存在了ConcurrentHashMap中。
@SuppressWarnings("serial") public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { /** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // ... ... this.beanDefinitionMap.put(beanName, beanDefinition); }
另外Spring還對外暴露了一些接口用來對bean初始化,例如BeanFactoryPostProcessor。
public interface BeanFactoryPostProcessor { /** * Modify the application context‘s internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for overriding or adding * properties even to eager-initializing beans. * @param beanFactory the bean factory used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
我們可以翻譯一下postProcessBeanFactory的註釋信息,postProcessBeanFactory可以修改應用上下文中已經進行standard初始化的beanFactory,此時所有bean的定義信息已經加載完成,但還未實例化,允許覆蓋、新增甚至重新初始化bean信息,一個典型的例子就是屬性覆蓋器PropertyOverrideConfigurer。對於一些參數可以配置在properties中,而不用配置在Spring的XML配置文件中。
2)、容器Bean的實例化
上面把bean容器啟動之後,工廠算是運轉起來了,配方(beanDefinition)也已經準備充分,然後就是生產(實例化)、管理貨物(bean)了。實例化bean主要通過反射和CGLIB兩種方式,在bean的實例化過程中,Spring也暴露了一些接口。
BeanNameAware 獲取該bean在配置文件中對應的id
BeanFactoryAware 獲取實例化該bean的BeanFactory
InitializingBean bean實例化、所有屬性設置後調用初始化方法
DisposableBean 在bean丟棄的時候調用銷毀方法
我們可以通過示例演示一下這幾個接口的使用。
1. 首先創建了Maven project,pom.xml引入spring-core、spring-context。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId> <artifactId>BeanFactoryDemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>BeanFactoryDemo</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>5.0.0.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
2. 創建bean對象,實現上面列出的接口
package com.demo.model; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; public class UserBean implements BeanNameAware,BeanFactoryAware,InitializingBean,DisposableBean { public void setBeanName(String name) { System.out.println(name); } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println(beanFactory); } public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean"); } public void destroy() throws Exception { System.out.println("DisposableBean"); } }
3. 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="user" class="com.demo.model.UserBean"></bean> </beans>
4. 測試
使用ApplicationContext獲取BeanFactory,再通過getBean方法獲取到對應的bean,最後調用destroy方法進行銷毀,從輸出結果可以看到依次調用了BeanNameAware,BeanFactoryAware,InitializingBean,DisposableBean接口。
public static void main( String[] args ) throws Exception { ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); BeanFactory factory=context; UserBean user=(UserBean)factory.getBean("user"); user.destroy(); }
輸出結果:user
org.springframework.beans.factory.support.DefaultListableBeanFactory@6bf256fa: defining beans [user]; root of factory hierarchy
InitializingBean
DisposableBean
三、ApplicationContext
在上面的示例中使用了ApplicationContext獲取bean的配置,然後直接將ApplicationContext接口 對象賦值給了BeanFactory接口對象,為什麽可以賦值呢?其實ApplicationContext接口實現了BeanFactory接口。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver
從上面ApplicationContext接口的繼承關系可以看到,它還通過繼承其他接口擴展了BeanFactory的功能。MessageSource:為應用提供國際化訪問功能。ResourceLoader:提供資源(如URL和文件系統)的訪問支持。ApplicationEventPublisher:引入事件機制,包括啟動事件、關閉事件等,讓容器在上下文中提供了對應用事件的支持。它代表的是一個應用的上下文環境。beanFactory主要是面對與 spring 框架的基礎設施,面對 spring 自己。而 Applicationcontex 主要面對與 spring 使用的開發者。基本都會使用 Applicationcontex 並非 beanFactory 。所以在上面實例使用的ApplicationContext獲取BeanFactory接口對象。
Spring之IOC容器