1. 程式人生 > >Spring之IOC容器

Spring之IOC容器

config bst 翻譯 lazyinit 並且 上下文環境 兩個 而是 messages

在前面博客中介紹什麽是依賴註入時有提到:依賴註入是組件之間依賴關系由容器在運行期決定,即由容器動態的將某個依賴關系註入到組件之中。那什麽是容器?既然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容器