实现你自己的AOP 框架

来源:互联网 发布:java属于前端还是后端 编辑:程序博客网 时间:2024/04/29 18:41

  AOP 是一种先进的编程理念,可以用在 日志, 安全,事务处理 方面 ,AOP 是对 OOP 的补充,是一种更通

用和强大的编程思想,它可以使代码更加 简洁, 紧凑,减少重复代码。

   AOP 框架可以分为两类:

  1. Class-weaving-based, such as AspectJ and JBoss AOP. Core and crosscutting concerns are implemented independently. Class weaving is the process of integrating the concern implementations to form the final system. Weaving can be performed at compile, load, and run time. Both AspectJ and JBoss AOP are very powerful AOP implementations. They provide field interception, caller side interception, and constructor interception.
  2. Proxy-based, such as Spring AOP, Nanning, and dynaop. With proxies, method invocations on an object can be intercepted to inject custom code. The aforementioned AOP frameworks use JDK dynamic proxy, CGLIB proxy, or both. Unlike the class-weaving-based ones, proxy-based AOP frameworks are simpler and often focus on method interception. Most of the time, Java developers use method interception only. Some proxy-based AOP implementations, such as Spring AOP, provide close integration with AspectJ to take advantage of its capabilities.

使用 JDK 动态代理机制,JVM 动态生成代理类 ,代理类在运行期间生成一系列特定的接口,对我们的某个类的方法调用,变为对隐藏的代理类的方法调用,代理类使用起来很简单,但执行起来比较慢,而且只能针对接口实现。

当你需要为没有实现接口的类应用代理机制,你可以使用CGLIB,CGLIB是一个高效的强大的代码生成类库,它底层应用的是ASM,一个简单高效的二进制代码操作框架,它更改二进制代码生成新的类。 CGLIB动态生成目标类的子类,覆盖 not final 方法,然后挂接用户的interceptor。

为了进一步理解AOP ,我们应用 动态代理 和 CGLIB 实现一个简单的AOP 框架,这个框架支持声明式事务处理,这里我们应用了Java 5 的新特征,包括annotations , generics,我们先从动态代理开始。

  1 实现一个代理工厂。

  代理工厂是 集中生成 所有目标类的代理类的工厂。客户不需要知道具体的生成机制。

public interface DynamicProxyFactory{
    <T> T createProxy(Class<T> clazz,
        T target,
        AOPInterceptor interceptor);
}

 要实现一个动态代理类,你需要 目标类 和 一组接口,对接口有一些规定,可以参阅动态代理相关文档,这里使用一个简单的接口。

public <T> T createProxy(Class<T> clazz,
    T target, AOPInterceptor interceptor) {
    InvocationHandler handler =
        new DynamicProxyInvocationHandler(target,
            interceptor);

    return (T)Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class<?>[] {clazz},
        handler);
}

代理工厂的实现很简单,首先实现一个InvocationHandler 的实例,然后它调用 static method Proxy.newProxyInstance(),返回一个代理类,这个代理类实现了作为第二个参数传入的接口。注意第二个参数是Class<?> 数组。

对生成的代理类的方法调用被转移到InvocationHandler 的 invoke 方法

public Object invoke(Object proxy,
        Method method, Object[] args)

让我们看一下 这个方法在DynamicProxyInvocationHandler.java 中的实现

public class DynamicProxyInvocationHandler        implements InvocationHandler {    private Object target;    private AOPInterceptor interceptor;    public DynamicProxyInvocationHandler(Object target,        AOPInterceptor interceptor)  {        this.target = target;        this.interceptor = interceptor;    }    public Object invoke(Object proxy, Method method,        Object[] args) throws Throwable{        try {            interceptor.before(method, args);            Object returnValue = method.invoke(target, args);            interceptor.after(method, args);            return returnValue;        } catch(Throwable t) {            interceptor.afterThrowing(method, args, t);            throw t;        } finally {            interceptor.afterFinally(method, args);        }    }}
它使用了Method对象的反射机制,
2。添加 Interceptor
因为invoke 方法代理了所有对我们的类的方法的调用,在它调用我们的类的方法之前或之后,添加相应的
代码,就实现了所谓的AOP机制。我们定义一个AOPInterceptor 接口
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);}
InvocationHandler 的 invoke 方法 ,在调用我们的方法之前,调用AOPInterceptor.before()
之后调用after(),例外抛出时调用afterThrowing(),finally 处理之后调用afterFinally()。
3  AOP Framework in Action
当然我们不希望对所用的方法都实现Interceptor,我们只对我们需要的一部分类做Intercept,
你可以将这些类和方法定义在XML文件或annotations中,在运行期间框架会根据XML 文件动态决定是否
实现Intercept
 这里是一个简单的 TransactionAnnotation
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface TransactionAnnotation {    String value();}
 假设你有一个服务 ,PersistenceService,在save() 中需要一个新的事物,在load()中不用事务
public interface PersistenceService {    @TransactionAnnotation("REQUIRES_NEW")    void save(long id, String data);    @TransactionAnnotation("NOT_SUPPORTED")    String load(long id);}

现在你需要一个事务. 假设你的 transaction API 如下:

public interface Transaction{    void open();    void rollBack();    void commit();    void closeIfStillOpen();}
这是一个用于事务处理的Interceptor
 public class TransactionInterceptor        implements AOPInterceptor {    private Transaction transaction;    public void before(Method method, Object[] args) {        if (isRequiresNew(method)) {            transaction = new TransactionAdapter();            transaction.open();        }    }    public void after(Method method, Object[] args) {        if (transaction != null) {            transaction.commit();        }    }    public void afterThrowing(Method method,        Object[] args, Throwable t) {        if (transaction != null) {            transaction.rollBack();        }    }    public void afterFinally(Method method, Object[] args) {        if (transaction != null) {            transaction.closeIfStillOpen();        transaction = null;        }    }
   protected boolean isRequiresNew(Method method) {        TransactionAnnotation transactionAnnotation =            method.getAnnotation(TransactionAnnotation.class);        if (transactionAnnotation != null) {            if ("REQUIRES_NEW".equals(                transactionAnnotation.value())){            return true;        }        }        return false;    }}
当你创建代理类时,你就可以将TransactionInterceptor intercept 到你自己的类中了。
DynamicProxyFactory proxyFactory = new DynamicProxyFactoryImpl();AOPInterceptor interceptor = new TransactionInterceptor();PersistenceService proxy =    proxyFactory.createProxy(PersistenceService.class,        new PersistenceServiceImpl(),        interceptor);proxy.save(1, "Jason Zhicheng Li");String data = proxy.load(1);

Alternative Implementation

Using CGLIB

Similar to InvocationHandler and Proxy in dynamic proxy, there are two key APIs

in CGLIB proxy, MethodInterceptor and Enhancer. The MethodInterceptor is the

general callback interface used by Enhancer, which dynamically generates

subclasses to override the non-final methods of the superclass. MethodInterceptor

is responsible for intercepting all method calls in the generated proxy. You can

invoke custom code before and after the invocation of the super methods, and even

skip invocation of the super methods. Typically, a single callback is used per

enhanced class, but you can use CallbackFilter to control which callback to use

for a method.Let's first create a CGLIB MethodInterceptor.

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);       }}

The implementation is very similar to DynamicProxyInvocationHandler in dynamic

proxy, but note that there is no target object and the type T is the concrete

class type, not the interface type as in DynamicProxyFactory. The real method

is invoked by using MethodProxy, which is faster, instead of the Method object.

Now let's create the proxy factory:

public class CGLIBProxyFactoryImpl        implements CGLIBProxyFactory {    public <T> T createProxy(Class<T> clazz,            AOPInterceptor interceptor) {        MethodInterceptor methodInterceptor =            new CGLIBMethodInterceptor(interceptor);        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(clazz);        enhancer.setCallback(methodInterceptor);        return (T)enhancer.create();    }}

After you set the superclass type and method interceptor, you simply call the

create() method on the Enhancer object to create a proxy. Optionally, you can

configure CallbackFilter to map a method to a callback by calling the

setCallbackFilter(CallbackFilter) method. In addition, you can specify the proxy

class to implement a set of interfaces. In this CGLIB implementation, since

no interface is specified, the transaction attributes must be declared in the

PersistenceService implementation instead of the interface.

Similarly, you can implement interceptors to address logging, validation,

auditing, caching, and security, which are orthogonal to core business concerns.

As shown above, both dynamic proxy and CGLIB implementation are simple to

implement, but you must be aware that important issues such as performance,

exception handling, and threading are not covered here.

Conclusion

The AOP implementation in this article is simplified for clarity, but it

shows you the essentials of proxy-based AOP frameworks. AOP decouples

crosscutting concerns, such as the transaction management demonstrated in

this article, from application core concerns. With aspect-oriented design and

programming, you can significantly simplify your design and implementation.

In some cases, however, third-party AOP frameworks cannot be used due to

non-technical reasons, such as corporate policies and license issues.

As shown in this article, you can implement your own AOP framework that is

tailored to meet your needs. JDK dynamic-proxy-based implementation is simpler,

since it uses standard Java. That means there are no third-party libraries or

build-time bytecode instrumentation. Alternatively, you can choose CGLIB to

proxy legacy classes and have better performance, but you need to introduce

multiple third-party libraries into your system. At that moment, you should ask

yourself if you need to pick an available AOP framework, which is often more

complete and sophisticated than your roll-your-own AOP implementation.

 
 
 


原创粉丝点击