设计模式四—"代理模式"(动态代理)

动态代理

动态代理就是通过使用字节码动态生成加载技术,在运行时生成并加载类。

常用的动态代理技术包括JDK动态代理,CGLIB等,其中JDK自带的动态代理使用简单,但是功能较弱,CGLIB是高级的字节码生成库,性能更好,而且功能强大。

先说说JDK动态代理的使用

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

JDK动态代理的使用核心是实现一个用来处理方法调用的接口InvocationHandler,在java.lang.reflect包中

1 public interface InvocationHandler {
2     public Object invoke(Object proxy, Method method, Object[] args)
3         throws Throwable;
4 }

Object proxy指被代理的对象。

Method method指被调用的方法。

Object[] args方法调用传递的参数。

一个实现了InvocationHandler接口的类,就是我们使用的代理类。

Proxy类:Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:

使用的核心方法是该类提供的

 1  public static Object newProxyInstance(ClassLoader loader,
 2                                           Class<?>[] interfaces,
 3                                           InvocationHandler h)
 4         throws IllegalArgumentException
 5     {
 6         if (h == null) {
 7             throw new NullPointerException();
 8         }
 9 
10         /*
11          * Look up or generate the designated proxy class.
12          */
13         Class<?> cl = getProxyClass(loader, interfaces);
14 
15         /*
16          * Invoke its constructor with the designated invocation handler.
17          */
18         try {
19             Constructor cons = cl.getConstructor(constructorParams);
20             return cons.newInstance(new Object[] { h });
21         } catch (NoSuchMethodException e) {
22             throw new InternalError(e.toString());
23         } catch (IllegalAccessException e) {
24             throw new InternalError(e.toString());
25         } catch (InstantiationException e) {
26             throw new InternalError(e.toString());
27         } catch (InvocationTargetException e) {
28             throw new InternalError(e.toString());
29         }
30     }

ClassLoader loader:用来定义代理类的的类加载器

Class<?>[] interfaces:代理类需要实现的一系列接口

InvocationHandler h:实现分发方法调用的的InvocationHandler

这个工厂方法根据需求生成指定的代理类。

实现一个示例,还是一个与静态代理例子的类似的sample

(1)主题接口IDBQuery

1 package com.sample;
2 
3 public interface IDBQuery {
4     public String query();
5     public void insert();
6     public void update();
7     public void delete();
8 }

(2)真实主题类,封装了实际业务逻辑,无聊的模拟了一个权限验证所需要的身份证明“admin”

 1 package com.sample;
 2 
 3 public class DBQuery implements IDBQuery {
 4     private String authority = "admin";
 5     public DBQuery(){
 6         try{
 7             Thread.sleep(10000);//simulate ops creating connections between
 8         }
 9         catch(Exception ie){
10             ie.printStackTrace();
11         }
12     }
13     
14     public String query() {
15         return "this is a record";
16     }
17         public void update() {
18         System.out.println("update a record");
19     }
20 
21     public void insert() {
22         System.out.println("insert a record");
23     }
24 
25     public void delete() {
26         System.out.println("delete a record");
27     }
28 
29     public String getAuthority() {
30         return authority;
31     }
32 
33     public void setAuthority(String authority) {
34         this.authority = authority;
35     }
36 }

(3)替换静态代理中代理类的JDK动态代理,在方法调用时加上身份检测,当然了,还可以根据身份决定方法能否被调用即method.getName()来判断

 1 package com.sample;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 
 7 public class JdkDBProxy implements InvocationHandler {
 8 
 9     private Object targetObject = null;
10 
11     public Object createProxyInstance(Object targetObject) {
12         this.targetObject = targetObject;
13         return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
14                                     this.targetObject.getClass().getInterfaces(),
15                                     this);
16     }
17 
18     public Object invoke(Object proxy, Method method, Object[] args)
19             throws Throwable {
20         DBQuery dbQuery = (DBQuery) this.targetObject;
21         Object result = null;
22         if (dbQuery.getAuthority().equals("admin")) {
23             System.out.println("call method:" + method.toString());
24             result = method.invoke(targetObject, args);
25         }
26         return result;
27     }
28 }

(4)测试使用动态代理

 1 package com.sample;
 2 
 3 public class Test {
 4     public static void main(String[] args) {
 5         JdkDBProxy jdkProxy = new JdkDBProxy();
 6         long begTime = System.currentTimeMillis();
 7         IDBQuery dbQuery = (IDBQuery)jdkProxy.createProxyInstance(new DBQuery());
 8         long endTime = System.currentTimeMillis();
 9         System.out.println("create dynamic proxy cost:" + (endTime-begTime));
10         System.out.println(dbQuery.query());
11         dbQuery.insert();
12         dbQuery.update();
13         dbQuery.delete();
14     }
15 
16 }

执行结果

create dynamic proxy cost:10009
call method:public abstract java.lang.String com.sample.IDBQuery.query()
this is a record
call method:public abstract void com.sample.IDBQuery.insert()
insert a record
call method:public abstract void com.sample.IDBQuery.update()
update a record
call method:public abstract void com.sample.IDBQuery.delete()
delete a record

 

posted @ 2014-03-25 22:08  RichardHu  阅读(159)  评论(0编辑  收藏  举报