上一篇我們完成搭建了一個簡單的MVC框架,使其可以通過DispatchServlet來處理所有的請求【從零寫javaweb框架】(八)請求轉發器,這一篇會實現AOP功能。
一.什麼是AOP
AOP(Aspect-Oriented Programming)是對面向對象編程(OOP)的一種補充,可以譯爲面向切面編程。其中切面是AOP的一個術語,表示從業務邏輯中分離出來的橫切邏輯,比如性能監控,日誌記錄、權限控制等,這些邏輯都可從核心的業務邏輯代碼中抽離出去。也就是說,通過AOP可以解決代碼耦合問題,是職責更加單一。
我個人的理解:AOP的作用通俗一點說就是可以在方法的前後附加上其他代碼,每次調用此方法之前或之後(又或者前後都有),都會先後執行附加的代碼。
二.設計模式之代理模式
如果想繼續深入瞭解AOP的實現,我們需要先了解靜態代理模式和動態代理模式,這裏附上我以前寫的兩篇博文(如果之前已經瞭解過代理模式了,那麼可以直接跳過看下一步):
靜態代理模式:設計模式_4:代理模式
動態代理模式:設計模式_25:動態代理模式
三.開發AOP框架
緊接着我之前寫的框架,現在要開始實現AOP了,首先定義一個切面註解:
package org.smart4j.framework.annotation;
import java.lang.annotation.*;
/**
* desc : 切面註解
* Created by Lon on 2018/2/3.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
/**
* 註解
*/
Class<? extends Annotation> value();
}
這個註解的value是一個class,表示我們要對哪個標有此註解類裏的方法進行切入(其實就是要代理哪個類),我們可以註解在一個類上面,表示此類是一個切面類。用法:@Aspect(Controller.class),它表示我們要對標有Controller(這個註解是我們之前編寫的,如果忘記了可以回頭看一看)註解的類進行切入。
然後繼續在框架中添加一個名爲Proxy的接口:
package org.smart4j.framework.proxy;
/**
* desc : 代理接口
* Created by Lon on 2018/2/3.
*/
public interface Proxy {
/**
* 執行鏈式代理
*/
Object doProxy(ProxyChain proxyChain) throws Throwable;
}
這個Proxy接口中有一個doProxy方法,參數是一條代理鏈proxyChain,這條代理鏈的作用是把多個代理串起來,並一個個依次執行,執行順序取決於添加到鏈上的先後順序,ProxyChain代碼:package org.smart4j.framework.proxy;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* desc : 代理鏈
* Created by Lon on 2018/2/3.
*/
public class ProxyChain {
// 要代理的目標類
private final Class<?> targetClass;
// 要代理的目標對象
private final Object targetObject;
// 要代理的目標方法
private final Method targetMethod;
// 目標方法的代理(這個是CGLib爲我們提供的)
private final MethodProxy methodProxy;
// 要代理的目標方法的方法參數
private final Object[] methodParams;
// 代理列表
private List<Proxy> proxyList = new ArrayList<Proxy>();
// 代理索引
private int proxyIndex = 0;
// ProxyChain構造函數
public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod,
MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodProxy = methodProxy;
this.methodParams = methodParams;
this.proxyList = proxyList;
}
public Class<?> getTargetClass() {
return targetClass;
}
public Method getTargetMethod() {
return targetMethod;
}
public Object[] getMethodParams() {
return methodParams;
}
/**
* 如果尚未達到proxyList上限,則從proxyList上拿取相應的對象,執行它的doProxy方法,
* 在Proxy接口的實現中會提供相應的橫切邏輯,並調用doProxyChain方法,隨後將再次調用
* 當前ProxyChain對象的doProxyChain方法,知道proxyIndex達到proxyList上限爲止,
* 到最後再調用methodProxy的invokeSuper方法,執行目標對象的業務邏輯。
*/
public Object doProxyChain() throws Throwable{
Object methodResult;
if (proxyIndex < proxyList.size()) {
methodResult = proxyList.get(proxyIndex++).doProxy(this);
} else {
methodResult = methodProxy.invokeSuper(targetObject, methodParams);
}
return methodResult;
}
}
我們下面一步還用到了動態代理,所以要往maven加入CGLib: <!-- CGLib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
現在寫一個類,用它來動態創建代理對象,命名爲ProxyManager:package org.smart4j.framework.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.List;
/**
* desc : 代理管理器
* Created by Lon on 2018/2/5.
*/
public class ProxyManager {
@SuppressWarnings("unchecked")
public static <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList){
return (T) Enhancer.create(targetClass, new MethodInterceptor() {
public Object intercept(Object targetObject, Method targetMethod,
Object[] methodParams, MethodProxy methodProxy) throws Throwable {
return new ProxyChain(targetClass,targetObject, targetMethod,methodProxy,methodParams,proxyList).doProxyChain();
}
});
}
}
接下來需要通過切面類來調用ProxyManager,寫一個抽象類,讓它提供一個模板方法,並在該抽象類的具體實現中擴展相應的抽象方法:
package org.smart4j.framework.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
/**
* desc : 切面代理類
* Created by Lon on 2018/2/6.
*/
public abstract class AspectProxy implements Proxy{
private static final Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class);
public Object doProxy(ProxyChain proxyChain) throws Throwable{
Object result = null;
Class<?> cls = proxyChain.getTargetClass();
Method method = proxyChain.getTargetMethod();
Object[] params = proxyChain.getMethodParams();
begin();
try {
if (this.intercept(cls, method, params)){
this.before(cls, method, params);
result = proxyChain.doProxyChain();
this.after(cls, method, params, result);
} else {
result = proxyChain.doProxyChain();
}
} catch (Throwable e){
LOGGER.error("proxy failure", e);
error(cls, method, params, e);
throw e;
} finally {
end();
}
return result;
}
public void begin(){
}
public boolean intercept(Class<?> cls, Method method, Object[] params) throws Throwable{
return true;
}
public void before(Class<?> cls, Method method, Object[] params) throws Throwable{
}
public void after(Class<?> cls, Method method, Object[] params, Object result) throws Throwable{
}
public void error(Class<?> cls, Method method, Object[] params, Throwable e) {
}
public void end(){
}
}
在doProxy方法中,我們從proxyChain參數中獲取了目標類、目標方法與方法參數、之後用try/catch/finally代碼塊來實現調用框架,從框架中抽象出一系列的鉤子方法,這些抽象方法可在AspectProxy的子類中有選擇性地進行實現。下一篇會寫加載AOP框架