SHIHUC

好记性不如烂笔头,还可以分享给别人看看! 专注基础算法,互联网架构,人工智能领域的技术实现和应用。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

动态代理到基于动态代理的AOP

Posted on 2016-03-18 09:46  shihuc  阅读(466)  评论(0)    收藏  举报

动态代理,是java支持的一种程序设计方法。

动态代理实现中有两个重要的接口和类,分别是InvocationHandler(interface),Proxy(class).

要实现动态代理,必须要定义一个被代理类的接口Interface,另外,被代理类必须实现这个接口,这样,在代理类生存过程中,就可以通过接口,反射的方式找到这个被代理类。这个和代理模式中要求基本一致:代理类和被代理类都必须实现同一个接口。只是这里,有点点不同,只是要求被代理类必须定义一个接口,而代理类依据被代理类接口通过反射的机制找到这个被代理类的实现。有点绕,静下心理一理,还是比较容易弄清的!

 

下面就用一个动态代理的例子来呈现其原理吧。

1.首先是被代理类的接口定义。

 1 package dynamic_proxy;
 2 
 3 /**
 4  * 动态代理,必须有一个公共的接口,这个也是代理模式的基本要求
 5  * 
 6  * @author  shihuc
 7  * @date    Mar 17, 2016
 8  *
 9  */
10 public interface Happy {
11     
12     public void sayHello();
13     
14     public void makeFriends(String guNiang);
15 }

 

2.被代理类的具体实现

 1 package dynamic_proxy;
 2 /**
 3  * 
 4  * 被代理的类,必须实现公共的接口,要有自己的特色活要干,比如这里,西门庆后面要被王婆代理,
 5  * 但是西门庆最终还是要上阵干活,泡潘金莲啊。只是外表上,让人看起来是王婆在作媒人。。。
 6  * 
 7  * @author  shihuc
 8  * @date    Mar 17, 2016
 9  *
10  */
11 
12 public class Ximenqing implements Happy{
13 
14     public void sayHello() {
15         System.out.println("Hi, I am Ximenqing");        
16     }
17 
18     public void makeFriends(String guNiang) {
19         System.out.println("Hello, " + guNiang + ", I am Ximenqing!");        
20     }
21 
22 }

 

3. 代理类的实现。这里很重要,代理类必须实现InvocationHandler这个Interface,并且实现里面的invoke函数。

 1 package dynamic_proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 /**
 7  * 
 8  * 王婆开始代理西门庆上场了,注意,这里只是表面上看是王婆代理,但是,王婆其实只是牵线搭桥,比如,
 9  * 将Happy这个接口的实例ximenqing传进代理,后面干活的,还是这个西门庆哟!
10  * 
11  * @author  shihuc
12  * @date    Mar 17, 2016
13  *
14  */
15 public class PaoniuHandler implements InvocationHandler{
16 
17     Happy happy;
18     
19     public PaoniuHandler(Happy happy){
20         this.happy = happy;
21     }
22     
23     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
24         
25         //在这里,你可以加入真实函数执行前的辅助逻辑。
26         System.out.println("Here, ‘BEFORE’ you can add your supplemental logic");
27         
28         //反射的机制实现对真是的被代理的函数进行执行操作
29         method.invoke(happy, args);
30         
31         //在这里,你可以加入真是逻辑执行完成后的一些处理逻辑
32         System.out.println("Here, 'AFTER' you can add some logic for you completed real logic");
33         
34         return null;
35     }
36 }

 

4. 下面用一个小测试程序,检测一下上面的动态代理的执行过程。

 1 package dynamic_proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Proxy;
 5 
 6 /**
 7  * 动态代理的具体运行步骤。重点是通过Proxy类来生成代理类的实例wangpo。这里的生成过程,和代理模式不同,这里是依据Proxy类的类函数newProxyInstance得到的。
 8  * static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
 9  * JDK1.6对上面这个函数的解释:“返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。”
10  * 
11  * @author  shihuc
12  * @date    Mar 18, 2016
13  *
14  */
15 public class Client {
16 
17     public static void main(String args[]){
18 
19         //真正干活的人,西门庆的诞生
20         Happy ximenqing = new Ximenqing();
21         
22         //创建代理类处理程序,在这里,需要传递进被代理者,也就是泡妞的主体西门庆。
23         InvocationHandler xmqHandler = new PaoniuHandler(ximenqing);
24         
25         /*代理者wangpo出场的核心程序。
26          * Proxy类的newProxyInstance通过用户指定的类加载器xmqHandler.getClass().getClassLoader(),
27          * 代理类要实现的接口列表ximenqing.getClass().getInterfaces(),
28          * 代理类指派方法调用的调用处理程序(也就是说,代理类基于Happy接口生成代理类,这个代理类要做的事情是InvocationHandler实现者里指定被代理者做的事情。
29          * 说白了,就是王婆帮西门庆出去搭讪潘金莲了,但是核心婆妞的事情还是西门庆上。)
30          */
31         Happy wangpo = (Happy)Proxy.newProxyInstance(xmqHandler.getClass().getClassLoader(), ximenqing.getClass().getInterfaces(), xmqHandler);
32         
33         //王婆搭讪潘金莲了。其实后手是西门庆。
34         wangpo.makeFriends("Ms Pan Jinlian");
35         
36     }
37 }

下面看看执行的输出:

1 Here, ‘BEFORE’ you can add your supplemental logic
2 Hello, Ms Pan Jinlian, I am Ximenqing!
3 Here, 'AFTER' you can add some logic for you completed real logic

 

到这里,动态代理的实现全部完成。是不是没有想象的那么复杂,关于核心InvocationHandler以及Proxy的newProxyInstance的具体实现,可以google,很多的!

 

接下来,看看如何基于动态代理,实现基于动态代理的AOP。其实改动的地方很小,下面给一个简单的例子,基于上面的例子做了点改动。

 1 package dyn_pxy_aop;
 2 
 3 /**
 4  * 动态代理,必须有一个公共的接口,这个也是代理模式的基本要求
 5  * 
 6  * @author  shihuc
 7  * @date    Mar 17, 2016
 8  *
 9  */
10 public interface Happy {
11     
12     public void sayHello();
13     
14     public void makeFriends(String guNiang);
15 }
 1 package dyn_pxy_aop;
 2 /**
 3  * 
 4  * 被代理的类,必须实现公共的接口,要有自己的特色活要干,比如这里,西门庆后面要被王婆代理,
 5  * 但是西门庆最终还是要上阵干活,泡潘金莲啊。只是外表上,让人看起来是王婆在作媒人。。。
 6  * 
 7  * @author  shihuc
 8  * @date    Mar 17, 2016
 9  *
10  */
11 
12 public class Ximenqing implements Happy{
13 
14     public void sayHello() {
15         System.out.println("Hi, I am Ximenqing");        
16     }
17 
18     public void makeFriends(String guNiang) {
19         System.out.println("Hello, " + guNiang + ", I am Ximenqing!");        
20     }
21 
22 }
 1 package dyn_pxy_aop;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 /**
 7  * 
 8  * 王婆开始代理西门庆上场了,注意,这里只是表面上看是王婆代理,但是,王婆其实只是牵线搭桥,比如,
 9  * 将Happy这个接口的实例ximenqing传进代理,后面干活的,还是这个西门庆哟!
10  * 
11  * @author  shihuc
12  * @date    Mar 17, 2016
13  *
14  */
15 public class PaoniuHandler implements InvocationHandler{
16 
17     Happy happy;
18     
19     public PaoniuHandler(Happy happy){
20         this.happy = happy;
21     }
22     
23     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        
24         
25         //反射的机制实现对真是的被代理的函数进行执行操作
26         method.invoke(happy, args);
27                 
28         return null;
29     }
30 }
 1 package dyn_pxy_aop;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 /**
 7  * 王婆的代理,其产生的机制,就非常的明显了,基于反射获取Happy的方法,然后直接利用InvocationHandler的invoke方法进行方法调用,
 8  * 更进一步的显示出真正干活的是谁了(西门庆)
 9  * 
10  * @author  shihuc
11  * @date    Mar 18, 2016
12  *
13  */
14 public class WangpoProxy implements Happy{
15 
16     InvocationHandler paoniuHandler;
17     
18     public WangpoProxy(InvocationHandler h){
19         this.paoniuHandler = h;
20     }
21     
22     public void sayHello() {
23         try {
24             Method m = ((PaoniuHandler)paoniuHandler).happy.getClass().getMethod("sayHello", null);
25             paoniuHandler.invoke(this, m, null);
26         } catch (NoSuchMethodException e) {
27             // TODO Auto-generated catch block
28             e.printStackTrace();
29         } catch (SecurityException e) {
30             // TODO Auto-generated catch block
31             e.printStackTrace();
32         } catch (Throwable e) {
33             // TODO Auto-generated catch block
34             e.printStackTrace();
35         }
36     }
37 
38     public void makeFriends(String guNiang) {
39         try {
40             Method m = ((PaoniuHandler)paoniuHandler).happy.getClass().getMethod("makeFriends", new Class[]{String.class});            
41             paoniuHandler.invoke(this, m, new Object[]{guNiang});
42         } catch (NoSuchMethodException e) {
43             // TODO Auto-generated catch block
44             e.printStackTrace();
45         } catch (SecurityException e) {
46             // TODO Auto-generated catch block
47             e.printStackTrace();
48         } catch (Throwable e) {
49             // TODO Auto-generated catch block
50             e.printStackTrace();
51         }
52     }
53 }
 1 package dyn_pxy_aop;
 2 
 3 /**
 4  * 所谓的AOP,最终的目的就是在不修改原有的业务代码的基础上,在原有逻辑的前(before),后(after),以及前和后(around)的位置,添加其他的功能(advice),比如日志,事物,安全等
 5  * 
 6  * @author  shihuc
 7  * @date    Mar 18, 2016
 8  *
 9  */
10 public class AOP_demo {
11     
12     public static void main(String[] args) {
13         
14         PaoniuHandler ph = new PaoniuHandler(new Ximenqing());
15         //这里可以加入advice
16         new WangpoProxy(ph).sayHello();        
17         new WangpoProxy(ph).makeFriends("Pan Jinlian");
18         //这里可以加入advice
19     }
20 
21 }

 

基于动态代理实现的AOP,其实有个很明显的不足,就是过于依赖反射,这就必然影响性能。但是它也有个明显的优势,就是实现起来灵活。

AOP的实现,除了基于动态代理的方案,还有动态字节码生成方案,静态AOP等等,可以慢慢研究,根据需要进行取舍!