1. 程式人生 > >BeanFactory和它的物件註冊和依賴繫結的三種方式

BeanFactory和它的物件註冊和依賴繫結的三種方式

一、Spring中的BeanFactory是什麼?

  BeanFactory是個基本的IoC容器,提供完整的IoC服務支援,負責物件建立的管理依賴注入服務

  如果沒有特殊指定,預設採用延遲初始化策略(lazy-load)。只有當客戶端物件需要訪問容器中的某個受管物件的時候,才對該受管物件進行初始化以及依賴注入操作

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

二、BeanFactory的物件註冊和依賴繫結的三種方式

1、直接編碼方式(無需xml檔案)

 1.1 專案結構

1.2 各個程式碼

public class App {
    private String says = "App";

    private App(){
        System.out.println("呼叫App無參構造");
    }

    public App(String says) {
        System.out.println("呼叫App有參構造, 引數says為:" + says);
        this.says = says;
    }

    public void say(){
        System.out.println("App.say():" + says);
    }

    public String getSays() {
        return says;
    }

    public void setSays(String says) {
        this.says = says;
    }

    @Override
    public String toString() {
        return "App{" +
                "says='" + says + '\'' +
                '}';
    }
}
public class Coder {
    private App app;

    private Coder(){
        System.out.println("呼叫Coder無參構造");
    }

    public Coder(App app) {
        System.out.println("呼叫Coder有參構造,引數app為:" + app.toString());
        this.app = app;
    }

    public App getApp() {
        return app;
    }

    public void setApp(App app) {
        System.out.println("呼叫Coder.setApp(), 引數app為: " + app.toString());
        this.app = app;
    }
}
public class BeanFactoryTest extends TestCase {
    /**
     * 直接編碼方式
     */
    public void testRedirectCode() {
        DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
        BeanFactory beanFactory = bindViaCode(beanRegistry);
        Coder coder = (Coder) beanFactory.getBean("coder");
        coder.getApp().say();

        /**
         * 輸出為:
         * //通過構造方法注入的情況
         * 呼叫App無參構造
         * 呼叫Coder有參構造,引數app為:App{says='App'}
         * App.say():App
         *
         * //通過setter方式注入的情況下
         * 呼叫Coder無參構造
         * 呼叫App無參構造
         * 呼叫Coder.setApp(), 引數app為: App{says='App'}
         * App.say():App
         */
    }

    public static BeanFactory bindViaCode(BeanDefinitionRegistry registry){
        AbstractBeanDefinition appBeanDefinition = new RootBeanDefinition(App.class);
        AbstractBeanDefinition coderBeanDefinition = new RootBeanDefinition(Coder.class);

        //將beanDefinition註冊到容器中
        registry.registerBeanDefinition("app", appBeanDefinition);
        registry.registerBeanDefinition("coder", coderBeanDefinition);

        //指定依賴關係
        //1、可以通過構造方法注入
//        ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
//        argumentValues.addIndexedArgumentValue(0, appBeanDefinition);
//        coderBeanDefinition.setConstructorArgumentValues(argumentValues);

        //2、可以通過setter方法注入
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        mutablePropertyValues.addPropertyValue("app", appBeanDefinition);
        coderBeanDefinition.setPropertyValues(mutablePropertyValues);

        //繫結完成
        return (BeanFactory)registry;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<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>
    <!--這是 pom.xml檔案 -->
    <groupId>com.me.sourcecode</groupId>
    <artifactId>spring-explore</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.1.3.RELEASE</spring.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>

</project>

1.3 原理 (摘自《spring揭祕》)

   打個比方說,BeanDefinitionRegistry就像圖書館的書架,所有的書是放在書架上的。雖然你
還書或者借書都是跟圖書館(也就是BeanFactory,或許BookFactory可能更好些)打交道,但書架才
是圖書館存放各類圖書的地方。所以,書架相對於圖書館來說,就是它的“BookDefinitionRegistry”。


   每一個受管的物件,在容器中都會有一個BeanDefinition的例項(instance)與之相對應,該
BeanDefinition的例項負責儲存物件的所有必要資訊,包括其對應的物件的class型別、是否是抽象
類、構造方法引數以及其他屬性等。當客戶端向BeanFactory請求相應物件的時候,BeanFactory會
通過這些資訊為客戶端返回一個完備可用的物件例項。RootBeanDefinition和ChildBean-
Definition是BeanDefinition的兩個主要實現類。

2、xml配置檔案方式

2.1 專案結構(上面的基礎上)

2.2 新增檔案SpringBean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
        http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

    <bean id="app" class="com.me.sourcecode.bean.App"></bean>

    <!--<bean id="coder" class="com.me.sourcecode.bean.Coder">-->
        <!--<constructor-arg index="0">-->
            <!--<ref bean="app" />-->
        <!--</constructor-arg>-->
    <!--</bean>-->

    <bean id="coder" class="com.me.sourcecode.bean.Coder">
        <property name="app" ref="app"></property>
    </bean>
</beans>

BeanFactoryTest類裡,增加對xml檔案配置方式的測試

/**
     * xml配置檔案方式
     */
    public void testXmlFile(){
        DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
        BeanFactory beanFactory = bindViaXmlFile(beanRegistry);
        Coder coder = (Coder) beanFactory.getBean("coder");
        coder.getApp().say();
    }

public static BeanFactory bindViaXmlFile(BeanDefinitionRegistry registry) {
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(registry);
        xmlBeanDefinitionReader.loadBeanDefinitions("classpath*:SpringBean.xml");
        return (BeanFactory) registry;
    }

2.3 原理(摘自《spring揭祕》)

  XmlBeanDefinitionReader負責讀取Spring指定格式的XML配置檔案並解析,之後將解析後的檔案內
容對映到相應的BeanDefinition,並載入到相應的BeanDefinitionRegistry中(在這裡是DefaultListableBeanFactory)

3、基於註解的方式

3.1 專案結構(不變)

3.2  有修改的內容

@Component
public class App {
    private String says = "App";

    private App(){
        System.out.println("呼叫App無參構造");
    }

    public App(String says) {
        System.out.println("呼叫App有參構造, 引數says為:" + says);
        this.says = says;
    }

    public void say(){
        System.out.println("App.say():" + says);
    }

    public String getSays() {
        return says;
    }

    public void setSays(String says) {
        this.says = says;
    }

    @Override
    public String toString() {
        return "App{" +
                "says='" + says + '\'' +
                '}';
    }
}
@Component
public class Coder {
    @Autowired
    private App app;

    private Coder(){
        System.out.println("呼叫Coder無參構造");
    }

    public Coder(App app) {
        System.out.println("呼叫Coder有參構造,引數app為:" + app.toString());
        this.app = app;
    }

    public App getApp() {
        return app;
    }

    public void setApp(App app) {
        System.out.println("呼叫Coder.setApp(), 引數app為: " + app.toString());
        this.app = app;
    }
}

SpringBean.xml檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
        http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

    <context:component-scan base-package="com.me.sourcecode.bean" />
</beans>

BeanFactoryTest增加基於註解方式的測試

/**
     * 基於註解的方式
     */
    public void testAnnoation(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("SpringBean.xml");
        Coder coder = (Coder) applicationContext.getBean("coder");
        coder.getApp().say();

        /**
         * 輸出為:
         * 呼叫App無參構造
         * 呼叫Coder無參構造
         *
         * App.say():App
         */
    }

3.3 原理  

  可以看到輸出結果中,沒有呼叫Coder.setApp()這一句,而又有App.say(): App這一句。它是Coder裡面的app是怎麼注入進入的呢?

 1) 通過SpringBean.xml檔案的<context:component-scan base-package="com.me.sourcecode.bean" />,開始掃描這個路徑,處理所有@Component註解標記的類(即App,Coder),生成對應的BeanDefinition

2)根據BeanDefinition建立Bean,此時Coder裡面的app還是null,也就說還沒有進行依賴注入

3)建立Bean之後,有個例項化Bean的流程。在這個流程中,AutowiredAnnotationBeanPostProcessor起了作用,通過反射的方式將App注入到Coder物件裡面了。

反射 field.set(bean, value) ,此時bean是Coder的例項,value是App的例項