實現你自己的AOP 框架

  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.

 
 
 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章