《转》java动态代理(JDK和cglib)

该文章转自:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

JAVA的动态代理 
代理模式 
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 
按照代理的创建时期,代理类可以分为两种。 
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
动态代理:在程序运行时,运用反射机制动态创建而成。 

首先看一下静态代理: 
1、Count.java 

 1 package net.battier.dao;  
 2   
 3 /** 
 4  * 定义一个账户接口 
 5  *  
 6  * @author Administrator 
 7  *  
 8  */  
 9 public interface Count {  
10     // 查看账户方法  
11     public void queryCount();  
12   
13     // 修改账户方法  
14     public void updateCount();  
15   
16 }  

2、CountImpl.java 

 1 package net.battier.dao.impl;  
 2   
 3 import net.battier.dao.Count;  
 4   
 5 /** 
 6  * 委托类(包含业务逻辑) 
 7  *  
 8  * @author Administrator 
 9  *  
10  */  
11 public class CountImpl implements Count {  
12   
13     @Override  
14     public void queryCount() {  
15         System.out.println("查看账户方法...");  
16   
17     }  
18   
19     @Override  
20     public void updateCount() {  
21         System.out.println("修改账户方法...");  
22   
23     }  
24   
25 }  
26   
27 、CountProxy.java  
28 package net.battier.dao.impl;  
29   
30 import net.battier.dao.Count;  
31   
32 /** 
33  * 这是一个代理类(增强CountImpl实现类) 
34  *  
35  * @author Administrator 
36  *  
37  */  
38 public class CountProxy implements Count {  
39     private CountImpl countImpl;  
40   
41     /** 
42      * 覆盖默认构造器 
43      *  
44      * @param countImpl 
45      */  
46     public CountProxy(CountImpl countImpl) {  
47         this.countImpl = countImpl;  
48     }  
49   
50     @Override  
51     public void queryCount() {  
52         System.out.println("事务处理之前");  
53         // 调用委托类的方法;  
54         countImpl.queryCount();  
55         System.out.println("事务处理之后");  
56     }  
57   
58     @Override  
59     public void updateCount() {  
60         System.out.println("事务处理之前");  
61         // 调用委托类的方法;  
62         countImpl.updateCount();  
63         System.out.println("事务处理之后");  
64   
65     }  
66   
67 }  

3、TestCount.java 

 1 package net.battier.test;  
 2   
 3 import net.battier.dao.impl.CountImpl;  
 4 import net.battier.dao.impl.CountProxy;  
 5   
 6 /** 
 7  *测试Count类 
 8  *  
 9  * @author Administrator 
10  *  
11  */  
12 public class TestCount {  
13     public static void main(String[] args) {  
14         CountImpl countImpl = new CountImpl();  
15         CountProxy countProxy = new CountProxy(countImpl);  
16         countProxy.updateCount();  
17         countProxy.queryCount();  
18   
19     }  
20 }  

观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。 
再来看一下动态代理: 
JDK动态代理中包含一个类和一个接口: 
InvocationHandler接口: 
public interface InvocationHandler { 
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数 

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。 

Proxy类: 
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法: 
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) 
                               throws IllegalArgumentException 
参数说明: 
ClassLoader loader:类加载器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子类实例 

Ps:类加载器 
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器; 
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的; 
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jrelibext目录中的类; 
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。 

动态代理 
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 

动态代理示例: 
1、BookFacade.java 

1 package net.battier.dao;  
2   
3 public interface BookFacade {  
4     public void addBook();  
5 }  

2、BookFacadeImpl.java 

 1 package net.battier.dao.impl;  
 2   
 3 import net.battier.dao.BookFacade;  
 4   
 5 public class BookFacadeImpl implements BookFacade {  
 6   
 7     @Override  
 8     public void addBook() {  
 9         System.out.println("增加图书方法。。。");  
10     }  
11   
12 }  
13   
14 、BookFacadeProxy.java  
15   
16 package net.battier.proxy;  
17   
18 import java.lang.reflect.InvocationHandler;  
19 import java.lang.reflect.Method;  
20 import java.lang.reflect.Proxy;  
21   
22 /** 
23  * JDK动态代理代理类 
24  *  
25  * @author student 
26  *  
27  */  
28 public class BookFacadeProxy implements InvocationHandler {  
29     private Object target;  
30     /** 
31      * 绑定委托对象并返回一个代理类 
32      * @param target 
33      * @return 
34      */  
35     public Object bind(Object target) {  
36         this.target = target;  
37         //取得代理对象  
38         return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
39                 target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)  
40     }  
41   
42     @Override  
43     /** 
44      * 调用方法 
45      */  
46     public Object invoke(Object proxy, Method method, Object[] args)  
47             throws Throwable {  
48         Object result=null;  
49         System.out.println("事物开始");  
50         //执行方法  
51         result=method.invoke(target, args);  
52         System.out.println("事物结束");  
53         return result;  
54     }  
55   
56 }  

3、TestProxy.java 

 1 package net.battier.test;  
 2   
 3 import net.battier.dao.BookFacade;  
 4 import net.battier.dao.impl.BookFacadeImpl;  
 5 import net.battier.proxy.BookFacadeProxy;  
 6   
 7 public class TestProxy {  
 8   
 9     public static void main(String[] args) {  
10         BookFacadeProxy proxy = new BookFacadeProxy();  
11         BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());  
12         bookProxy.addBook();  
13     }  
14   
15 }  

但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。 

Cglib动态代理 
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 
示例 
1、BookFacadeCglib.java 

1 package net.battier.dao;  
2   
3 public interface BookFacade {  
4     public void addBook();  
5 }  

2、BookCadeImpl1.java 

 1 package net.battier.dao.impl;  
 2   
 3 /** 
 4  * 这个是没有实现接口的实现类 
 5  *  
 6  * @author student 
 7  *  
 8  */  
 9 public class BookFacadeImpl1 {  
10     public void addBook() {  
11         System.out.println("增加图书的普通方法...");  
12     }  
13 }  

3、BookFacadeProxy.java 

 1 package net.battier.proxy;  
 2   
 3 import java.lang.reflect.Method;  
 4   
 5 import net.sf.cglib.proxy.Enhancer;  
 6 import net.sf.cglib.proxy.MethodInterceptor;  
 7 import net.sf.cglib.proxy.MethodProxy;  
 8   
 9 /** 
10  * 使用cglib动态代理 
11  *  
12  * @author student 
13  *  
14  */  
15 public class BookFacadeCglib implements MethodInterceptor {  
16     private Object target;  
17   
18     /** 
19      * 创建代理对象 
20      *  
21      * @param target 
22      * @return 
23      */  
24     public Object getInstance(Object target) {  
25         this.target = target;  
26         Enhancer enhancer = new Enhancer();  
27         enhancer.setSuperclass(this.target.getClass());  
28         // 回调方法  
29         enhancer.setCallback(this);  
30         // 创建代理对象  
31         return enhancer.create();  
32     }  
33   
34     @Override  
35     // 回调方法  
36     public Object intercept(Object obj, Method method, Object[] args,  
37             MethodProxy proxy) throws Throwable {  
38         System.out.println("事物开始");  
39         proxy.invokeSuper(obj, args);  
40         System.out.println("事物结束");  
41         return null;  
42   
43   
44     }  
45   
46 }  

4、TestCglib.java 

 1 package net.battier.test;  
 2   
 3 import net.battier.dao.impl.BookFacadeImpl1;  
 4 import net.battier.proxy.BookFacadeCglib;  
 5   
 6 public class TestCglib {  
 7       
 8     public static void main(String[] args) {  
 9         BookFacadeCglib cglib=new BookFacadeCglib();  
10         BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());  
11         bookCglib.addBook();  
12     }  
13 }  

以上为转载内容,下面是自己写的Test

  1 package com.xt.test;
  2 
  3 import java.lang.reflect.InvocationHandler;
  4 import java.lang.reflect.Method;
  5 import java.lang.reflect.Proxy;
  6 
  7 interface Anim {
  8     public void sound();
  9 
 10     public void name();
 11 }
 12 
 13 class Dog implements Anim {
 14 
 15     @Override
 16     public void sound() {
 17         // TODO Auto-generated method stub
 18         System.out.println("汪汪汪!");
 19     }
 20 
 21     @Override
 22     public void name() {
 23         // TODO Auto-generated method stub
 24         System.out.println("一条小狗!");
 25     }
 26 
 27 }
 28 
 29 class Cat implements Anim {
 30 
 31     @Override
 32     public void sound() {
 33         // TODO Auto-generated method stub
 34         System.out.println("喵喵喵!");
 35     }
 36 
 37     @Override
 38     public void name() {
 39         // TODO Auto-generated method stub
 40         System.out.println("一只小猫!");
 41     }
 42 
 43 }
 44 
 45 interface Car {
 46     public void name();
 47 
 48     public void color();
 49 
 50     public void run();
 51 }
 52 
 53 class Bmw implements Car {
 54 
 55     @Override
 56     public void name() {
 57         // TODO Auto-generated method stub
 58         System.out.println("一辆宝马!");
 59     }
 60 
 61     @Override
 62     public void color() {
 63         // TODO Auto-generated method stub
 64         System.out.println("黑色!");
 65     }
 66 
 67     @Override
 68     public void run() {
 69         // TODO Auto-generated method stub
 70         System.out.println("车子开动!");
 71     }
 72 
 73 }
 74 
 75 class Wm implements Car {
 76 
 77     @Override
 78     public void name() {
 79         // TODO Auto-generated method stub
 80         System.out.println("一辆大众!");
 81     }
 82 
 83     @Override
 84     public void color() {
 85         // TODO Auto-generated method stub
 86         System.out.println("银色!");
 87     }
 88 
 89     @Override
 90     public void run() {
 91         // TODO Auto-generated method stub
 92         System.out.println("车子开动!");
 93     }
 94 
 95 }
 96 
 97 /**
 98  * JDK动态代理代理类
 99  * 
100  * @author student
101  * 
102  */
103 class DynamicProxy implements InvocationHandler {
104     private Object target;
105 
106     /**
107      * 绑定委托对象并返回一个代理类
108      * 
109      * @param target
110      * @return
111      */
112     public Object bind(Object target) {
113         this.target = target;
114         // 取得代理对象
115         Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(),
116                 target.getClass().getInterfaces(), this); // 要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
117         return o;
118     }
119 
120     @Override
121     /** 
122      * 调用方法 
123      */
124     public Object invoke(Object proxy, Method method, Object[] args)
125             throws Throwable {
126         Object result = null;
127         if (target instanceof Anim && method.getName().equals("sound"))
128             System.out.println("准备叫:");
129         if (target instanceof Car && method.getName().equals("run"))
130             System.out.println("车子点火!");
131         // 执行方法
132         result = method.invoke(target, args);
133         if (target instanceof Anim && method.getName().equals("sound"))
134             System.out.println("叫响了");
135         if (target instanceof Car && method.getName().equals("run"))
136             System.out.println("车子停了!");
137         return result;
138     }
139 
140 }
141 
142 public class DynamicProxyTest {
143     public static void main(String[] args) {
144         DynamicProxy proxy = new DynamicProxy();
145         Anim dog = (Anim) proxy.bind(new Dog());
146         dog.name();
147         dog.sound();
148         System.out.println("---------------------");
149         Anim cat = (Anim) proxy.bind(new Cat());
150         cat.name();
151         cat.sound();
152         System.out.println("---------动态的精髓来了,无需再构建代理,不同接口使用相同代理即可------------");
153         // 动态的精髓来了,无需再构建代理,不同接口使用相同代理即可
154         Car bmw = (Car) proxy.bind(new Bmw());
155         bmw.name();
156         bmw.color();
157         bmw.run();
158         System.out.println("---------------------");
159         Car wm = (Car) proxy.bind(new Wm());
160         wm.name();
161         wm.color();
162     }
163 }

 输出结果:

一条小狗!
准备叫:
汪汪汪!
叫响了
---------------------
一只小猫!
准备叫:
喵喵喵!
叫响了
---------动态的精髓来了,无需再构建代理,不同接口使用相同代理即可------------
一辆宝马!
黑色!
车子点火!
车子开动!
车子停了!
---------------------
一辆大众!
银色!

原文地址:https://www.cnblogs.com/wubingshenyin/p/4416096.html