目錄
AOP的由來
AOP的出現(xiàn)并不是要完全取代OOP, 而是作為OOP的補(bǔ)充
按照OOP的思想, 如果多個(gè)類中出現(xiàn)相同的代碼, 應(yīng)該考慮定義一個(gè)基類, 將這些相同的代碼提取到基類中
通過引入基類實(shí)現(xiàn)復(fù)用的方法在大多情況下是可行的, 但是對于下面的情況卻無能為力
public class PostService {
private TransactionManager transManager;
private PerformanceMonitor pmonitor;
private TopicDao topicDao;
private ForumDao forumDao;
public void removeTopic(int topicId) {
pmonitor.start(); // ① 性能監(jiān)控開始
transManager.beginTransaction(); // ② 事務(wù)處理開始
topicDao.removeTopic(topicId); // ③ 業(yè)務(wù)邏輯
transManager.commit(); // ② 事務(wù)處理結(jié)束
pmonitor.end(); // ① 性能監(jiān)控結(jié)束
}
public void createForum(Forum forum) {
pmonitor.start(); // ① 性能監(jiān)控開始
transManager.beginTransaction(); // ② 事務(wù)處理開始
forumDao.create(forum); // ③ 業(yè)務(wù)邏輯
transManager.commit(); // ② 事務(wù)處理結(jié)束
pmonitor.end(); // ① 性能監(jiān)控結(jié)束
}
…
}
由于性能監(jiān)控, 事務(wù)處理的代碼依附在業(yè)務(wù)類方法的流程中, 所以無法抽象到基類中
AOP通過橫向抽取機(jī)制, 為這類無法通過縱向繼承進(jìn)行抽象的重復(fù)性代碼提供了解決方案
通過上述AOP的由來不難看出
AOP并不是"萬金油", 它一般只適合于那些具有橫切邏輯的應(yīng)用場合: 如性能監(jiān)測、訪問控制、事務(wù)管理、日志記錄和異常處理等
什么是AOP?
知道了為什么會(huì)有AOP這么個(gè)"東西", 那到底什么是AOP呢
百度百科中的定義如下
AOP為Aspect Oriented Programming的縮寫, 意為: 面向切面編程, 通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)
Wikipedia中的定義如下
In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a "pointcut" specification, such as "log all function calls when the function's name begins with 'set'". This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code core to the functionality. AOP forms a basis for aspect-oriented software development.
如果說百度百科的解釋比較短, 你還能看下去的話, 那么看到Wiki解釋的長度你可能就望而卻步了
站在"巨人"的肩膀上, 本人對AOP的定義如下
AOP(剖面編程, 對于面對切面編程的翻譯表示不喜歡)是指在運(yùn)行時(shí)動(dòng)態(tài)地將代碼切入到類的指定方法、指定位置上的編程思想
它有兩個(gè)非常關(guān)鍵的特征
不會(huì)修改接口
動(dòng)態(tài)添加實(shí)現(xiàn)
AOP與設(shè)計(jì)模式
AOP與Bridge
對于Bridge模式
Big Four的設(shè)計(jì)模式中的定義如下
Decouple an abstraction from its implementation so that the two can vary independently
將抽象和實(shí)現(xiàn)解耦, 使得兩者可以獨(dú)立地變化
而上面討論AOP的第一個(gè)特點(diǎn)就是
- 不會(huì)修改接口
所以說, AOP體現(xiàn)了Bridge模式的設(shè)計(jì)思想
關(guān)于Bridge的更多介紹, 詳細(xì)參考設(shè)計(jì)模式 之 結(jié)構(gòu)型模式
AOP與Dynamic Proxy
如果說AOP體現(xiàn)了Bridge模式的設(shè)計(jì)思想, 那么AOP的實(shí)現(xiàn)就要基于Dynamic Proxy了
例如下面在方法調(diào)用時(shí)打印日志的例子
final ISubject subject = new RealSubject();
ISubject proxy = (ISubject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), new Class[]{ISubject.class}, new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Log.i("InvocationHandler", method.getName());
Object result = method.invoke(subject, objects);
return result;
}
});
proxy.request();
關(guān)于Dynamic Proxy的介紹, 詳細(xì)參考設(shè)計(jì)模式 之 Proxy
AOP與Spring
本文的標(biāo)題是"談?wù)勔苿?dòng)開發(fā)編程中的AOP(剖面編程)", 為什么要說Spring呢?
啥都不說先, "一言不合"先上個(gè)Spring官方的架構(gòu)圖

tutorialspoint關(guān)于AOP與Spring的描述準(zhǔn)確而全面
為了不丟失信息和保證準(zhǔn)確, 直接引用原文如下
One of the key components of Spring Framework is the Aspect oriented programming (AOP) framework
Aspect Oriented Programming entails breaking down program logic into distinct parts called so-called concerns. The functions that span multiple points of an application are called cross-cutting concerns and these cross-cutting concerns are conceptually separate from the application's business logic
There are various common good examples of aspects like logging, auditing, declarative transactions, security, and caching etc
如果沒有Spring的流行, 及其核心的IoC(Inversion of Control), DI(Dependecy Injection), AOP等思想, 這些概念也不會(huì)如此快如此廣地被人們熟知
更多關(guān)于Spring與AOP, 可以參考11. Aspect Oriented Programming with Spring
AOP與移動(dòng)開發(fā)
Android開發(fā)中的AOP
如果要說Android中設(shè)計(jì), 模式與AOP的集大成者, 那么非Retrofit莫屬了
關(guān)于Retrofit的詳細(xì)分析, 可以參考Retrofit分析-經(jīng)典設(shè)計(jì)模式案例
首先我們先回顧下Retrofit的使用
public interface TestObjectApi {
@GET("classes/TestObject")
@Headers({
"X-LC-Id: kdWDrbX9k02QyGhLof6Injmi-gzGzoHsz",
"X-LC-Key: h2DtBuFcAd2e8NFCq5LY6V86"
})
public Call<ResponseBody> getTestObjects();
}
public class NetworkUtil {
private static OkHttpClient mOkHttpClient = new OkHttpClient();
private static Converter.Factory mFastJsonConverterFactory = FastJsonConverterFactory.create();
private static TestObjectApi mTestObjectApi;
public static TestObjectApi getTestObjectApi() {
if (mTestObjectApi == null) {
Retrofit retrofit = new Retrofit.Builder()
.client(mOkHttpClient)
.baseUrl("https://api.leancloud.cn/1.1/")
.addConverterFactory(mFastJsonConverterFactory)
.build();
mTestObjectApi = retrofit.create(TestObjectApi.class);
}
return mTestObjectApi;
}
}
這里只需要定義接口, 對象是在運(yùn)行時(shí)通過Dynamic Proxy動(dòng)態(tài)生成的
除了這種Dynamic Proxy的實(shí)現(xiàn)方法外
Dependency Injection(依賴注入)也是實(shí)現(xiàn)AOP的常用方式
關(guān)于依賴注入更多可以參考依賴注入原理
例如這里Retrofit.java中的client方法
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
就是將實(shí)現(xiàn)網(wǎng)絡(luò)請求的對象注入到Retrofit對象中, 這種依賴注入滿足了AOP的兩個(gè)核心特征
在接口不變的情況下, 只要是實(shí)現(xiàn)了規(guī)定接口的client, 都可以依賴注入到Retrofit作為實(shí)際的網(wǎng)絡(luò)請求對象
如果有優(yōu)于OkHttp的實(shí)現(xiàn)的話, 完全可以自由靈活的切換到新的實(shí)現(xiàn)方式
如果想了解AOP在android中的應(yīng)用, 可以參考Aspect Oriented Programming in Android
iOS開發(fā)中的AOP
相比于Android中AOP的兩種實(shí)現(xiàn)方式(Dynamic Proxy, Dependency Injection), iOS中AOP的實(shí)現(xiàn)就顯得有點(diǎn)不那么"尋常"
由于Objective-C是基于Smalltalk發(fā)展而來的"消息型"語言, 所以O(shè)bjective-C相比于Java來說實(shí)現(xiàn)起來更加自然和簡單
沒錯(cuò), 就是基于強(qiáng)大的Runtime
例如在不改變系統(tǒng)接口和實(shí)現(xiàn)(當(dāng)然你也沒法改變)的前提, 統(tǒng)計(jì)按鈕的次數(shù)
#import "UIButton+Extension.h"
#import <objc/runtime.h>
@implementation UIButton (Extension)
+ (void)load {
Class buttonClass = [UIButton class];
Method originalMethod = class_getInstanceMethod(buttonClass, @selector(sendAction:to:forEvent:));
Method swizzledMethod = class_getInstanceMethod(buttonClass, @selector(dynamic_sendAction:to:forEvent:));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
- (void)dynamic_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
NSLog(@"counter++");
[self dynamic_sendAction:action to:target forEvent:event];
}
@end
關(guān)于Runtime和Method Swizzling的更多解釋, 可以參考iOS開發(fā) 之 Runtime
很多第三方的iOS庫都是基于Runtime來實(shí)現(xiàn)AOP, 即在不改變接口的情況下, 動(dòng)態(tài)地修改實(shí)現(xiàn)
而且這樣的第三方庫往往都有一個(gè)相同的特點(diǎn): 不會(huì)對原有代碼做任何改動(dòng)
這里我們就拿最近用到的MLeaksFinder來說吧
MLeaksFinder:精準(zhǔn) iOS 內(nèi)存泄露檢測工具 | WeRead團(tuán)隊(duì)博客中對MLeaksFinder實(shí)現(xiàn)分析如下
在一個(gè)ViewController被pop或dismiss一小段時(shí)間后, 看看該UIViewController, 它的view, view的subviews等等是否還存在
這里使用了AOP技術(shù), hook掉UIViewController和UINavigationController的pop跟dismiss方法, 關(guān)于如何 hook, 請參考 Method Swizzling
參考
更多文章, 請支持我的個(gè)人博客