論 Spring 框架所運(yùn)用的設(shè)計(jì)模式
在當(dāng)今軟件開(kāi)發(fā)的廣闊領(lǐng)域中,Spring 框架無(wú)疑占據(jù)著重要的一席之地。它以其強(qiáng)大的功能和靈活性,成為眾多開(kāi)發(fā)者的得力助手。而在 Spring 框架的背后,蘊(yùn)含著一系列精妙絕倫的設(shè)計(jì)模式,這些設(shè)計(jì)模式猶如隱藏的智慧密碼,賦予了框架無(wú)盡的活力與魅力。
當(dāng)我們深入探究 Spring 框架時(shí),就仿佛開(kāi)啟了一扇通往軟件設(shè)計(jì)藝術(shù)殿堂的大門(mén)。其中的設(shè)計(jì)模式不僅是代碼組織和架構(gòu)的精巧構(gòu)建,更是對(duì)軟件開(kāi)發(fā)理念的深刻詮釋。它們巧妙地解決了各種復(fù)雜問(wèn)題,實(shí)現(xiàn)了代碼的高效復(fù)用、模塊的松散耦合以及系統(tǒng)的良好擴(kuò)展性。通過(guò)對(duì) Spring 框架涉及的設(shè)計(jì)模式的剖析,我們將領(lǐng)略到設(shè)計(jì)智慧的熠熠光輝,感受它們?nèi)绾卧谲浖澜缰醒堇[出一場(chǎng)場(chǎng)精彩絕倫的代碼之舞,為構(gòu)建高質(zhì)量、可維護(hù)的軟件系統(tǒng)奠定堅(jiān)實(shí)基礎(chǔ)。讓我們一同踏上這場(chǎng)探索 Spring 框架設(shè)計(jì)模式的奇妙之旅,去揭示其中的奧秘與精彩。
一、關(guān)于設(shè)計(jì)模式的一些只是鋪墊
1.軟件設(shè)計(jì)有哪些原則?
整體來(lái)說(shuō)有七大原則:
- 開(kāi)閉原則:對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。
- 里氏轉(zhuǎn)換:子類(lèi)繼承父類(lèi)時(shí),除添加新的方法完成新增功能外,盡量不要重寫(xiě)父類(lèi)的方法。
- 依賴(lài)倒置: 高層類(lèi)應(yīng)該依賴(lài)于底層類(lèi)的抽象而不是具體。
- 合成復(fù)用:盡量使用對(duì)象組合/聚合,而不是繼承關(guān)系達(dá)到軟件復(fù)用的目的。
- 單一職責(zé):不要多于一個(gè)導(dǎo)致類(lèi)變更的原因。
- 接口原則:用多個(gè)接口確定類(lèi)的行為,而不是一個(gè)總接口定義所有行為。
- 迪米特法則:最少知道原則,一個(gè)類(lèi)應(yīng)該盡可能減少對(duì)其他類(lèi)的了解,避免類(lèi)之間過(guò)度耦合。(其他類(lèi)應(yīng)該封裝一個(gè)方法提供的該類(lèi)使用)
2.設(shè)計(jì)模式分為哪幾類(lèi)?有哪些設(shè)計(jì)模式
分類(lèi):
- 創(chuàng)建型模式:創(chuàng)建型模式主要用于創(chuàng)建對(duì)象。
- 結(jié)構(gòu)型模式:主要用于處理類(lèi)或?qū)ο蟮慕M合。
- 行為型模式:主要用于描述對(duì)類(lèi)或?qū)ο笤鯓咏换ズ驮鯓臃峙渎氊?zé)。
創(chuàng)建型模式:
- 單例模式:確保類(lèi)有且只有一個(gè)對(duì)象被創(chuàng)建。
- 抽象工廠模式:允許客戶(hù)創(chuàng)建對(duì)象的宗族,而無(wú)需指定他們的具體類(lèi)。
- 建造者模式:將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,讓同樣的構(gòu)建過(guò)程而已創(chuàng)建不同的表示。
- 工廠方法模式:由類(lèi)決定要?jiǎng)?chuàng)建的具體類(lèi)是哪一個(gè)。
- 原型模式:用原型實(shí)例指定創(chuàng)建對(duì)象的種類(lèi),并且通過(guò)拷貝這個(gè)原型來(lái)創(chuàng)建新的對(duì)象。
結(jié)構(gòu)型模式:
- 適配器模式:封裝對(duì)象,并提供不同的接口。
- 橋接模式:將抽象部分和實(shí)現(xiàn)部分分離,讓他們獨(dú)立的變化。
- 裝飾模式:包裝一個(gè)對(duì)象,提供新的行為。
- 組合模式:客戶(hù)用一致的方式處理對(duì)象集合和單個(gè)對(duì)象。
- 外觀模式:簡(jiǎn)化一群類(lèi)的接口。
- 享元模式:運(yùn)用共享技術(shù)有效的支持大量細(xì)粒度的對(duì)象。
- 代理模式:包裝對(duì)象,以控制對(duì)此對(duì)象的訪問(wèn)。
行為型模式:
- 模板方法模式:定義一個(gè)操作算法的總體架構(gòu),將一些步驟的實(shí)現(xiàn)放在子類(lèi)中。
- 命令模式:封裝請(qǐng)求成為對(duì)象。
- 迭代器模式:在對(duì)象的集合之中游走,而不是暴露集合的實(shí)現(xiàn)。
- 觀察者模式:讓對(duì)象能夠在狀態(tài)改變時(shí)被通知。
- 中介者模式:用一個(gè)中介對(duì)象來(lái)封裝一系列的對(duì)象交互。中介者使各個(gè)對(duì)象不需要顯示的相互引用,從而使其耦合松散,而且可以獨(dú)立地改變他們之間的交互。
- 備忘錄模式:在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài)。 以后就可以將該對(duì)象恢復(fù)到保存狀態(tài)。
- 解釋器模式:介紹給定一個(gè)語(yǔ)言,定義它的文法的一種表示,并定義一個(gè)解釋器, 該解釋器使用該表示來(lái)解釋語(yǔ)言中的句子。
- 狀態(tài)模式:封裝了基于狀態(tài)的行為,并使用委托在行為之間的切換。
- 策略模式:封裝可以互換的行為,并使用委托來(lái)決定要使用哪一個(gè)。
- 責(zé)任鏈模式:為了解除請(qǐng)求的發(fā)送者和接收者之間的耦合,使多個(gè)對(duì)象都有機(jī)會(huì)處理這個(gè)請(qǐng)求。將這些處理對(duì)象連成一個(gè)鏈,并沿著這個(gè)鏈傳遞該請(qǐng)求,直到一個(gè)對(duì)象處理它。
- 訪問(wèn)者模式:一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作。它使你可以在不改變各元素的類(lèi)的前提下定義作用于這些元素的新操作。
二、詳解Spring核心中的設(shè)計(jì)模式
1.為什么說(shuō)Spring框架中的IOC是解耦的
從代碼層面來(lái)說(shuō),Spring通過(guò)控制反轉(zhuǎn)將對(duì)象之類(lèi)依賴(lài)關(guān)系交由容器管理,如下所示我們的AService 依賴(lài)BService ,BService 可能還會(huì)依賴(lài)于CService 層層遞進(jìn),如果是傳統(tǒng)編碼,我們可能需要通過(guò)硬編碼的方式完成AService 的構(gòu)建。 而Spring通過(guò)IOC思想,在初始化階段將所有對(duì)象都交由三級(jí)緩存管理,將所有java bean初始化責(zé)任的實(shí)現(xiàn)細(xì)節(jié)轉(zhuǎn)移給Spring,使用時(shí)也只需指明接口類(lèi)型,接口實(shí)現(xiàn)也無(wú)需關(guān)心,只需在配置層面中指定,而非通過(guò)硬編碼完成依賴(lài)管理:
所以我們使用時(shí)只需幾個(gè)簡(jiǎn)單的配置和注解即可完成各種復(fù)雜的bean的依賴(lài)管理,這也就是開(kāi)發(fā)層面對(duì)象依賴(lài)關(guān)系管理的解耦:
@Service("aService")
@Slf4j
public class AService {
@Autowired
private BService bService;
//......
}
從使用層面來(lái)說(shuō),Spring中的IOC也用到類(lèi)似于門(mén)面模式的思想,將工具類(lèi)的使用和創(chuàng)建剝離,整個(gè)工具類(lèi)的創(chuàng)建過(guò)程對(duì)使用者來(lái)說(shuō)是透明解耦的。
例如我們需要使用日志框架,在spring中我們只需給出簡(jiǎn)單的配置,框架即在運(yùn)行時(shí)基于給定配置完成對(duì)應(yīng)的日志工具的注入(可以是log4j可以是slf4j或者其他日志框架),程序啟動(dòng)后即可直接使用功能,無(wú)需關(guān)心創(chuàng)建和實(shí)現(xiàn):
2.Spring源碼中涉及的簡(jiǎn)單工廠模式
簡(jiǎn)單工廠模式的思想就是對(duì)外屏蔽創(chuàng)建對(duì)象的細(xì)節(jié),將對(duì)象的獲取統(tǒng)一內(nèi)聚到一個(gè)工廠類(lèi)中,這一點(diǎn)在Spring中的ApplicationContext 發(fā)揮的淋漓盡致。 我們都知道Spring將所有java bean統(tǒng)一交由三級(jí)緩存進(jìn)行管理,使用時(shí)我們可以通過(guò)上下文或者需要的java bean,用戶(hù)只需按需要傳遞給工廠對(duì)應(yīng)的bean名稱(chēng)即可得到自己需要的對(duì)象即可:
對(duì)應(yīng)的我們也給出使用示例:
@Autowired
private ApplicationContext applicationContext;
public Object getBean(String beanName) {
Object bean = applicationContext.getBean(beanName);
return bean;
}
3.Spring中的工廠方法模式
工廠方法模式適用于想讓工廠專(zhuān)注創(chuàng)建一個(gè)對(duì)象的場(chǎng)景,相較于簡(jiǎn)單工廠模式,工廠方法模式思想是提供一個(gè)工廠的接口,開(kāi)發(fā)者根據(jù)這個(gè)規(guī)范創(chuàng)建不同的工廠,然后按需使用不同的工廠創(chuàng)建不同的類(lèi)即可。這種做法確保了工廠類(lèi)也遵循開(kāi)閉原則。
Spring中的FactoryBean就是工廠方法模式的典型實(shí)現(xiàn),如果我們希望容器中能夠提供一個(gè)可以創(chuàng)造指定類(lèi)的工廠,那么我們就可以通過(guò)FactoryBean實(shí)現(xiàn)。 例如我們希望有一個(gè)工廠可以創(chuàng)建經(jīng)理,另一個(gè)工廠可以創(chuàng)建主管。那么我們就可以通過(guò)FactoryBean實(shí)現(xiàn)。 實(shí)現(xiàn)步驟如下,由于經(jīng)理和主管都是雇員類(lèi),所以我們創(chuàng)建一個(gè)雇員類(lèi):
//雇員類(lèi)
public class EmployeeDTO {
private Integer id;
private String firstName;
private String lastName;
private String designation;
//Setters and Getters are hidden behind this comment.
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
@Override
public String toString() {
return "Employee [id=" + id + ", firstName=" + firstName
+ ", lastName=" + lastName + ", type=" + designation + "]";
}
}
然后我們繼承FactoryBean接口實(shí)現(xiàn)一個(gè)工廠方法類(lèi),如下所示,可以看到如果我們可以根據(jù)傳入的designation決定創(chuàng)建的雇員類(lèi)型。
public class EmployeeFactoryBean extends AbstractFactoryBean<Object> {
// 根據(jù)這個(gè)值決定創(chuàng)建主管還是經(jīng)理
private String designation;
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
//This method will be called by container to create new instances
@Override
protected Object createInstance() throws Exception {
EmployeeDTO employee = new EmployeeDTO();
employee.setId(-1);
employee.setFirstName("dummy");
employee.setLastName("dummy");
//Set designation here
employee.setDesignation(designation);
return employee;
}
//This method is required for autowiring to work correctly
@Override
public Class<EmployeeDTO> getObjectType() {
return EmployeeDTO.class;
}
}
兩種雇員的配置如下所示:
<!--factoryBean使用示例-->
<!--經(jīng)理工廠-->
<bean id="manager" class="com.study.service.EmployeeFactoryBean">
<property name="designation" value="Manager" />
</bean>
<!--主管工廠-->
<bean id="director" class="com.study.service.EmployeeFactoryBean">
<property name="designation" value="Director" />
</bean>
如果我們想創(chuàng)建director(主管)的工廠,那么我們的代碼就可以這樣使用,注意我們獲取bean時(shí)必須使用&,否則獲得的就不是EmployeeFactoryBean,則是EmployeeDTO
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Object factory = context.getBean("&director");
System.out.println(factory);
//工廠方法模式,通過(guò)單一職責(zé)的工廠獲取專(zhuān)門(mén)的類(lèi)
System.out.println(((EmployeeFactoryBean) factory).getObject());
當(dāng)然,如果想直接獲取高管或者經(jīng)理,獲取bean時(shí)不加&即可代碼如下所示即可:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
EmployeeDTO manager = (EmployeeDTO) context.getBean("manager");
System.out.println(manager);
Object director = context.getBean("director");
System.out.println(director);
4.工廠方法模式相較于簡(jiǎn)單工廠模式的優(yōu)缺點(diǎn)
工廠方法模式的優(yōu)點(diǎn):
- 符合開(kāi)閉原則,相較于上面說(shuō)到的簡(jiǎn)單工廠模式來(lái)說(shuō),我們無(wú)需因?yàn)樵黾右粋€(gè)類(lèi)型而去修改工廠代碼,我們完全可以通過(guò)實(shí)現(xiàn)一個(gè)新的工廠實(shí)現(xiàn)。
- 更符合單一職責(zé)的原則,對(duì)于單個(gè)類(lèi)型創(chuàng)建的工廠邏輯更加易于維護(hù)。
缺點(diǎn):
- 針對(duì)特定類(lèi)型都需要?jiǎng)?chuàng)建特定工廠,創(chuàng)建的類(lèi)會(huì)增加,導(dǎo)致項(xiàng)目變得臃腫。
- 因?yàn)楣S方法的模式結(jié)構(gòu),維護(hù)的成本相對(duì)于簡(jiǎn)單工廠模式會(huì)更高一些。
5.單例模式在Java中的使用優(yōu)勢(shì)
節(jié)省沒(méi)必要的創(chuàng)建對(duì)象的時(shí)間,由于是單例的對(duì)象,所以創(chuàng)建一次后就可以一直使用了,所以我們無(wú)需為了一個(gè)重量級(jí)對(duì)象的創(chuàng)建而耗費(fèi)大量的資源。
由于重量級(jí)對(duì)象的創(chuàng)建次數(shù)少了,所以我們就避免了沒(méi)必要的GC。從而降低GC壓力,避免沒(méi)必要的STW(Stop the World)導(dǎo)致的GC停頓。
6.Spring中單例模式的實(shí)現(xiàn)
Spring中獲取對(duì)象實(shí)例的方法即DefaultSingletonBeanRegistry中的getSingleton就是典型的Double-Checked Locking單例模式代碼:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
//一級(jí)緩存沒(méi)有需要的bean,進(jìn)入該邏輯
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//二級(jí)對(duì)象也沒(méi)有,上鎖進(jìn)入創(chuàng)建邏輯
synchronized (this.singletonObjects) {
// 再次檢查一級(jí)緩存也沒(méi)有,避免重復(fù)創(chuàng)建問(wèn)題
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//......
//創(chuàng)建對(duì)象存入二級(jí)緩存中
}
}
}
}
}
return singletonObject;
}
7.Spring中的代理模式
代理模式解耦了調(diào)用者和被調(diào)用者的關(guān)系,同時(shí)通過(guò)對(duì)原生類(lèi)型的代理進(jìn)行增強(qiáng)易于拓展和維護(hù),Spring AOP就是通過(guò)代理模式實(shí)現(xiàn)增強(qiáng)切入,我們就以JDK代理為例查看Spring中的實(shí)現(xiàn):
public Object getProxy(@Nullable ClassLoader classLoader) {
// 忽略代碼
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//通過(guò)被代理的類(lèi)的接口以及增強(qiáng)邏輯創(chuàng)建一個(gè)增強(qiáng)的用戶(hù)所需要的類(lèi)
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
查看newProxyInstance的實(shí)現(xiàn)即可看到j(luò)dk代理的傳統(tǒng)創(chuàng)建細(xì)節(jié)即拿到被代理的類(lèi)類(lèi)型和需要增強(qiáng)后的方法實(shí)現(xiàn)InvocationHandler 通過(guò)反射完成代理類(lèi)創(chuàng)建:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//......
//獲取接口類(lèi)類(lèi)型
Class<?> cl = getProxyClass0(loader, intfs);
//......
//從緩存中獲取代理類(lèi)的構(gòu)造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//......
//基于InvocationHandler 和構(gòu)造方法完成代理類(lèi)創(chuàng)建
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
//......
} catch (InvocationTargetException e) {
//......
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
8.Spring中的模板方法模式
模板方法模式即固定一個(gè)算法骨架,抽象出某些可變的方法交給子類(lèi)實(shí)現(xiàn),Spring的AbstractApplicationContext的refresh方法就是典型模板方法模式,
@Override
public void refresh() throws BeansException, IllegalStateException {
// 給容器refresh加鎖,避免容器處在refresh階段時(shí),容器進(jìn)行了初始化或者銷(xiāo)毀的操作
synchronized (this.startupShutdownMonitor) {
// .........
try {
// .........
//定義了相關(guān)接口給用戶(hù)實(shí)現(xiàn),該方法會(huì)通過(guò)回調(diào)的方式調(diào)用這些方法,已經(jīng)實(shí)現(xiàn)好的細(xì)節(jié)
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊(cè)攔截bean創(chuàng)建過(guò)程的BeanPostProcessor,已經(jīng)實(shí)現(xiàn)好的細(xì)節(jié)
registerBeanPostProcessors(beanFactory);
//模板方法的體現(xiàn),用戶(hù)可自定義重寫(xiě)該方法
onRefresh();
//.......
}
// .......
}
}
9.模板方法模式的優(yōu)劣勢(shì)
優(yōu)勢(shì)很明顯:
- 算法骨架由父類(lèi)定義,封裝不變,擴(kuò)展可變。
- 子類(lèi)只需按需實(shí)現(xiàn)抽象類(lèi)即可,易于擴(kuò)展維護(hù)。
- 提取了公共代碼,避免編寫(xiě)重復(fù)代碼。
缺點(diǎn):
- 可讀性下降
- 可能會(huì)導(dǎo)致子類(lèi)泛濫問(wèn)題。
10.Spring中的觀察者模式
觀察者模式是一種行為型模式。 它表示的是一種主題與訂閱者之間具有依賴(lài)關(guān)系,當(dāng)訂閱者訂閱的主題狀態(tài)發(fā)生改變,會(huì)發(fā)送通知給響應(yīng)訂閱者,觸發(fā)訂閱者的響應(yīng)操作。
Spring 事件驅(qū)動(dòng)模型就是觀察者模式很經(jīng)典的一個(gè)應(yīng)用。Spring 事件驅(qū)動(dòng)模型非常有用,在很多場(chǎng)景都可以解耦我們的代碼。比如我們每次發(fā)布一個(gè)通知就需要某個(gè)用戶(hù)做出收到的響應(yīng),這個(gè)時(shí)候就可以利用觀察者模式來(lái)解決這個(gè)問(wèn)題。
首先我們需要定義一個(gè)事件類(lèi):
public class DemoEvent extends ApplicationEvent {
private String msg;
public DemoEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
然后創(chuàng)建一個(gè)監(jiān)聽(tīng)器集成Spring框架的ApplicationListener,對(duì)DemoEvent進(jìn)行監(jiān)聽(tīng):
@Component
public class DemoListener implements ApplicationListener<DemoEvent> {
private static Logger logger = LoggerFactory.getLogger(DemoListener.class);
@Override
public void onApplicationEvent(DemoEvent event) {
logger.info("收到消息,消息內(nèi)容:{}", event.getMsg());
}
}
這樣我們發(fā)布事件后,監(jiān)聽(tīng)器就接受并處理返回結(jié)果了:
@Autowired
private ApplicationContext applicationContext;
@Test
public void sendMsg() {
DemoEvent event = new DemoEvent(this, "你好");
applicationContext.publishEvent(event);
}
對(duì)應(yīng)輸出結(jié)果如下所示:
2022-11-22 13:03:15.814 INFO 15600 --- [ main] com.example.demo.DemoListener : 收到消息,消息內(nèi)容:你好
11.Spring中用到的適配器模式
適配器模式有用在在DisposableBeanAdapter適配統(tǒng)一處理bean銷(xiāo)毀上,因?yàn)槲覀僢ean銷(xiāo)毀方法可以通過(guò)xml配置也可以通過(guò)繼承DisposableBean接口實(shí)現(xiàn),所以這兩種不同的方法在銷(xiāo)毀時(shí)處理方式可能不一樣,所以我們可以通過(guò)適配器模式將這兩個(gè)處理邏輯封裝成統(tǒng)一適配器進(jìn)行處理
@Override
public void destroy() {
//......
//如果是接口實(shí)現(xiàn)則執(zhí)行該類(lèi)實(shí)現(xiàn)的destroy方法
if (this.invokeDisposableBean) {
//......
try {
if (System.getSecurityManager() != null) {
//......
}
else {
//
((DisposableBean) this.bean).destroy();
}
}
catch (Throwable ex) {
//......
}
}
//如果有注解或者xml配置的方法,則走invokeCustomDestroyMethod進(jìn)行調(diào)用
if (this.destroyMethod != null) {
invokeCustomDestroyMethod(this.destroyMethod);
}
else if (this.destroyMethodName != null) {
Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
if (methodToInvoke != null) {
invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
}
}
}
12.Spring中的裝飾者模式
裝飾者模式即通過(guò)組合的方式對(duì)原有類(lèi)的行為進(jìn)行一些擴(kuò)展操作即在開(kāi)閉原則下的一種結(jié)構(gòu)型設(shè)計(jì)模式,就以Spring中的TransactionAwareCacheDecorator為例,它就是實(shí)現(xiàn)緩存支持事務(wù)的功能,繼承緩存接口,并將目標(biāo)緩存類(lèi)組合進(jìn)來(lái),保證原有類(lèi)不被修改的情況下實(shí)現(xiàn)功能的擴(kuò)展:
//繼承Cache 類(lèi)
public class TransactionAwareCacheDecorator implements Cache {
//行為需要擴(kuò)展的目標(biāo)類(lèi)
private final Cache targetCache;
// 從接口那里獲得的put方法,通過(guò)對(duì)targetCache的put進(jìn)行進(jìn)一步封裝實(shí)現(xiàn)功能的包裝
@Override
public void put(final Object key, @Nullable final Object value) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
TransactionAwareCacheDecorator.this.targetCache.put(key, value);
}
});
}
else {
this.targetCache.put(key, value);
}
}
}
三、更多關(guān)于設(shè)計(jì)模式
1.什么是策略模式
策略模式是一種行為模式,它的作用主要是用于封裝那些動(dòng)作一致,但是具體實(shí)現(xiàn)不一致的場(chǎng)景。例如我們現(xiàn)在有一個(gè)審核的動(dòng)作,不同的人審核的方式不同。而且審核的行為會(huì)隨著時(shí)間推移不斷增加,單純使用if else去維護(hù)會(huì)使得代碼變得非常凌亂。 這時(shí)候使用策略模式定義一下相同的行為,讓子類(lèi)去實(shí)現(xiàn)不同的策略,這樣的方式是最利于拓展的。
2.策略模式在Spring中如何使用
使用策略模式前,我們先需要使用接口固定一下策略的定義,例如我們現(xiàn)在要?jiǎng)?chuàng)建一個(gè)生成對(duì)象的策略createObj:
public interface CreateObjStrategy {
Object createObj();
}
對(duì)應(yīng)我們分別寫(xiě)一個(gè)創(chuàng)建字符串對(duì)象和json對(duì)象的策略代碼:
@Component("createObj_str")
public class CreateStrObjStrategy implements CreateObjStrategy {
@Override
public Object createObj() {
Map<String, Object> map = new HashMap<>();
map.put("name", "xiaoming");
return JSONUtil.toJsonStr(map);
}
}
@Component("createObj_json")
public class CreateJsonObjStrategy implements CreateObjStrategy {
@Override
public Object createObj() {
JSONObject obj = new JSONObject();
obj.put("name", "zhangsan");
return obj;
}
}
到此為止,我們已經(jīng)完成了所有的策略的封裝,按照原生的策略模式的做法它會(huì)通過(guò)一個(gè)上下文來(lái)配置當(dāng)前策略,就像下面這張?jiān)O(shè)計(jì)圖:
對(duì)應(yīng)我們給出封裝的代碼示例:
public class CreateJsonObjContext {
private CreateObjStrategy createObjStrategy;
public CreateJsonObjContext(CreateObjStrategy strategy) {
this.createObjStrategy = strategy;
}
public Object createObj() {
return createObjStrategy.createObj();
}
}
實(shí)際上有了Spring我們無(wú)需進(jìn)行顯示聲明創(chuàng)建了,我們可以通過(guò)配置、注解等方式指明本地程序需要注入的CreateObjStrategy 實(shí)現(xiàn),加載上下文的時(shí)候通過(guò)ApplicationContextAware這樣的擴(kuò)展點(diǎn)把bean取出來(lái)設(shè)置到CreateJsonObjContext 中:
@Component
public class CreateJsonObjContext implements ApplicationContextAware {
private CreateObjStrategy createObjStrategy;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//從容器中拿到由我們配置后容器中所設(shè)置的CreateObjStrategy設(shè)置到Context中
CreateObjStrategy bean = applicationContext.getBean(CreateObjStrategy.class);
createObjStrategy = bean;
}
public Object createObj() {
return createObjStrategy.createObj();
}
}
3.Spring中策略模式的運(yùn)用
最典型的就時(shí)InstantiationStrategy,Spring為其實(shí)現(xiàn)了兩種不同的策略,分別是CglibSubclassingInstantiationStrategy和SimpleInstantiationStrategy,在Spring通過(guò)實(shí)例化java bean的時(shí)候其內(nèi)部就會(huì)通過(guò)getInstantiationStrategy從上下文中獲取初始化策略instantiationStrategy,然后調(diào)用instantiationStrategy的instantiate完成bean的創(chuàng)建:
對(duì)應(yīng)的我們也給出AbstractAutowireCapableBeanFactory中instantiate實(shí)例化bean的實(shí)現(xiàn)印證這一說(shuō)法:
private Object instantiate(String beanName, RootBeanDefinition mbd,
@Nullable Object factoryBean, Method factoryMethod, Object[] args) {
try {
else {
//通過(guò)getInstantiationStrategy獲取上下文中對(duì)應(yīng)的創(chuàng)建策略完成bean的創(chuàng)建
return this.beanFactory.getInstantiationStrategy().instantiate(
mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
}
}
catch (Throwable ex) {
//......
}
}