這篇是承接《輕量級 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 中一個標準的切面完整代碼: 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 的。
從理論上來講 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 是如何實現這一功能的呢?
在 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 的相關代碼。