緒論
shiro是一個簡單易用,功能強大的Java安全框架,學習其源碼設計思想對我們的編碼水平的提高大有裨益。現在,就從源碼角度帶大家學習一下shiro里面的工廠方法模式。 這里的前提是讀者有過使用shiro的基礎,沒有也行,關鍵理解思想就可以了。
從一個簡單例子說起
首先,我們先從一個簡單的例子說起。我們在使用ini文件來作為用戶角色權限配置的時候,我們獲取SecurityManager的方法是:
?Factory<SecurityManager>?factory?=?new?IniSecurityManagerFactory("classpath:demo01_getstarted.ini");
?
?SecurityManager?securityManager?=?factory.getInstance();
復制代碼
上面兩行代碼看似簡單,其實框架底層就使用了工廠方法模式。
關于上面的例子就不多講了。這里是側重分析工廠方法設計模式。
工廠方法模式
在工廠方法模式中,我們對模式角色劃分為四種:
1、抽象產品(產品接口):比如上面shiro實例中的SecurityManager
2、具體產品:比如實現了SecurityManager的類
3、抽象工廠(工廠接口):比如上面shiro實例中的Factory、AbstractFactory
4、具體工廠:比如上面shiro實例中的IniSecurityManagerFactory
我們先來看看shiro里面工廠方法模式實現的源碼:
一、頂級工廠抽象接口Factory,所有抽象工廠類都繼承此接口
public?interface?Factory<T>?{
????T?getInstance();
}
復制代碼
二、抽象工廠類,這個抽象工廠類負責獲取工廠實例,具體創建過程由其子類來實現
public?abstract?class?AbstractFactory<T>?implements?Factory<T>?{
????private?boolean?singleton;
????private?T?singletonInstance;
????public?AbstractFactory()?{
????????this.singleton?=?true;
????}
????public?boolean?isSingleton()?{
????????return?singleton;
????}
????public?void?setSingleton(boolean?singleton)?{
????????this.singleton?=?singleton;
????}
???//?獲取工廠實例,可以以單例形式獲取,也可以每一次獲取都創建一個實例
????public?T?getInstance()?{
????????T?instance;
????????if?(isSingleton())?{
????????????if?(this.singletonInstance?==?null)?{
????????????????this.singletonInstance?=?createInstance();
????????????}
????????????instance?=?this.singletonInstance;
????????}?else?{
????????????instance?=?createInstance();
????????}
????????if?(instance?==?null)?{
????????????String?msg?=?"Factory?'createInstance'?implementation?returned?a?null?object.";
????????????throw?new?IllegalStateException(msg);
????????}
????????return?instance;
????}
????protected?abstract?T?createInstance();
}
復制代碼
上面兩個工廠類抽象了最基本的工廠接口--創建工廠、獲取工廠。如果我們需要對工廠類進行擴展的話,只需要繼承AbstractFactory來實現即可,非常方便。現在看一下AbstractFactory的一個子類。
IniFactorySupport是一個特定的抽象工廠類,是根據ini文件來創建工廠實例的工廠抽象類。我們不需要細究IniFactorySupport代碼干了什么。只需要明白,它是對根據ini文件創建工廠做了一些邏輯處理就好了。
我們可以看到,繼承AbstractFactory,我們可以隨便擴展定制我們工廠類的行為。
public?abstract?class?IniFactorySupport<T>?extends?AbstractFactory<T>?{
????public?static?final?String?DEFAULT_INI_RESOURCE_PATH?=?"classpath:shiro.ini";
????private?static?transient?final?Logger?log?=?LoggerFactory.getLogger(IniFactorySupport.class);
????private?Ini?ini;
????private?Map<String,??>?defaultBeans;
????protected?IniFactorySupport()?{
????}
????protected?IniFactorySupport(Ini?ini)?{
????????this.ini?=?ini;
????}
????public?Ini?getIni()?{
????????return?ini;
????}
????public?void?setIni(Ini?ini)?{
????????this.ini?=?ini;
????}
????protected?Map<String,??>?getDefaults()?{
????????return?defaultBeans;
????}
????public?void?setDefaults(Map<String,??>?defaultBeans)?{
????????this.defaultBeans?=?defaultBeans;
????}
????public?static?Ini?loadDefaultClassPathIni()?{
????????Ini?ini?=?null;
????????if?(ResourceUtils.resourceExists(DEFAULT_INI_RESOURCE_PATH))?{
????????????log.debug("Found?shiro.ini?at?the?root?of?the?classpath.");
????????????ini?=?new?Ini();
????????????ini.loadFromPath(DEFAULT_INI_RESOURCE_PATH);
????????????if?(CollectionUtils.isEmpty(ini))?{
????????????????log.warn("shiro.ini?found?at?the?root?of?the?classpath,?but?it?did?not?contain?any?data.");
????????????}
????????}
????????return?ini;
????}
????
????protected?Ini?resolveIni()?{
????????Ini?ini?=?getIni();
????????if?(CollectionUtils.isEmpty(ini))?{
????????????log.debug("Null?or?empty?Ini?instance.??Falling?back?to?the?default?{}?file.",?DEFAULT_INI_RESOURCE_PATH);
????????????ini?=?loadDefaultClassPathIni();
????????}
????????return?ini;
????}
???
????public?T?createInstance()?{
????????Ini?ini?=?resolveIni();
????????T?instance;
????????if?(CollectionUtils.isEmpty(ini))?{
????????????log.debug("No?populated?Ini?available.??Creating?a?default?instance.");
????????????instance?=?createDefaultInstance();
????????????if?(instance?==?null)?{
????????????????String?msg?=?getClass().getName()?+?"?implementation?did?not?return?a?default?instance?in?"?+
????????????????????????"the?event?of?a?null/empty?Ini?configuration.??This?is?required?to?support?the?"?+
????????????????????????"Factory?interface.??Please?check?your?implementation.";
????????????????throw?new?IllegalStateException(msg);
????????????}
????????}?else?{
????????????log.debug("Creating?instance?from?Ini?["?+?ini?+?"]");
????????????instance?=?createInstance(ini);
????????????if?(instance?==?null)?{
????????????????String?msg?=?getClass().getName()?+?"?implementation?did?not?return?a?constructed?instance?from?"?+
????????????????????????"the?createInstance(Ini)?method?implementation.";
????????????????throw?new?IllegalStateException(msg);
????????????}
????????}
????????return?instance;
????}
????protected?abstract?T?createInstance(Ini?ini);
????protected?abstract?T?createDefaultInstance();
}
復制代碼
通過看類關系圖,IniSecurityManagerFactory繼承IniFactorySupport,在IniFactorySupport基礎上面進一步封裝創建工廠過程。
IniSecurityManagerFactory的源碼就不貼出來了,明白設計思想就可以了。
通過源碼分析,我們可以看到,首先抽象出最基本的工廠接口,具體的工廠類由其子類去實現。一個具體工廠類對應這一類產品。當需要新增產品類的時候,我們只需要新加工廠類,并且新增對應的產品類即可,不需要修改原有工廠類代碼,符合了設計模式中的開閉原則、單一職責原則。
demo實現
一、創建工廠接口:IFactory IFactory.java
/**
?*?泛型代表的是產品類型
?*
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?interface?IFactory<T>?{
????/**
?????*?獲取產品
?????*
?????*?@return?產品實例
?????*/
????T?getInstance();
}
復制代碼
進一步抽象工廠接口 AbstractFactory.java
/**
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?abstract?class?AbstractFactory<T>?implements?IFactory<T>?{
???/**
????????*?創建產品,具體創建過程由其子類實現
????????*
????????*?@return?創建的產品實例
????????*/
???????protected?abstract?T?createInstance();
???
???????@Override
???????public?T?getInstance()?{
???????????return?createInstance();
???????}
}
復制代碼
二、創建產品接口 IProduct.java
/**
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?interface?IProduct?{
}
復制代碼
進一步分類產品,創建一類產品接口 Car.java
/**
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?abstract?class?Car?implements?IProduct?{
????/**
?????*?創建汽車產品
?????*
?????*?@return?創建的汽車產品
?????*/
????protected?abstract?Car?createCar();
????/**
?????*?駕駛汽車
?????*/
????public?abstract?void?drive();
}
復制代碼
具體產品類 Taxi.java
/**
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?class?Taxi?extends?Car?{
????private?Taxi?taxi;
????@Override
????protected?Car?createCar()?{
????????this.taxi?=?new?Taxi();
????????return?this.taxi;
????}
????@Override
????public?void?drive()?{
????????System.out.println("我是接送客的車");
????}
}
復制代碼
三、創建具體產品的工廠類 TaxtFactory.java
/**
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?class?TaxiFactory?extends?AbstractFactory<Car>?{
????@Override
????protected?Car?createInstance()?{
????????return?new?Taxi();
????}
}
復制代碼
四、客戶端代碼 Client.java
/**
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?class?Clent?{
????public?static?void?main(String[]?args)?{
????????IFactory<Car>?factory?=?new?TaxiFactory();
????????Car?taxi?=?factory.getInstance();
????????taxi.drive();
????}
}
復制代碼
通過例子,我們知道,在工廠方法模式中,有一個頂級的產品接口,對產品作出做基本的抽象,然后產品下面還有不同產品的分類,在同一類產品中又有不同的具體產品,比如car類產品下面又會有多種汽車產品。每一個具體的產品都有對應一個具體的工廠類。 如果想再新加一個新的產品,不論是car類產品,還是非car類產品,我們都可以通過新加工廠類和產品類來實現,比如新增一個船類產品
Ship.java
/**
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?abstract?class?Ship?implements?IProduct?{
????/**
?????*?造船
?????*
?????*?@return
?????*/
????protected?abstract?IProduct?createShip();
????public?abstract?void?doSomething();
}
復制代碼
創建漁船 FishShip.java
/**
?*?漁船
?*
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?class?FishShip?extends?Ship?{
????private?FishShip?ship;
????@Override
????public?IProduct?createShip()?{
????????this.ship?=?new?FishShip();
????????return?this.ship;
????}
????@Override
????public?void?doSomething()?{
????????System.out.println("我在打魚呀");
????}
}
復制代碼
創建漁船工廠類 FishShipFactory.java
/**
?*?@author?wunanliang
?*?@date?2018/1/8
?*?@since?1.0.0
?*/
public?class?FishShipFactory?extends?AbstractFactory<Ship>?{
????@Override
????protected?Ship?createInstance()?{
????????return?new?FishShip();
????}
復制代碼
添加一個產品,我們就得添加產品類和工廠類。對于系統的擴展來說,工廠方法模式有優勢,但是會增加系統的復雜度以及類的數量。
結束語
對于設計模式,大家重點是理解這樣設計的原理與優缺點,不要機械的背誦條條框框。實際我們在開發真實系統時,會糅合多種設計模式在一起。只有我們對設計模式有本質性的認識和掌握,才是真正掌握了設計模式。