AOP
(Aspect Oriented Programming,面向切面编程),可以在运行时动态地将代码切入到类中指定方法、指定位置上的一种技术。说白了,就是把 横切逻辑 从 业务逻辑 中抽离出来。哪些属于 横切逻辑 呢?比如,性能监控、日志、事务管理、权限控制等等。
更详细的介绍请参考(Spring AOP 实现原理)。
依赖 CGLib
实现一个简单 aop
。这里假设大家已经了解了 动态代理(若不清楚,请自行 Google
,其实挺简单的)。
我们的目标是使得下面的代码可行,它可以拦截 Controller
注解修饰的类,为类中方法提供增强逻辑(比如,下面可统计方法执行时间)。
说白了,aop
的终极目标就是:使用 代理对象 替换 目标对象(或称为被代理对象)。代理对象是代理类的实例,它的方法中含有我们的横切逻辑和业务逻辑,代理类是使用 CGLib
动态生成的。我们可以用 map
来存储 “代理对象”,每个 “代理对象” 对应的 key
为 “目标类”。
【可能已经被我绕晕了 +_+ !OK,我的目的达到了!没事,继续往下看,最后你会发现这些也就那么回事~】
// ControllerAspect.java
/**
* 拦截 controller 的所有方法
*/
@Aspect(Controller.class)
public class ControllerAspect extends AspectProxy {
private final static Logger LOGGER = LoggerFactory.getLogger(ControllerAspect.class);
private long beginTime;
// 前置增强
@Override
protected void before(Class targetClass, Method method, Object[] params) {
LOGGER.debug("------ begin ------");
LOGGER.debug(String.format("class :: %s", targetClass.getName()));
LOGGER.debug(String.format("method :: %s", method.getName()));
beginTime = System.currentTimeMillis();
}
// 后置增强
@Override
protected void after(Class targetClass, Method method, Object[] params) {
LOGGER.debug(String.format("time :: %dms", System.currentTimeMillis() - beginTime));
LOGGER.debug("------ end ------");
}
}
aop
支持 链式代理,那么我们该怎样实现呢?map
存储这个关系),而我们的目标是要得到 (目标类,代理对象) 这一映射。怎么由前者转换为后者呢?doProxy()
方法,它配合 ProxyChain
类中的 doProxyChain()
方法实现 链式代理。注意 ProxyChain proxyChain
参数,它存储了 “目标类” 的 “代理链”,其实它泛型参数 T
就是指目标类。 package top.inotwant.proxy;
/**
* 代理接口
*/
public interface Proxy {
/**
* 链式处理操作
*
* @param proxyChain 描述 被代理者 对应的代理链
*/
Object doProxy(ProxyChain proxyChain);
}
ProxyChain
,它总体上描述被代理者(目标类)对应的代理链(代理类集合)。注意,代理的最小单元是方法。所以在类中存在 method
与 methodProxy
这两个属性。更精确地说,一个 ProxyChain
实例,封装一个具体方法的代理过程。 package top.inotwant.proxy;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.List;
/**
* 描述 被代理者 对应的代理链
*/
public class ProxyChain<T> {
private final Class targetClass; // 目标类
private final T targetObject; // 目标对象
private final Method method; // 此次被代理的方法(被代理的最小单元为方法)
private final MethodProxy methodProxy; // 所属 cgLib ,由 cgLib 提供,最终由它执行原目标类中的方法
private final Object[] params; // 此次被代理的方法的参数
private List proxyList; // 代理链
private int index = 0; // index 指示将要执行的 “增强(或称为‘横切逻辑’)”
public ProxyChain(Class targetClass, T targetObject, Method method, MethodProxy methodProxy, Object[] params, List proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.method = method;
this.methodProxy = methodProxy;
this.params = params;
this.proxyList = proxyList;
}
public Class getTargetClass() {
return targetClass;
}
public Method getMethod() {
return method;
}
public MethodProxy getMethodProxy() {
return methodProxy;
}
public Object[] getParams() {
return params;
}
public T getTargetObject() {
return targetObject;
}
/**
* 配合 doProxy() 以及利用 index 实现 “链式代理”
*/
public Object doProxyChain() throws Throwable {
Object result;
if (this.index >= proxyList.size()) {
result = methodProxy.invokeSuper(targetObject, params);
this.index = 0; // TODO 自己添加,为了实现 链式代理 的多次调用
} else {
result = proxyList.get(this.index++).doProxy(this);
}
return result;
}
}
Proxy
的模板类 AspectProxy
(很容易理解它为什么被称为模板类)。其中,doProxy()
方法很重要,它与上面的 doProxyChain()
搭配实现链式代理。建议,画一个调用栈模拟一下代理过程。当我们搞清楚后,会发现这个结构很巧妙! package top.inotwant.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
/**
* Proxy 的 “模板类”
*/
public abstract class AspectProxy implements Proxy {
private final static Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class);
@Override
public Object doProxy(ProxyChain proxyChain) {
Class targetClass = proxyChain.getTargetClass();
Method method = proxyChain.getMethod();
Object[] params = proxyChain.getParams();
Object result;
try {
if (intercept(targetClass, method, params)) { // 拦截条件
before(targetClass, method, params); // 前置增强
result = proxyChain.doProxyChain(); // 此处很重要,用于实现链式代理
after(targetClass, method, params); // 后置增强
} else {
result = proxyChain.doProxyChain();
}
} catch (Throwable e) { // 这里处理了 doProxyChain() 抛出的异常
error(targetClass, method, params);
LOGGER.error("aspect proxy fail", e);
throw new RuntimeException(e);
} finally {
end();
}
return result;
}
// 重写此以实现 “拦截条件”
protected boolean intercept(Class targetClass, Method method, Object[] params) {
return true;
}
// 重写此以实现 “前置增强”
protected void before(Class targetClass, Method method, Object[] params) {
}
// 重写此以实现 “后置增强”
protected void after(Class targetClass, Method method, Object[] params) {
}
// 重写此以实现 “抛出增强”
protected void error(Class targetClass, Method method, Object[] params) {
}
// 重写此以实现 "结束增强"
protected void end() {
}
}
cgLib
的支持了,我们创建了下面这个类封装了这个过程。阅读后你会发现: ProxyChain
的构造方法所需的大部分参数,cgLib
都给提供了。实际上我们是结合了 cgLib
后才创建的 ProxyChain
。我们顺一下代理过程:现在我们已拿到了代理对象,用代理对象调用原目标类的某一方法时,cgLib
会调用 MethodInterceptor
的 intercept()
方法(见下面,Enhancer.create()
方法的第二个参数,这里使用了匿名类)。调用时,cgLib
自然会把参数准备好,这些参数描述了要代理的单元。然后,正如 intercept()
方法中所描述的,我们先创建一个 ProxyChain
的实例。接着调用该实例中的 doProxyChain()
方法,该方法中描述的就是上面用调用栈模拟的过程(具体的链式代理过程)。最后返回原方法的执行结果。
package top.inotwant.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;
public class ProxyManager {
/**
* 获取代理对象
* @param targetClass 目标类
* @param proxyList 代理链
* @return 代理对象
*/
@SuppressWarnings("unchecked")
public static T getProxyInstance(final Class targetClass, final List proxyList) {
return (T) Enhancer.create(targetClass, new MethodInterceptor() {
@Override
public Object intercept(Object targetObject, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
return new ProxyChain<>(targetClass, (T) targetObject, method, methodProxy, params, proxyList).doProxyChain();
}
});
}
}
Aspect
,作用于代理类上,用于描述该代理类作用于哪些目标类(用 value
指定) package top.inotwant.annocation;
import java.lang.annotation.*;
/**
* AOP 的 切面 注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
Class extends Annotation> value();
}
ProxyManager
中的 getProxyInstance()
获取代理对象)。所以下面的 getProxyMap()
就是为了产生(目标类,代理链) 映射。然后,只需在 static 块中使用 “代理对象” 替换 “被代理对象” 即可。后面要使用目标类的对象时,只需要在 BeanHelper
获取即可(获取的对象为 “代理对象” 它的方法中包含了横切逻辑)。 package top.inotwant.helper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.inotwant.annocation.Aspect;
import top.inotwant.proxy.AspectProxy;
import top.inotwant.proxy.Proxy;
import top.inotwant.proxy.ProxyManager;
import top.inotwant.proxy.TransactionProxy;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* AOP 实现类
*/
public final class AopHelper {
private final static Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);
static {
LOGGER.warn("======================AOP HELPER=========================");
Map, List> proxyMap;
try {
proxyMap = getProxyMap();
for (Map.Entry, List> entry : proxyMap.entrySet()) {
Class> sourceClass = entry.getKey();
List proxyList = entry.getValue();
Object proxyInstance = ProxyManager.getProxyInstance(sourceClass, proxyList);
// 使用 “代理对象” 替换 “被代理对象”
BeanHelper.putBean(sourceClass, proxyInstance);
}
} catch (Exception e) {
LOGGER.error("aop helper fail", e);
throw new RuntimeException(e);
}
}
/**
* 生成 (被代理类,代理类集(或称为代理链)) 映射
*/
public static Map, List> getProxyMap() throws Exception {
// 用于存储 (目标类,代理链),即返回结果
Map, List> result = new HashMap<>();
// 获取 AspectProxy 的所有子类
Set> proxySet = ClassHelper.getSubClassSet(AspectProxy.class);
for (Class> proxyClass : proxySet) {
// 判断子类是否被 Aspect 修饰,若被修饰说明它是一个 代理类
if (proxyClass.isAnnotationPresent(Aspect.class)) {
Aspect aspect = proxyClass.getAnnotation(Aspect.class);
// 获取该代理类对应注解标识(下面将使用该标识获取所有的目标类(或称为被代理类))
Class extends Annotation> value = aspect.value();
if (!value.equals(Aspect.class)) {
// 获取代理类对应的所有目标类
Set> annotationClassSet = ClassHelper.getAnnotationClassSet(value);
// 通过遍历代理类集合,不断生成 (目标类,代理链)映射
for (Class> sourceClass : annotationClassSet) {
if (result.get(sourceClass) == null) {
List proxyList = new ArrayList<>();
proxyList.add((Proxy) proxyClass.newInstance());
result.put(sourceClass, proxyList);
} else {
result.get(sourceClass).add((Proxy) proxyClass.newInstance());
}
}
}
}
}
return result;
}
}