AOP與裝飾器模式簡(jiǎn)介
Aspect-Oriented Programming(AOP)面向切面編程,相對(duì)而言是Object-Oriented Programming(OOP)面向?qū)ο缶幊掏健<偃绗F(xiàn)在有一個(gè)特殊的需求,我們需要在每個(gè)方法進(jìn)入或退出時(shí)都打印日志案狠。我們不想在所有的方法上都傻傻地寫(xiě)上日志钱雷,想著這種相同的邏輯是否能用一個(gè)段代碼全部實(shí)現(xiàn)呢拉庵?這里我們就可以使用AOP了钞支,我們關(guān)注的是進(jìn)入方法和退出方法的那一個(gè)瞬間(那一個(gè)切面)烁挟。類似地撼嗓,這種需要統(tǒng)一處理的場(chǎng)景或是有著很多相同邏輯的地方我們都可以使用AOP來(lái)解決且警,比如日志斑芜、緩存杏头、鑒權(quán)大州。如果使用OOP解決上述問(wèn)題厦画,我們則會(huì)使用裝飾器模式根暑。
裝飾器模式
裝飾器模式(Decorator pattern)很簡(jiǎn)單排嫌,本質(zhì)上就是動(dòng)態(tài)地為?個(gè)對(duì)象增加功能淳地,但是不改變其結(jié)構(gòu)(用人話解釋就是加上個(gè)“包裝”)颇象,這里就不細(xì)說(shuō)了遣钳,我們實(shí)現(xiàn)一個(gè)帶日志和緩存的裝飾器模式來(lái)簡(jiǎn)單說(shuō)明蕴茴。一般來(lái)說(shuō)裝飾器模式都是和接口來(lái)混用的倦淀。每包一層裝飾器就實(shí)現(xiàn)一個(gè)額外的功能撞叽。帶日志和緩存的裝飾器模式有如下的邏輯:DataService
是一個(gè)接口能扒;DataServiceImpl
實(shí)現(xiàn)了DataService
接口(是一個(gè)基本實(shí)現(xiàn))初斑;LogDecorator
實(shí)現(xiàn)了DataService
见秤,內(nèi)置成員變量DataServiceImpl
鹃答,增加日志邏輯测摔,在合適的地方調(diào)用DataServiceImpl
的基本實(shí)現(xiàn)方法锋八;CacheDecorator
同理羞酗。
定義服務(wù)接口
public interface DataService {
String getData();
}
服務(wù)的基礎(chǔ)實(shí)現(xiàn)
public class DataServiceImpl implements DataService {
@Override
public String getData() {
return UUID.randomUUID().toString();
}
}
加上日志裝飾器
public class LogDecorator implements DataService {
private final DataServiceImpl dataService;
public LogDecorator(DataServiceImpl dataService) {
this.dataService = dataService;
}
@Override
public String getData() {
System.out.println("method invoke");
final String data = dataService.getData();
System.out.println("method finish");
return data;
}
}
加上緩存裝飾器
public class CacheDecorator implements DataService {
private final LogDecorator logDecorator;
private final Map<String, String> map;
public CacheDecorator(LogDecorator logDecorator) {
this.logDecorator = logDecorator;
this.map = new HashMap<>();
}
@Override
public String getData() {
String value = map.get("getData");
if (value == null) {
value = logDecorator.getData();
map.put("getData", value);
}
return value;
}
}
裝飾器有個(gè)很大的問(wèn)題欺嗤,對(duì)于每一個(gè)方法剂府,我們都要手動(dòng)寫(xiě)一遍日志腺占,實(shí)在是太麻煩了铡羡,而且很啰嗦烦周。
AOP的兩種實(shí)現(xiàn):動(dòng)態(tài)代理
什么是動(dòng)態(tài)代理呢读慎?動(dòng)態(tài)指的是運(yùn)行時(shí)動(dòng)態(tài)生成字節(jié)碼夭委,代理指的是攔截掉原先的方法株灸,然后根據(jù)當(dāng)前情況完成相應(yīng)的功能逐抑〔薨保總的來(lái)說(shuō)腐巢,動(dòng)態(tài)代理就是動(dòng)態(tài)生成字節(jié)碼完成一些功能擴(kuò)展肉瓦。一般地泞莉,動(dòng)態(tài)代理可以分為JDK動(dòng)態(tài)代理和動(dòng)態(tài)字節(jié)碼增強(qiáng)鲫趁。
JDK動(dòng)態(tài)代理
使用Proxy.newProxyInstance
進(jìn)行JDK動(dòng)態(tài)代理堡僻,注意第二的參數(shù)需要參數(shù)一個(gè) interface 的 Class 數(shù)組钉疫。第三個(gè)參數(shù)需要實(shí)現(xiàn)InvocationHandler
這個(gè)接口牲阁,處理代理實(shí)例上的方法調(diào)用并返回結(jié)果备燃。
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
public class Main {
public static void main(String[] args) {
final DataService service = new DataServiceImpl();
DataService service1 = (DataService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
new Class[]{DataService.class},
new LogProxy(service));
System.out.println(service1.getData());
}
}
public class LogProxy implements InvocationHandler {
private final DataService delegate;
public LogProxy(DataService delegate) {
this.delegate = delegate;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method invoke");
final Object value = method.invoke(delegate);
System.out.println("method finish");
return value;
}
}
JDK動(dòng)態(tài)代理的優(yōu)點(diǎn)是比較方便,不需要依賴任何第三方庫(kù)冀膝。缺點(diǎn)也比較明顯,功能比較受限赐纱,只適用于接口疙描。
動(dòng)態(tài)字節(jié)碼增強(qiáng)
如果要代理的是個(gè)類不是接口怎么辦?我們可以使用CGLIB或ByteBuddy字節(jié)碼?成效五。他們的本質(zhì)都是在內(nèi)存中生成一個(gè)動(dòng)態(tài)的增強(qiáng)的子類畏妖。他們非常的強(qiáng)大戒劫,但是缺點(diǎn)是需要引?額外的第三?類庫(kù),由于是通過(guò)繼承方式增強(qiáng)的疯攒,所以他們不能增強(qiáng)final
類/final
/private
?法敬尺。關(guān)于ByteBuddy的使用署恍,我們?cè)?a href="http://www.reibang.com/p/f70138744b57" target="_blank">Java 注解這一篇有提到盯质。這里我們使用CGLIB來(lái)做例子呼巷。
public class Main {
public static void main(String[] args) {
final DataServiceImpl service = new DataServiceImpl();
final Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(DataServiceImpl.class);
enhancer.setCallback(new LogInterceptor(service));
final DataServiceImpl service1 = (DataServiceImpl) enhancer.create();
System.out.println(service1.getData());
}
}
這一部分邏輯和上邊JDK動(dòng)態(tài)代理中的LogProxy
還是很像的。
public class LogInterceptor implements MethodInterceptor {
private final DataServiceImpl delegate;
public LogInterceptor(DataServiceImpl delegate) {
this.delegate = delegate;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("method invoke");
final Object value = method.invoke(delegate);
System.out.println("method finish");
return value;
}
}