沙漠中的魚

          欲上天堂,先下地獄
          posts - 0, comments - 56, trackbacks - 0, articles - 119
            BlogJava :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理
          AOP:Aspect Oriented Programming,意為面向切面/局部的程序設(shè)計。它是面向?qū)ο蟮某绦蛟O(shè)計的一種延伸。

          本文試圖通過使用Java本身的動態(tài)屬性,來實現(xiàn)一個具有簡單的AOP功能的容器。

          開始理解起來可能比較費勁,但我們可以通過一個通俗說明來這樣理解AOP的概念,就是使用AOP可以不用修改原有的代碼,而可以追加新的功能。

          比如,我們用AOP實現(xiàn)了用戶登陸(判斷ID與密碼是否正確)功能,現(xiàn)在我們要求在用戶登陸時用LOG記錄用戶登陸的情況。一般的做法是直接修改已有的登陸邏輯代碼,但使用AOP,可以不用修改原有的代碼而完成此功能。


          本文試圖通過使用Java本身的動態(tài)代理功能,來實現(xiàn)一個具有簡單的AOP功能的容器。從而幫助大家對AOP有個大概的認(rèn)識。

          Java動態(tài)代理功能
          首先,我們簡單介紹一下Java動態(tài)代理功能。JAVA的動態(tài)代理功能主要通過java.lang.reflect.Proxy類與java.lang.reflect.InvocationHandler接口完成,這里正是通過它們實現(xiàn)一個簡單的AOP容器的。其實,像JBoss AOP等其他動態(tài)AOP框架也都是通過Proxy和InvocationHandler來實現(xiàn)的。


          • Java從JDK1.3開始提供動態(tài)代理(Java Proxy)功能。
          • 所謂動態(tài)代理,即通過代理類:Proxy的代理,接口和實現(xiàn)類之間可以不直接發(fā)生聯(lián)系,而可以在運行期(Runtime)實現(xiàn)動態(tài)關(guān)聯(lián)。
          • AOP(Aspect Oriented Programming):面向切面編程,其中的一種實現(xiàn)方法便是用Proxy來實現(xiàn)的。
          • Java Proxy只能代理接口,不能代理類。
          • Java Proxy功能主要通過java.lang.reflect.Proxy類與java.lang.reflect.InvocationHandler接口實現(xiàn)。
          • java.lang.reflect.Proxy (代理類) > ProxyInterface(被代理的接口)   > InvocationHandler(關(guān)聯(lián)類)> Class(可以在InvocationHandler中被調(diào)用)。它們之間的關(guān)系可以用下面的流程圖來表示:


          動態(tài)代理類:Proxy 被代理的接口 InvocationHandler實現(xiàn)類 代理類

               實際上的調(diào)用關(guān)系可以用下面的流程圖來表示:

          Proxy.newProxyInstance

          XxxxInterface xx = $ProxyN(N=0,1,2…)

          XxxxInterface.calledMethod

          $ProxyN.calledMethod

          InvocationHandler.invoke

          method.invoke(obj, args)

          obj.calledMethod
          1. Proxy. newProxyInstance的參數(shù):必須傳送以下3個參數(shù)給Proxy. newProxyInstance方法:ClassLoader,Class[],InvocationHandler。其中參數(shù)2為被代理的接口 Class,參數(shù)3為實現(xiàn)InvocationHandler接口的實例。
          2. 可以通過Proxy. newProxyInstance方法得到被代理的接口的一個實例(instance),該實例由newProxyInstance方法動態(tài)生成,并實現(xiàn)了該接口。
          3. 當(dāng)程序顯示調(diào)用接口的方法時,其時是調(diào)用該實例的方法,此方法又會調(diào)用與該實例相關(guān)聯(lián)InvocationHandler的invoke方法。
          4. 這樣我們可以在InvocationHandler.invoke方法里調(diào)用某些處理邏輯或真正的邏輯處理實現(xiàn)類。



          用Java Proxy實現(xiàn)AOP容器
          下面我們使用Java Proxy來實現(xiàn)一個簡單的AOP容器。
          文件列表:
          文件名 說明
          AopInvocationHandlerImpl.java 該類實現(xiàn)了java.lang.reflect.InvocationHandler接口,我們通過它記錄LOG信息
          AopContainer.java 簡單的AOP容器,通過它把IDoBusiness與AopInvocationHandlerImpl關(guān)聯(lián)起來
          IDoBusiness.java 邏輯處理接口
          DoBusiness.java 邏輯處理實現(xiàn)類
          TestAop.java 測試類

          簡單的AOP容器:
          AopContainer.java
          package com.test.aop.framework;

          import java.lang.reflect.Proxy;

          /**
          * A Simple AOP Container
          *
          */

          public class AopContainer {
              public static <T> T getBean(Class<T> interfaceClazz, final T obj) {
                   assert interfaceClazz.isInterface();
                  
                  return (T) Proxy.newProxyInstance(interfaceClazz.getClassLoader(),
                          new Class[] { interfaceClazz }, new AopInvocationHandlerImpl(obj));
               }

              public static Object getBean(final Object obj) {
                  return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                           obj.getClass().getInterfaces(), new AopInvocationHandlerImpl(obj));
               }
          }
          第一個getBean方法通過2個參數(shù)(第一個參數(shù)為被代理的接口,第二個參數(shù)為被代理的類實例)
          第二個getBean方法只有一個參數(shù),就是類的實例。該類必須實現(xiàn)1個以上的接口。本文的例子并沒有使用到該方法,所以這里順便介紹一下它的使用方法。比如有一個類HelloWorld實現(xiàn)了接口IHelloWorld1和IHelloWorld2,那么可以通過
          HelloWorld helloWorld = new HelloWorld();
          IHelloWorld1 helloWorld1 = (IHelloWorld1)AopContainer.getBean(helloWorld);
          //或
          IHelloWorld2 helloWorld2 = (IHelloWorld2)AopContainer.getBean(helloWorld);
          調(diào)用。當(dāng)然很多時候都不會直接用new HelloWorld()生成HelloWorld實例,這里為了簡便,就直接用new生成HelloWorld實例了。

          實現(xiàn)InvocationHandler接口的中間類:
          AopInvocationHandlerImpl.java
          package com.test.aop.framework;

          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;

          public class AopInvocationHandlerImpl implements InvocationHandler {
              private Object bizPojo;
              
              public AopInvocationHandlerImpl(Object bizPojo) {
                   this.bizPojo = bizPojo;
               }

              /*
               * (non - Javadoc)
               *
               * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
               *       java.lang.reflect.Method, java.lang.Object[])
               */

              public Object invoke(Object proxy, Method method, Object[] args)
                      throws Throwable {
                   Object o = null;
                  try {
                       System.out.println("Start:" + method.getName());
                       o = method.invoke(bizPojo, args);
                       System.out.println("End:" + method.getName());
                   } catch (Exception e) {
                       e.printStackTrace();
                       System.out.println("Exception Occured!" + e.getMessage());
                      // excetpion handling.
                   }
                  return o;
               }
          }
          AopInvocationHandlerImpl.invoke方法的第一個參數(shù)為代理類,在我們這個例子里為java.lang.reflect.Proxy類的一個實例。第二個參數(shù)Method,為被代理的接口的方法調(diào)用(實際上是自動生成代理類的方法調(diào)用),第三個方法為方法調(diào)用的參數(shù)。
          我們通過在AopInvocationHandlerImpl.invoke方法里的method.invoke(bizPojo, args)來調(diào)用bizPojo類的與被代理接口的同名方法。這里,bizPojo必須實現(xiàn)了被代理的接口。
          在我們的例子里,我們在實際上被調(diào)用的業(yè)務(wù)邏輯方法的前后輸出了日志信息。

          實際上的邏輯處理類。該類實現(xiàn)了被代理的接口:IDoBusiness。
          DoBusiness.java
          package com.test.aop.framework;

          /**
          * A business class
          *
          */

          public class DoBusiness implements IDoBusiness {
              public void printNothing() {
                   System.out.println("Just Say Hello!");
               }
              
              public void throwException() {
                  throw new RuntimeException("throw Exception from DoBusiness.throwException()");
               }

          }

          被代理的接口定義:
          IDoBusiness.java
          package com.test.aop.framework;

          /**
          * interface for business logic process
          *
          */

          public interface IDoBusiness {
              public void printNothing();
              public void throwException();
          }

          測試類:
          TestAop.java
          package com.test.aop.framework;


          /**
          * Test AOP
          *
          */

          public class TestAop {

              /**
               * @param args
               */

              public static void main(String[] args) {
                   DoBusiness doBusiness = new DoBusiness();
                   IDoBusiness idoBusiness = AopContainer.getBean(IDoBusiness.class, doBusiness);
                   idoBusiness.printNothing();
                  
                   idoBusiness.throwException();
               }

          }

          總結(jié):
          本文通過Java Proxy實現(xiàn)了一個簡單地AOP容器。也簡單地展示了AOP的基本實現(xiàn)原理,實際上可以以此為基礎(chǔ)實現(xiàn)一個功能完善的AOP容器。

           

          轉(zhuǎn)載:http://hi.baidu.com/e9151/blog/item/9c8d772be0319d305243c130.html

          主站蜘蛛池模板: 融水| 青浦区| 太和县| 平远县| 双牌县| 呼和浩特市| 牡丹江市| 绥化市| 东明县| 富平县| 宁安市| 定州市| 句容市| 汕头市| 汕尾市| 封开县| 师宗县| 云阳县| 灌阳县| 三亚市| 修武县| 华安县| 锡林浩特市| 汤原县| 巴林左旗| 安达市| 顺义区| 潮州市| 临朐县| 泊头市| 保山市| 桦川县| 镇远县| 赣州市| 临夏县| 湖南省| 鹤庆县| 临潭县| 兴隆县| 宁城县| 通化县|