在上一篇aop的原理后,本篇决定手撸一个最简易的AOP框架,好让大家能对AOP的实现有个“去繁从简”的认识。这里也分享下自己学习框架源码的心得,学习框架源码效率比较高的一种方式是:先理解框架设计,然后根据自己理解去造一个小轮子(这个框架的简易版本),然后慢慢地再对照框架源码添加特性到那个小轮子,这样吸收到的框架知识是会非常扎实的。该例子主要展示如何扫描切面类中的方法组装调用方法链、使用cglib工具类生成目标类的代理类。整个例子的设计只需要三步,总代码行数不足200行。
第一步,切面注解/代理容器/日志切面定义/异常切面定义等基本组成。
@Retention(RUNTIME)
@Target(TYPE)
public @interface AspectAnnotation {}
public enum InterceptPoint {
BEFORE, AFTER, EXCEPTION
}
//日志切面
@AspectAnnotation
public class LogAspect {
public static void before(Object object, Method method, Object[] args) {
System.out.println("log before");
}
public static void after(Object object, Method method, Object[] args, Object result) {
System.out.println("log end");
}
}
//异常切面
@AspectAnnotation
public class ExceptionAspect {
public static void exception(Object object, Method method, Object[] args, Throwable e) {
System.out.println("log exception");
}
}
//目标类的
public class ServiceA {
//目标方法
public void test() {
System.out.println("invoke serviceA test method");
}
}
第二步,利用反射技术来生成拦截方法数组,这里定义了一个以被代理类的类型为key的调用链路的map容器,根据特定的类型就可以得到需要执行的方法链路。
@Retention(RUNTIME)
@Target(TYPE)
public @interface AspectAnnotation {}
public enum InterceptPoint {
BEFORE, AFTER, EXCEPTION
}
//日志切面
@AspectAnnotation
public class LogAspect {
public static void before(Object object, Method method, Object[] args) {
System.out.println("log before");
}
public static void after(Object object, Method method, Object[] args, Object result) {
System.out.println("log end");
}
}
//异常切面
@AspectAnnotation
public class ExceptionAspect {
public static void exception(Object object, Method method, Object[] args, Throwable e) {
System.out.println("log exception");
}
}
//目标类的
public class ServiceA {
//目标方法
public void test() {
System.out.println("invoke serviceA test method");
}
}
第二步,利用反射技术来生成拦截方法调用链路,这里定义了一个以被代理类的类型为key的调用链路的map容器,根据特定的类型就可以得到需要执行的方法链路。调用的示意图如下。
//定义切面类数组
static Class[] aspects = new Class[]{LogAspect.class, ExceptionAspect.class};
//代理目标类
static Class[] targetClasses = new Class[]{ServiceA.class};
//根据反射拿到方法对象
private static Method getMethod(Class cls, String name, Class[] parameters) {
try {
return cls.getMethod(name, parameters);
} catch (Exception ex) {
}
return null;
}
//添加拦截方法
private static void addInterceptMethod(Class cls, InterceptPoint point, Method method) {
if (method == null) {
return;
}
Map> map = interceptMethodsMap.get(cls);
if (map == null) {
map = new HashMap<>();
interceptMethodsMap.put(cls, map);
}
List methods = map.get(point);
if (methods == null) {
methods = new ArrayList<>();
map.put(point, methods);
}
methods.add(method);
}
第三步,使用cglib的MethodInterceptor进行方法拦截,我们把具体的改写后的执行过程重新写到intercept方法中,这样我们就完成了对原代理对象执行方法的拦截调用。
//生成代理对象
private static T createInstance(Class cls) throws InstantiationException, IllegalAccessException {
if (!interceptMethodsMap.containsKey(cls)) {
return cls.newInstance();
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls); //设置被代理类
enhancer.setCallback(new AspectInterceptor()); //设置拦截方法类
return (T) enhancer.create(); //返回代理类对象
}
//定义拦截方法类
static class AspectInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//获取被代理类的before方法链
List beforeMethods = getInterceptMethods(object.getClass().getSuperclass(), InterceptPoint.BEFORE);
//逐个调用链中的方法
for (Method m : beforeMethods) {
m.invoke(null, new Object[]{object, method, args});
}
try {
//调用被代理类的原始方法
Object result = proxy.invokeSuper(object, args);
//获取被代理类的after方法链
List afterMethods = getInterceptMethods(object.getClass().getSuperclass(), InterceptPoint.AFTER);
//逐个调用链中的方法
for (Method m : afterMethods) {
m.invoke(null, new Object[]{object, method, args, result});
}
return result;
} catch (Throwable e) {
//获取被代理类的exception方法链
List exceptionMethods = getInterceptMethods(object.getClass().getSuperclass(), InterceptPoint.EXCEPTION);
//逐个调用链中的方法
for (Method m : exceptionMethods) {
m.invoke(null, new Object[]{object, method, args, e});
}
throw e;
}
}
static List getInterceptMethods(Class cls, InterceptPoint point) {
Map> map = interceptMethodsMap.get(cls);
if (map == null) {
return Collections.emptyList();
}
List methods = map.get(point);
if (methods == null) {
return Collections.emptyList();
}
return methods;
}
}
经过简单三步,我们就完成了极度简易的aop框架的开发,正所谓万变不离其宗,其他的AOP框架如aspectj等实现切面/切点的效果也无非是通过各种手段(xml/annotation)等组装好的方法调用链路,然后使用代理工具库类cglib/jdk的拦截接口,对原有代理对象的方法改写。三步下来,完美设计一个简易aop框架。下一篇将会去看看大名鼎鼎aspectj框架的运行原理。