1. 程式人生 > >【Spring原始碼解析】FactoryBean-工廠方法模式的實現及使用

【Spring原始碼解析】FactoryBean-工廠方法模式的實現及使用

一、工廠方法模式中的三種模式的特點

工廠模式中的三種模式,分別是:簡單工廠模式、工廠方法模式、抽象工廠模式,三種分別是什麼,以及適合場景是什麼?

(1)簡單工廠模式:一個抽象介面對應一個產品介面,特定產品實現這個介面,針對不同產品都可以在同一個工廠中生產,同一個工廠生產產品可以通過多種方式,單生產方法(通過型別判定具體是要哪個產品,並進行new返回),多生產方法(工廠中有多個產品的不同生產方法,每一個生產方法對應一個new 產品的return),或者是靜態方法簡單工廠(將工廠中的返回某個產品的方法類設定為static,則直接通過類呼叫靜態方法即可,不需要new工廠類的物件例項)

該方法的問題是:如果要新增一個產品,必須對工廠類進行修改,但是如果通過反射或者其他方式可以做統一處理,其實也不會涉及到這個問題,如果要新增一個產品必須要對工廠類修改的時候,此時針對簡單工廠模式的修改覺得比較麻煩的話或者想要使擴充套件性更好的話,可以通過工廠方法模式(備註:簡單工廠模式比較像一個集中廠,各種不同的東西都能產)

Client端去呼叫的時候,需要知道:工廠的類名,及提供的生產方法從而可以獲得具體產品的例項,以及產品的介面,可以通過例項呼叫介面得到需要的產品的資訊

其實就是:一個工廠類名Fatory,從工廠的生產方法獲取具體產品例項,getProduct()其中會返回new ProductA()或者new ProductB(),之後想要真正去進行產品的使用,還需要通過產品的介面,進行呼叫,例如ProductA.getProductInfo()等

(2)工廠方法模式:可以看做是簡單工廠模式的升級版;工廠方法模式就是一個工廠介面和多個工廠實現類,要增加一個新的產品,增加一個新的工廠實現類即可,針對之前的老的工廠實現類也不需要修改

工廠方法模式相當於在簡單工廠模式的基礎上,增加了對於不同的產品進行多個不同工廠的實現類的新增,不同的工廠用於Get不同的產品,用於進行不同產品的具體生產

Client進行呼叫的時候,直接通過識別不同工廠,然後通過工廠介面類提供的公共方法,即可進行介面方法呼叫,獲取產品;還需要知道具體的產品介面,用於進行具體的產品資訊的獲取

(3)抽象工廠模式:針對有多個介面的情況,應用於有多個產品族產生的情況

抽象工廠模式中可以定義不止一個介面,一個工廠也可以生產不止一個產品類,但是即使是這樣,如果要新增一個新的介面,在其中需要修改的部分也是很多的,抽象工廠類的介面需要增加,其他工廠對於該型別的產品的工廠實現要增加,同時可以通過新建其他產品類進行具體產品類的返回;但是如果針對某一個介面對應的產品門類的話,是可以很方便的新增的,而且對老的工廠介面和具體的工廠類,都沒有影響,也就是說我想要建一個新的工廠,進行產品的生產,很容易。

二、Spring中的工廠方式模式的使用FactoryBean

FactoryBean是一個介面,具體包含的介面如下所以:

這三個介面具體含義是:

T getObject() throws Exception;    //返回此工廠管理的物件的例項
Class<?> getObjectType();    //返回此FactoryBean建立的物件的型別
//這個工廠返回的物件是否是單例,預設返回true;若是單例則由getObject返回的//是相同的物件,是可以快取的引用
default boolean isSingleton() {     
   return true;
}

基於此,我們知道這個FactoryBean也用於生成物件例項,那麼其實也是一種Bean,該型別的Bean可以作為對通過xml或者普通的註解直接進行生成Bean的一種補充,簡單的說就是:可以通過FactoryBean來返回你想要的Bean,該類的Bean的生成可能是會比較複雜,或者你想要在做一些擴充套件操作的,都可以使用該方式

後面,可以重點說明一下ProxyFactoryBean,這個部分會在代理模式的時候說明~

下面通過對FactoryBean的實現,看一下自己使用的效果,FactoryBean的程式碼如下:

package factorybean.demo;

/**

 * Created by xiami on 2019/6/3.

 */

public interface FactoryBean <T> {

    T getObject();

    Class<?> getObjectType();

    default boolean isSingleton(){return true;}

}

下面是User類對FactoryBean的實現:

package factorybean.demo;

/**

 * Created by xiami on 2019/6/3.

 */

public class User implements FactoryBean {

    private String name;

    private String passWd;

    private int age;


    public void setAge(int age) {

        this.age = age;

    }

    public void setPassWd(String passWd) {

        this.passWd = passWd;

    }

    public void setName(String name) {

        this.name = name;

    }

    @Override

    public Object getObject() {

        if (age <= 0){

            return new Exception("年齡輸入錯誤");

        }

        else {

            return "輸入引數正確";

        }

    }

    @Override

    public Class<?> getObjectType() {

        return User.class;

    }

    public String getName() {

        return name;

    }

    public String getPassWd() {

        return passWd;
    }

    public int getAge() {

        return age;

    }

}

重點關注:在getObject()中針對輸入的不同age引數做了一個判斷,用於輸出引數是否異常

接下來是:

在springtest.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"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd

       http://www.springframework.org/schema/context

       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="factorybeanUser" class="factorybean.demo.User">

        <property name="name" value="lxlx" />

        <property name="passWd" value="111" />

        <property name="age" value="1"/>

    </bean>

</beans>

注意,因為在簡單工廠模式中實現的GetBean還無法解析FactoryBean的型別,因此這裡直接用的Spring-Framework作為lib庫方便對Spring中的xml解析和GetBean的呼叫,Client呼叫類程式碼如下:

package factorybean.demo;

import org.springframework.beans.BeansException;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**

 * Created by 58 on 2019/6/3.

 */
public class Client {
    public static void main(String[] args) throws BeansException {

        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("classpath:factorybean/demo/springtest.xml");
        User user = (User)classPathXmlApplicationContext.getBean("factorybeanUser");
        System.out.println(user.getObject());
    }
}

當springtest.xml中配置age的值為-1時,執行結果為:

當springtest.xml中配置age的值為12時,執行結果為

可見:通過對FactoryBean的介面的實現,在getObject()中能夠返回自己想要的內容,通過GetBean()就可以獲取到,之後的文章會對FactoryBean在GetBean中是什麼時候被獲取做介紹

參考文章:

https://blog.csdn.net/zknxx/article/details/79572387

https://blog.csdn.net/zknxx/article/details/79588391

https://www.jianshu.com/p/6f0a5962