Guice Aop 與 Hasor Aop 原理及其實現

    這篇是承接《輕量級 Java 開發框架 設計》系列Blog文的後續文章,本文主要介紹 Hasor 中 AOP 方面的設計以及實現。 java

    提起 Aop 你們並不陌生,OSC中也不缺少優秀的介紹文章。於是我也不費脣舌去介紹什麼是 AOP 以及AOP 的切面概念。下面就轉入正題。 git

    Hasor 的核心採用的是 Google Guice 也就是說,AOP 核心實現也就是 Guice 提供的。所以本文首先要介紹 Guice 是如何實現 Aop 而後在介紹 Hasor 的 Aop 實現方式。因此什麼 CGLib、ASM、Javassist 都先閃到一遍去把,不必搞一個重複的功能添加進來。 github

    本文將分爲三個部分講解:
1 Guice Aop:介紹使用原生 Guice 方法實現 Aop。
2 Hasor Aop:介紹使用 Hasor 的 Aop 如何使用。
3 講解 Hasor 是如何實現 Aop 以及如何處理 Aop 鏈問題的。
api

Guice Aop

    先看看如何使用原生 Guice 實現 AOP 把,下面是 Guice 中一個標準的切面完整代碼: app

class MyInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("before.");
        Object returnData = invocation.proceed();
        System.out.println("after.");
        return returnData;
    }
}

    建立 Guice 使用下面這個代碼: 框架

class MyModule implements Module {
    public void configure(Binder binder) {
        // TODO Auto-generated method stub
    }
}
public static void main(String[] args) {
    Guice.createInjector(new MyModule());
}

    下面咱們將介紹如何肯定哪些類須要 Aop。首先 Guice 是經過兩個匹配器完成 Aop 是否加裝的判斷。 它們一個是負責匹配類、一個是負責匹配類中的方法。 maven

    使用 Guice 作 Aop 有一個好處就是,你永遠不須要預先準備哪些類須要 Aop。當你經過 Guice 建立類對象時 Guice 會經過匹配器來判斷建立的類型是否知足加裝 Aop,以及加裝哪些 Aop。 源碼分析

    接下來咱們會關心匹配器的問題,下面這段代碼中 「Matchers.inSubpackage("org.more.test")」 表示匹配「org.more.test.guice」 包下的全部類。而 「Matchers.any()」 部分的含義是匹配全部方法。 網站

public class AopTest {
    static class MyInterceptor implements MethodInterceptor {
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("before.");
            Object returnData = invocation.proceed();
            System.out.println("after.");
            return returnData;
        }
    }
    static class MyModule implements Module {
        public void configure(Binder binder) {
            Matcher<Class> m = Matchers.inSubpackage("org.more.test");
            binder.bindInterceptor(m, Matchers.any(), new MyInterceptor());
        }
    }
    //
    public void foo() {
        System.out.println("Hello Word.");
    }
    //
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new MyModule());
        AopTest obj = injector.getInstance(AopTest.class);
        obj.foo();
    }
}

    若是你對「Matchers.inSubpackage("org.more.test")」這種方式感到不滿,能夠經過實現 Matcher 接口制定適合本身的篩選器。Hasor 就是經過制定篩選器達到目的的。 ui

    好了上面對 Guice 自己如何實現 Aop 作了一個簡短的介紹,接下來將介紹 Hasor 中如何實現 Aop 的。

Hasor Aop

    從理論上來講 Aop 被分爲三個切面(調用前、調用後、異常發生)。同時當配置多個 Aop 時還會涉及到 「鏈」 的問題。而這就像是同心圓環,最內層的是目標方法。要想調用到目標方法須要逐一通過其外面的 Aop切面程序。

    在實現 Aop 時候有兩條路能夠選擇:

    1、是制定一個相似 Filter 的接口 Api 使調用鏈式結構的 Aop代理變得像是在操做過濾器。
    2、是聲明定義三個接口專門用於通知切面程序三個切點事件,在攔截器中切點位置執行切面方法調用。

    前一種方式可能會面臨設計結構的問題,這種方式能夠在「Aop過濾器」鏈中控制是否要繼續向下執行。然後一種雖然結構很是清晰,可是切面程序也失去了對 Aop 鏈控制。

    從開發角度來看 「過濾器」 也能夠獲得(調用前、調用後、異常發生)這三個事件點。所以 Hasor 放棄了第二種實現方式。那麼先看一下使用 Hasor Api 如何開發 Aop 程序:

public class SimpleAop_Test {
    public static class MyInterceptor implements MethodInterceptor {
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("before.");
            Object returnData = invocation.proceed();
            System.out.println("after.");
            return returnData;
        }
    }
    //
    @Aop(MyInterceptor.class)
    public void foo() {
        System.out.println("Hello Word.");
    }
    //
    public static void main(String[] args) throws IOException {
        AppContext appContext = new AnnoStandardAppContext();
        appContext.start();
        //
        SimpleAop_Test obj = appContext.getInstance(SimpleAop_Test.class);
        obj.foo();
    }
}

    從代碼比較上來看,除了使用 @Aop 註解並無什麼太大的改進。不過在這裏我要先給你們說明一下,Guice 是一款 IoC/Aop 框架。切面程序按理來講一旦被容器管理也應當能夠被依賴注入。不過您能夠留心觀察一下,在 Guice 的原生實現方式中攔截器是被 new 出來的,經過查看 Guice API也不難發現註冊攔截器也只有這一種方式。

    使用 Hasor 的 Aop 除了簡單的經過 @Aop 註解就能夠實現以外,攔截器類還能夠被依賴注入。這樣一來 Guice 在 Aop 開發方面就變得更加友好了。

    @Aop 註解是能夠被標記到(方法 或 類)上,經過標記位置的不一樣決定了 Aop 做用範圍。固然您能夠混合使用例如:

@Aop(MyInterceptor.class)
public class SimpleAop_Test {
    public void foo1() {
        System.out.println("Hello Word.");
    }
    @Aop(MyInterceptor2.class)
    public void foo2() {
        System.out.println("Hello Word.");
    }
}

    或許有的時候還須要全局 Aop 配置,@Aop 並不支持全局配置。所以須要藉助 Hasor 的插件來實現這一目標:

@Plugin
public class GlobalAopInterceptor implements HasorPlugin {
    public void loadPlugin(ApiBinder apiBinder) {
        Matcher matcher = ......;
        apiBinder.getGuiceBinder().bindInterceptor(//
                matcher, Matchers.any(), new MyInterceptor());
    }
}

Hasor Aop 實現源碼分析

    那麼 Hasor 是如何實現這一功能的呢?

    在 Hasor 中 @Aop 並非 Hasor 核心直接提供的功能。它是由一個 Hasor 插件提供的,這個插件僅有幾個類組成。它們位於:「net.hasor.plugins.aop」 包下。

    接下來讓咱們咱們先看看插件入口程序:

@Plugin
public class AopPlugin extends AbstractHasorPlugin {
    public void loadPlugin(ApiBinder apiBinder) {
        Matcher<Object> matcher = AopMatchers.annotatedWith(Aop.class);//
        apiBinder.getGuiceBinder().bindInterceptor(matcher, matcher, new AopInterceptor());
    }
}

    做爲 Aop 實現的入口咱們看到,Aop 插件僅僅是向 Guice 註冊了一個攔截器。這個攔截器負責攔截全部標記了 @Aop 註解的類或方法。下面是相關匹配器的代碼:

/**
 * 負責檢測匹配。規則:只要類型或方法上標記了某個註解。
 * @version : 2013-11-22
 * @author 趙永春(zyc@hasor.net)
 */
class MatcherAnnotationType extends AbstractMatcher<Object> {
    private Class<? extends Annotation> annotationType = null;
    public MatcherAnnotationType(Class<? extends Annotation> annotationType) {
        this.annotationType = annotationType;
    }
    public boolean matches(Object type) {
        if (type instanceof Class<?>)
            return this.matches((Class<?>) type);
        if (type instanceof Method)
            return this.matches((Method) type);
        return false;
    }
    public boolean matches(Class<?> matcherType) {
        if (matcherType.isAnnotationPresent(this.annotationType) == true)
            return true;
        Method[] m1s = matcherType.getMethods();
        Method[] m2s = matcherType.getDeclaredMethods();
        for (Method m1 : m1s) {
            if (m1.isAnnotationPresent(this.annotationType) == true)
                return true;
        }
        for (Method m2 : m2s) {
            if (m2.isAnnotationPresent(this.annotationType) == true)
                return true;
        }
        return false;
    }
    public boolean matches(Method matcherType) {
        if (matcherType.isAnnotationPresent(this.annotationType) == true)
            return true;
        if (matcherType.getDeclaringClass().isAnnotationPresent(this.annotationType) == true)
            return true;
        return false;
    }
}

    有了這個匹配器,只要調用帶有 @Aop 標記的類或方法。都會進入咱們的攔截器 AopInterceptor,下面是攔截器代碼(爲了縮短代碼長度,下面的代碼中去掉了部分泛型聲明):

class AopInterceptor implements MethodInterceptor, AppContextAware {
    private AppContext appContext           = null;
    private Map        methodInterceptorMap = new HashMap();
    //
    public AopInterceptor() {
        AwareUtil.registerAppContextAware(this);
    }
    //
    public void setAppContext(AppContext appContext) {
        this.appContext = appContext;
    }
    //
    public Object invoke(MethodInvocation invocation) throws Throwable {
       Method targetMethod = invocation.getMethod();
       List<Class> list = this.methodInterceptorMap.get(targetMethod);
       //1.取得攔截器
       if (list == null) {
         list = new ArrayList();
         Aop beforeAnno = targetMethod.getDeclaringClass().getAnnotation(Aop.class);
         if (beforeAnno != null) {
             for (Class interType : beforeAnno.value())
                 if (interType != null)
                     list.add(interType);
         }
         beforeAnno = targetMethod.getAnnotation(Aop.class);
         if (beforeAnno != null) {
             for (Class interType : beforeAnno.value())
                 if (interType != null)
                     list.add(interType);
         }
         this.methodInterceptorMap.put(targetMethod, list);
        }
        //2.建立對象
        return new AopChainInvocation(appContext, list, invocation).invoke(invocation);
    }
}

    上面這段代碼中,有關「AppContextAware」部分的內容稍後介紹。首先咱們假設 AppContext 已經存在。當攔截器攔截到符合 @Aop 的方法調用以後。這個攔截器會取得調用方法的 Method 對象。

    接下來攔截器會嘗試從 Method 對象中獲取 @Aop 註解中配置的攔截器信息。

    固然,這裏考慮到了攔截器鏈的問題,所以會有一個 List 對象用於收集這個方法調用都配置了哪些真正的Aop 攔截器。

    最後利用收集到的信息構造一個「AopChainInvocation」 對象來處理調用過濾器鏈,下面是源碼:

class AopChainInvocation implements MethodInvocation {
    private MethodInterceptor[] beforeInterceptor = null;
    private MethodInvocation    invocation        = null;
    private int                 index             = -1;
    //
    public AopChainInvocation(AppContext appContext, List<Class> interTypeList, MethodInvocation invocation) {
        List<MethodInterceptor> beforeList = new ArrayList<MethodInterceptor>();
        for (Class<? extends MethodInterceptor> interType : interTypeList) {
            if (interType != null)
                beforeList.add(appContext.getInstance(interType));
        }
        this.beforeInterceptor = beforeList.toArray(new MethodInterceptor[beforeList.size()]);
        this.invocation = invocation;
    }
    public Object invoke(MethodInvocation invocation) throws Throwable {
        index++;
        if (index < beforeInterceptor.length) {
            return beforeInterceptor[index].invoke(this);
        } else {
            return invocation.proceed();
        }
    }
    //-----------------------------------------------------------
    public Object[] getArguments() {
        return invocation.getArguments();
    }
    public Object proceed() throws Throwable {
        return this.invoke(this.invocation);
    }
    public Object getThis() {
        return invocation.getThis();
    }
    public AccessibleObject getStaticPart() {
        return invocation.getStaticPart();
    }
    public Method getMethod() {
        return invocation.getMethod();
    }
}

    AppContextAware接口是由「net.hasor.plugins.aware」插件提供的。這個插件功能是給予那些不方便獲經過注入方式獲取 AppContext 接口對象的類。在 AppContext 啓動的第一時間給予它們注入。

    以上就是 Hasor 中有關 Aop 方面的完整說明。

----------------------------------------------------------------
目前的開發代碼存放於(包括Demo程序)
    Github:    https://github.com/zycgit/hasor
    git@OSC: http://git.oschina.net/zycgit/hasor

很是感謝您百忙之中抽出時間來看這一系博文。能夠經過Maven 中央倉庫網站  http://search.maven.org/ 搜索 Hasor 下載 hasor 的相關代碼。

相關文章
相關標籤/搜索