AOP
1. aop 简介
1.1 什么是 aop
AOP
(Aspect Oriented Programming,面向切面编程),可以在运行时动态地将代码切入到类中指定方法、指定位置上的一种技术。说白了,就是把 横切逻辑 从 业务逻辑 中抽离出来。哪些属于 横切逻辑 呢?比如,性能监控、日志、事务管理、权限控制等等。
1.2 aop 综述
更详细的介绍请参考(Spring AOP 实现原理)。
1.3 本文目标
依赖 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 <T> void before(Class<T> 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 <T> void after(Class<T> targetClass, Method method, Object[] params) {
LOGGER.debug(String.format("time :: %dms", System.currentTimeMillis() - beginTime));
LOGGER.debug("------ end ------");
}
}
2. aop 实现
2.1 实现要点
- 在实际应用中我们往往有多个横切需求(比如,既要日志输出又要权限管理),这就需要我们的
aop
支持 链式代理,那么我们该怎样实现呢? - 到后面我们会发现我们很容易得到 (代理类,被代理类) 的映射关系(我们可以用
map
存储这个关系),而我们的目标是要得到 (目标类,代理对象) 这一映射。怎么由前者转换为后者呢?
2.2 详细实现
1)实现链式代理
【1】首先创建一个代理接口,所有的代理类都直接或间接实现它。其中只有 doProxy()
方法,它配合 ProxyChain
类中的 doProxyChain()
方法实现 链式代理。注意 ProxyChain<T> proxyChain
参数,它存储了 “目标类” 的 “代理链”,其实它泛型参数 T
就是指目标类。
package top.inotwant.proxy;
/**
* 代理接口
*/
public interface Proxy {
/**
* 链式处理操作
*
* @param proxyChain 描述 被代理者 对应的代理链
*/
<T> Object doProxy(ProxyChain<T> proxyChain);
}
【2】构造 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<T> targetClass; // 目标类
private final T targetObject; // 目标对象
private final Method method; // 此次被代理的方法(被代理的最小单元为方法)
private final MethodProxy methodProxy; // 所属 cgLib ,由 cgLib 提供,最终由它执行原目标类中的方法
private final Object[] params; // 此次被代理的方法的参数
private List<Proxy> proxyList; // 代理链
private int index = 0; // index 指示将要执行的 “增强(或称为‘横切逻辑’)”
public ProxyChain(Class<T> targetClass, T targetObject, Method method, MethodProxy methodProxy, Object[] params, List<Proxy> proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.method = method;
this.methodProxy = methodProxy;
this.params = params;
this.proxyList = proxyList;
}
public Class<T> 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;
}
}
【3】接下来,我们创建一个 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 <T> Object doProxy(ProxyChain<T> proxyChain) {
Class<T> 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 <T> boolean intercept(Class<T> targetClass, Method method, Object[] params) {
return true;
}
// 重写此以实现 “前置增强”
protected <T> void before(Class<T> targetClass, Method method, Object[] params) {
}
// 重写此以实现 “后置增强”
protected <T> void after(Class<T> targetClass, Method method, Object[] params) {
}
// 重写此以实现 “抛出增强”
protected <T> void error(Class<T> targetClass, Method method, Object[] params) {
}
// 重写此以实现 "结束增强"
protected void end() {
}
}
【4】假如现在我们已经有了 “目标类” 和 “代理链(加在该目标类上的所有代理,或称为代理类集合)”,那么我们如何结合上面三个类来获取 “代理对象” 呢?这就需要 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> T getProxyInstance(final Class<T> targetClass, final List<Proxy> 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();
}
});
}
}
2)实现映射转换
【1】创建注解 Aspect
,作用于代理类上,用于描述该代理类作用于哪些目标类(用 value
指定)
package top.inotwant.annocation;
import java.lang.annotation.*;
/**
* AOP 的 切面 注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
Class<? extends Annotation> value();
}
【2】上面已经提到,要想生成 “代理对象” 需要 “目标类” 和 “代理链”(使用 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<Class<?>, List<Proxy>> proxyMap;
try {
proxyMap = getProxyMap();
for (Map.Entry<Class<?>, List<Proxy>> entry : proxyMap.entrySet()) {
Class<?> sourceClass = entry.getKey();
List<Proxy> 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<Class<?>, List<Proxy>> getProxyMap() throws Exception {
// 用于存储 (目标类,代理链),即返回结果
Map<Class<?>, List<Proxy>> result = new HashMap<>();
// 获取 AspectProxy 的所有子类
Set<Class<?>> 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<Class<?>> annotationClassSet = ClassHelper.getAnnotationClassSet(value);
// 通过遍历代理类集合,不断生成 (目标类,代理链)映射
for (Class<?> sourceClass : annotationClassSet) {
if (result.get(sourceClass) == null) {
List<Proxy> proxyList = new ArrayList<>();
proxyList.add((Proxy) proxyClass.newInstance());
result.put(sourceClass, proxyList);
} else {
result.get(sourceClass).add((Proxy) proxyClass.newInstance());
}
}
}
}
}
return result;
}
}