您的位置:首页 > 编程语言

AOP切面编程

2015-07-14 12:27 811 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息