本篇是spi
的第四篇,本篇講解的是spi
中增長的AOP
,仍是和上一篇同樣,咱們先從你們熟悉的spring引出AOP.java
AOP
是老生常談的話題了,思想都不會是一蹴而就的.好比架構設計從All in One
到SOA
也是一個逐步演進的過程,因此本篇也講講這個AOP的思想演進過程.面試
AOP
,那你講講這用到了什麼設計模式,dubbo又是如何作的.假如咱們就以AOP
最經常使用的場景事務
來講,咱們最初的作法是怎麼樣的?spring
public class EmployeeServiceImpl implements IEmployeeService {
private TransactionManager txManager;
@Override
public void save() {
try {
txManager.begin();
System.out.println("保存操做");
txManager.commit();
}catch (Exception e){
txManager.rollback();
e.printStackTrace();
}
}
@Override
public void update() {
try {
txManager.begin();
System.out.println("更新操做");
txManager.commit();
}catch (Exception e){
txManager.rollback();
e.printStackTrace();
}
}
}
複製代碼
這些代碼存在的問題就很明顯了,好比設計模式
優化代碼咱們第一個想到的是設計模式,那麼咱們進入以下的優化緩存
public class APP {
@Test
public void testSave() throws Exception {
IEmployeeService service = new EmployeeServiceImplWapper(new TransactionManager(),
new EmployeeServiceImpl());
service.save();
}
@Test
public void testUpdate() throws Exception {
IEmployeeService service = new EmployeeServiceImplWapper(new TransactionManager(),
new EmployeeServiceImpl());
service.update();
}
}
複製代碼
經過裝飾設計模式,咱們解決了上面遇到的兩個問題,可是同時也引出了新的問題,在客戶端咱們暴露了真實的對象EmployeeServiceImpl
,這樣就很不安全,那麼咱們可不能夠把真實對象隱藏起來,讓使用者看不到呢?那麼咱們進一步優化安全
經過這種方式,真實對象對使用者進行了必定的隱藏,可是又引出了新的問題架構
save
方法,萬一有不少方法,則須要處理不少次EmployeeServiceImpl
和EmployeeServiceImplProxy
都要改動),增長了代碼的維護難度EmployeeServiceImplProxy
是隻給IEmployeeService
接口服務的,假如我新增了一個IRoleService
,又要搞一個RoleServiceImplProxy
,增長了維護難度鑑於以上問題,咱們可否再優化一下呢?答案是能夠的app
動態代理類是在程序運行期間由JVM經過反射等機制動態的生成的,因此不存在代理類的字節碼文件.代理對象和真實對象的關係是在程序運行事情才肯定的.ide
動態代理的方式和區別咱們前面有講過,這裏就簡單演示一下jdk動態代理函數
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class JDKProxyTest {
@Autowired
private TransactionManagerInvocationHandle handle;
@Test
public void testSave() throws Exception {
IEmployeeService service = handle.getProxyObject();
service.save();
}
@Test
public void testUpdate() throws Exception {
IEmployeeService service = handle.getProxyObject();
service.update();
}
}
複製代碼
public class TransactionManagerInvocationHandle implements InvocationHandler {
@Setter
private TransactionManager txManager;
@Setter
private Object target;//真實對象
//生成代理對象
//泛型只是爲了調用時不用強轉,若是用Object的話調用時須要強轉
public <T> T getProxyObject() {
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),//類加載器
target.getClass().getInterfaces(),//爲哪些接口作代理(攔截什麼方法)
this);//爲哪一個類監聽加強操做的方法(把這些方法攔截到哪裏處理)
}
//如何作加強操做(被攔截的方法在這裏加強處理)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
try {
txManager.begin();
//原封不動調用以前的方法
obj = method.invoke(target, args);
txManager.commit();
return obj;
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
return obj;
}
}
複製代碼
這樣,對於使用者來講,就無需再關心事務的邏輯.固然這個還須要getProxyObject
獲取動態代理對象是否是仍是太麻煩,那如何不調用getProxyObject
就無聲無息的注入動態代理對象呢?能夠觀看以前的dubbo源碼解析-簡單原理、與spring融合
看了這麼多演進的過程,是否是仍是沒有看到dubbo
源碼的影子?由於dubbo
在作spi
的設計的時候,也是有一個演進和優化的過程的.咱們來看看dubbo是怎麼作的
//dubbo spi中的aop
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
複製代碼
下面引用文檔介紹
ExtensionLoader 在加載擴展點時,若是加載到的擴展點有拷貝構造函數,則斷定爲擴展點 Wrapper 類。
Wrapper類內容:
package com.alibaba.xxx;
import com.alibaba.dubbo.rpc.Protocol;
public class XxxProtocolWrapper implemenets Protocol {
Protocol impl;
public XxxProtocol(Protocol protocol) { impl = protocol; }
// 接口方法作一個操做後,再調用extension的方法
public void refer() {
//... 一些操做
impl.refer();
// ... 一些操做
}
// ...
}
複製代碼
經過 Wrapper 類能夠把全部擴展點公共邏輯移至 Wrapper 中。新加的 Wrapper 在全部的擴展點上添加了邏輯,有些相似 AOP,即 Wrapper 代理了擴展點。
看到這裏可能發現,dubbo裏面的spi
增長的aop
,其實就是裝飾者設計模式.可是從上面的演進中咱們發現,裝飾者設計模式仍是有不少弊端的,後面是逐步演進,最後到達動態代理.那dubbo又是如何處理這個弊端逐步演進的?
dubbo裏面有個概念叫擴展點自適應
,也就是給接口注入拓展點是一個Adaptive
實例,直到方法執行時,才決定調用的是哪個拓展點的實現.這個在下一篇的Adaptive
會詳細介紹,本篇其實也是下一篇的啓蒙篇.
既然本篇提到了spring的aop,那麼這裏插播一個小技巧,Spring的AOP
加強方式一共有5種,分別爲
加強類型 | 應用場景 |
---|---|
前置加強 | 權限控制、記錄調用日誌 |
後置加強 | 統計分析結果數據 |
異常加強 | 經過日誌記錄方法異常信息 |
最終加強 | 釋放資源 |
環繞加強 | 緩存、性能、權限、事務管理 |
面試的時候也會問到5種加強方式,可是不少同窗都是說,我天天都在加班,哪有時間記這些.可是其實若是你理解他的設計思想,那麼就能夠"理解性記憶",之後想忘都忘不掉.
//環繞
try {
//前置
System.out.println("=====");
//後置
}catch (Exception e){
//異常
}finally {
//最終
}
複製代碼
其實他這5種方式就是根據try-catch-finally
的模型來設計的,只要你記住了這個設計的思想,天然不會忘記這5種方式,這也是我以前反覆強調的,理解透原理和設計思想,不少東西都是一通百通的.
肥朝 是一個專一於 原理、源碼、開發技巧的技術公衆號,號內原創專題式源碼解析、真實場景源碼原理實戰(重點)。掃描下面二維碼關注肥朝,讓本該造火箭的你,再也不擰螺絲!