AOP的实现底层实际上即为反射,JDK中的反射类java.lang.reflect.Proxy是Java中唯一可以访问调度器的类。类似地,常见的动态代理库cglib也是通过反射机制实现了动态代理的封装。技术成熟度相对较高的AspectJ和Spring AOP会在底层实现一套reflect机制,区别是两者对规范实现如何定义而已。
无论是AspectJ还是Spring AOP,所有这些AOP编程都是基于反射和运行时的动态代理控制实现的。下面通过Proxy和Cglib来实现自己的动态代理切面。
假设我要实现一套基于注解的Trasaction事务管理,通过AOP动态代理来实现。注解和接口实现如下
/** * @author Barudisshu */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface TransactionAnn { String[] name() default {}; boolean[] readonly() default false; int[] level() default Connection.TRANSACTION_READ_COMMITTED; String value() default ""; }
/** * @author Barudisshu */ public interface Transaction { void open(); void rollBack(); void commit(); void closeIfStillOpen(); }
Proxy动态代理
动态代理的机制如下
实现一个代理工厂
/** * 代理工厂类 * @author Barudisshu */ public class AspectFactory { /**单例实现*/ private AspectFactory(){ } @SuppressWarnings("unchecked") public static <T> T getInstance(T target,Aspect... aspects){ AspectHandler handler = new AspectHandler(target, aspects); Class clazz = target.getClass(); return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazz.getInterfaces(), handler); } }
封装并继承调度器
/** * @author Barudisshu */ public class AspectHandler implements InvocationHandler { private Object target = null; private Aspect[] aspects = null; private int index = -1; public AspectHandler(int index, Object target, Aspect[] aspects) { this.index = index; this.target = target; this.aspects = aspects; } public AspectHandler(Object target, Aspect[] aspects) { this.target = target; this.aspects = aspects; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Aspect[] getAspects() { return aspects; } public void setAspects(Aspect... aspects) { this.aspects = aspects; } /** * 委托方法 * * @param proxy 代理对象 * @param method 代理方法 * @param args 方法参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (index == -1) { return new AspectHandler(0, target, aspects).invoke(proxy, method, args); } Object result = null; if (index < aspects.length) { result = aspects[index++].aspect(this, proxy, method, args); } else if (index++ == aspects.length) { result = method.invoke(target, args); } return result; } }
/** * @author Barudisshu */ public interface Aspect { public Object aspect(InvocationHandler ih, Object proxy, Method method, Object[] args) throws Throwable; }
实现真实对象,真实对象继承Aspect,并将通过AspectHandler被委托代理
/** * @author Barudisshu */ public class TransactionAspect implements Aspect { public Object aspect(InvocationHandler ih, Object proxy, Method method, Object[] args) throws Throwable { Object result = null; TransactionAnn transactionAnn = method.getAnnotation(TransactionAnn.class); if (transactionAnn != null) { Transaction transaction = new TransactionAdapter(); // TODO: 获取注解信息或设置值 String[] names = transactionAnn.name(); String values = transactionAnn.value(); int[] level = transactionAnn.level(); boolean[] readOnly = transactionAnn.readonly(); try { //执行操作 result = ih.invoke(proxy, method, args); // TODO: 处理 transaction.open(); transaction.commit(); }catch (Throwable t) { // if exception transaction.rollBack(); Throwable cause = t.getCause(); if (cause != null) { // you should define your own Exception here throw new TransactionException(cause.getMessage(), cause); } else { throw new TransactionException(t.getMessage(), t); } }finally { // finally transaction.closeIfStillOpen(); } } else { //执行操作 result = ih.invoke(proxy, method, args); } return result; } }
根据时序图,我们接下来只需要创建相应的客户端进行调度即可
/** * @author Barudisshu */ public class AspectClient { public static void main(String[] args) { PersistenceService persistenceService = AspectFactory.getInstance(new PersistenceServiceImpl(), new TransactionAspect()); persistenceService.save(3,"321"); } }
/** * @author Barudisshu */ public interface PersistenceService { @TransactionAnn(name = "save") void save(long id,String data); @TransactionAnn(level = Connection.TRANSACTION_READ_UNCOMMITTED) String load(long id); }
/** * @author Barudisshu */ public class PersistenceServiceImpl implements PersistenceService { @Override public void save(long id, String data) { throw new TransactionException("异常则回滚!!!"); } @Override public String load(long id) { return null; } }
在这里,我们显示抛出一个
运行时异常TranscationException,运行客户端将执行transaction.rollBack()方法。
既然有JDK的Proxy代理了,为什么还需要使用CGLIB之类的其他外部库?实际上Proxy只是对代理的简单实现,在实际使用中还不能满足需求,如上述设计中,Proxy的newProxyInstance静态方法是通过clone实现的,可能需要考虑是浅拷贝还是深拷贝的问题。
若以CGLIB实现,则问题会显得简单的多。
CGLIB动态代理
创建单例的代理工厂
public class CGLIBProxyFactory { private CGLIBProxyFactory() { } @SuppressWarnings("unchecked") public static <T> T createProxy(T target, AOPInterceptor interceptor) { MethodInterceptor methodInterceptor = new CGLIBMethodInterceptor(interceptor); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setInterfaces(target.getClass().getInterfaces()); enhancer.setCallback(methodInterceptor); return (T) enhancer.create(); } }
在Cglib则是通过方法拦截器直接实现,只需要继承拦截器即可
/** * @author Barudisshu */ public class CGLIBMethodInterceptor implements MethodInterceptor { private AOPInterceptor interceptor; public CGLIBMethodInterceptor(AOPInterceptor interceptor) { this.interceptor = interceptor; } public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { try { interceptor.before(method, args); Object returnValue = methodProxy.invokeSuper(object, args); interceptor.after(method, args); return returnValue; } catch (Throwable t) { interceptor.afterThrowing(method, args, t); throw t; } finally { interceptor.afterFinally(method, args); } } }
/** * @author Barudisshu */ public interface AOPInterceptor { void before(Method method, Object[] args); void after(Method method, Object[] args); void afterThrowing(Method method, Object[] args, Throwable throwable); void afterFinally(Method method, Object[] args); }
添加事务处理的真实对象
/** * @author Barudisshu */ public class TransactionCglib implements AOPInterceptor { private Transaction transaction; @Override public void before(Method method, Object[] args) { if (isRequiresNew(method)) { transaction = new TransactionAdapter(); transaction.open(); } } @Override public void after(Method method, Object[] args) { if (transaction != null) { transaction.commit(); } } @Override public void afterThrowing(Method method, Object[] args, Throwable throwable) { if (transaction != null) { transaction.rollBack(); } } @Override public void afterFinally(Method method, Object[] args) { if (transaction != null) { transaction.closeIfStillOpen(); transaction = null; } } protected boolean isRequiresNew(Method method) { TransactionAnn transactionAnnotation = method.getAnnotation(TransactionAnn.class); if (transactionAnnotation != null) { if ("REQUIRES_NEW".equals(transactionAnnotation.value())) { return true; } } return false; } }
OK,十分简单,Transaction真实对象将被MethodInterceptor封装,并在Enhancer中实现回调。下面只需要添加相应的客户端调用即可
/** * @author Barudisshu */ public class CglibClient { public static void main(String[] args) { PersistenceService persistenceService = CGLIBProxyFactory.createProxy(new PersistenceServiceImpl(), new TransactionCglib()); persistenceService.save(3L, "321"); } }
注意和Proxy中实现的不同
/** * @author Barudisshu */ public class PersistenceServiceImpl implements PersistenceService { @TransactionAnn("REQUIRES_NEW") public void save(long id, String data) { System.out.println("Generally save ... "); } @TransactionAnn("NOT_SUPPORTED") public String load(long id) { return null; } }
鉴于AspectJ和Spring AOP切面内容比较多,下面简单讲一下AspectJ的配置。
AspectJ部署
- 到eclipse官网下载http://www.eclipse.org/aspectj/downloads.php,直接用7zip解压或双击安装。
- 将aspectj1.x/lib/aspectjrt.jar拷贝到%JAVA_HOME%\jre\lib\ext中,注意这步很重要,否则会说找不到路径。
- 在IDE中设置Ajc编译器路径(编译器用于将aj文件编译成.class文件),以IDEA为例
编译器一定是aspectjtools,不要选错。
OK,差不多了,写个测试例子
/** * @author Barudisshu */ public aspect HelloWorld { pointcut callPointcut(): call(void cn.barudisshu.aop.MyClass.foo(int,java.lang.String)); before():callPointcut(){ System.out.println("Hello World"); System.out.println("In the advice attached to the call pointcut"); } after():callPointcut(){ System.out.println("All things has done"); } }
方法切入点
/** * @author Barudisshu */ public class MyClass { public void foo(int number,String name){ System.out.println("Inside foo (int,String)"); } public static void main(String[] args) { // Create an instance of MyClass MyClass myObject = new MyClass(); // Make the call to foo myObject.foo(1,"Russ Miles"); } }
如下代码将输出
Hello World In the advice attached to the call pointcut Inside foo (int,String) All things has done