`

动态代理的使用以及其实现机制

阅读更多

一、动态代理的使用

  动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实。代理一般会实现它所表示的实际对象的接口。代理可以访问实际对象,但是延迟实现实际对象的部分功能,实际对象实现系统的实际功能,代理对象对客户隐藏了实际对象。客户不知道它是与代理打交道还是与实际对象打交道。

  动态代理主要包含以下角色:

  动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。

  代理接口 是代理类实现的一个接口。

  代理实例 是代理类的一个实例。

  每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

  

  目前Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。 Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现

  下面看一个例子:

  1.代理接口:IComputer.java

1 package com.proxy;
2 
3 public interface IComputer {
4     void execute();
5 }

  2.被代理对象:Laptop.java

复制代码
 1 package com.proxy;
 2 
 3 //笔记本电脑
 4 public class Laptop implements IComputer {
 5 
 6     public void execute() {
 7         System.out.println("电脑正在执行中......");
 8     }
 9 
10 }
复制代码

  3.调用处理类:TimeHander.java

复制代码
 1 package com.proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 public class TimeHander implements InvocationHandler {
 7     private Object object;
 8     public TimeHander(Object object) {
 9         this.object = object;
10     }
11     public Object invoke(Object proxy, Method method, Object[] args)
12             throws Throwable {
13         long start = System.currentTimeMillis();
14         System.out.println("start:"+start);
15         method.invoke(object, args);
16         Thread.sleep((int)(Math.random()*2000));
17         long end = System.currentTimeMillis();
18         System.out.println("end:"+end);
19         System.out.println("total:"+(end-start));
20         return null;
21     }
22 
23 }
复制代码

  4.测试程序:

复制代码
 1 package com.proxy;
 2 
 3 import java.lang.reflect.Proxy;
 4 
 5 public class ProxyTest {
 6 
 7     public static void main(String[] args) {
 8         Laptop laptop = new Laptop();
 9         TimeHander hander = new TimeHander(laptop);
10         IComputer computer = (IComputer)Proxy.newProxyInstance(laptop.getClass().getClassLoader(), laptop.getClass().getInterfaces(), hander);
11         computer.execute();
12     }
13 
14 }
复制代码


程序运行结果:

start:1369118281186
电脑正在执行中......
end:1369118282849
total:1663

 

  二、动态代理运行机制

  Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 方法会返回一个代理对象类的实例。如上面例子中IComputer computer = (IComputer)Proxy.newProxyInstance(laptop.getClass().getClassLoader(), laptop.getClass().getInterfaces(), hander);执行这一句话的时候会通过反射机制动态的生成一个代理类,该类实现了IComputer接口,并且重写了接口里面的方法(也就是说代理类与被代理类有相同的接口),在该代理类里面有一个InvocationHandler类型的成员变量,也就是调用处理程序,通过调用处理程序来给被代理类增强功能。创建好代理类后就调用类加载器将该类加载到类存,然后再通过反射创建一个该代理类的实例对象。

  为了能够更加的理解动态代理的运行机制,我自己来实现了一个动态代理:

  1.代理接口:Moveable.java

1 package com.test;
2 
3 public interface Moveable {
4     void move();
5 }

  2.被代理对象:Tank.java

复制代码
 1 package com.test;
 2 
 3 import java.util.Random;
 4 
 5 public class Tank implements Moveable {
 6 
 7     public void move() {
 8         System.out.println("Tank moving...");
 9         try {
10             Thread.sleep(new Random().nextInt(10000));
11         } catch (InterruptedException e) {
12             e.printStackTrace();
13         }
14     }
15 
16 }
复制代码

  3.下面写一个Proxy类来为被代理对象产生一个代理类对象,来实现增加记录运行时间的功能。

复制代码
 1 package com.test;
 2 
 3 import java.io.File;
 4 import java.io.FileWriter;
 5 import java.lang.reflect.Constructor;
 6 import java.lang.reflect.Method;
 7 
 8 import javax.tools.JavaCompiler;
 9 import javax.tools.StandardJavaFileManager;
10 import javax.tools.ToolProvider;
11 import javax.tools.JavaCompiler.CompilationTask;
12 
13 public class Proxy {
14     public static Object newProxyInstance(Class interfaces,InvocationHandler h)throws Exception{
15         StringBuffer methodStr = new StringBuffer();
16         String tr = "\r\n";
17         Method[] methods = interfaces.getMethods();
18         //拼接代理类的方法
19         for (Method method : methods) {
20             methodStr.append(
21             "    public "+ method.getReturnType()+ " " +method.getName()+"() {" + tr +
22             "        try {" + tr +
23             "            java.lang.reflect.Method md = " + interfaces.getName() + "." + "class.getMethod(\""  + method.getName() + "\");" + tr +
24             "            h.invoke(this,md);" + tr +
25             "        }catch(Exception e) {e.printStackTrace();}" + tr +
26             "    }" + tr 
27             );
28         }
29         
30         //拼接代理类
31         String src = "package com.test;" + tr +
32         "import com.test.Moveable;" + tr +
33         "public class TimeProxy implements " + interfaces.getName() + " {" + tr +
34         "    private com.test.InvocationHandler h;" + tr +
35         "    public TimeProxy(com.test.InvocationHandler h) {" + tr +
36         "        this.h = h;" + tr +
37         "    }" + tr +
38         methodStr.toString() + tr +
39         "}";
40         //创建代理类
41         String fileName = System.getProperty("user.dir") + "/src/com/test/TimeProxy.java";
42         File file = new File(fileName);
43         FileWriter writer = new FileWriter(file);
44         writer.write(src);
45         writer.flush();
46         writer.close();
47         //编译
48         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
49         StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
50         Iterable units = fileMgr.getJavaFileObjects(fileName);
51         CompilationTask ct = compiler.getTask(null, fileMgr, null, null, null, units);
52         ct.call();
53         fileMgr.close();
54         //加载类到内存:
55         Class c = ClassLoader.getSystemClassLoader().loadClass("com.test.TimeProxy");
56         Constructor constructor = c.getConstructor(InvocationHandler.class); //得到参数为InvocationHandler类型的构造方法
57         Object m = constructor.newInstance(h); //通过该构造方法得到实例
58         return m;
59         
60     }
61 }
复制代码

  4.TankProxy.java

复制代码
 1 package com.test;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 public class TankProxy {
 6     public static <T> T getBean(final Object tank) throws Exception{
 7         return (T)Proxy.newProxyInstance(tank.getClass().getInterfaces()[0], new InvocationHandler(){
 8             public void invoke(Object proxy, Method method) {
 9                 long start = System.currentTimeMillis();
10                 System.out.println("start:"+start);
11                 try {
12                     method.invoke(tank, new Object[]{});
13                 } catch (Exception e) {
14                     e.printStackTrace();
15                 }
16                 long end = System.currentTimeMillis();
17                 System.out.println("end:"+end);
18                 System.out.println("time:"+(end-start));
19             }
20             
21         });
22     }
23 }    
复制代码

  5.测试程序:

复制代码
 1 package com.test;
 2 
 3 import java.util.List;
 4 
 5 import com.extend.Tank2;
 6 import com.extend.Tank3;
 7 import com.juhe.LogProxy;
 8 import com.juhe.TimeProxy;
 9 
10 public class Test {
11     public static void main(String[] args) throws Exception {
12         Tank tank = new Tank();
13         Moveable m =  TankProxy.getBean(tank);
14         m.move();
15         
16     }
17 
18 }
复制代码

执行该程序的结果为:
start:1369121253400
Tank moving...
end:1369121260078
time:6678

动态生成的代理类的内容如下:

复制代码
 1 package com.test;
 2 import com.test.Moveable;
 3 public class TimeProxy implements com.test.Moveable {
 4     private com.test.InvocationHandler h;
 5     public TimeProxy(com.test.InvocationHandler h) {
 6         this.h = h;
 7     }
 8     public void move() {
 9         try {
10             java.lang.reflect.Method md = com.test.Moveable.class.getMethod("move");
11             h.invoke(this,md);
12         }catch(Exception e) {e.printStackTrace();}
13     }
14 
15 }
复制代码

  

  看了这个例子,对动态代理的实现机制应该会有一定的了解了!

   小结:动态代理在运行期通过接口动态生成代理类,这为其带来了一定的灵活性,但这个灵活性却带来了两个问题,第一代理类必须实现一个接口,如果没实现接口会抛出一个异常。第二性能影响,因为动态代理使用反射的机制实现的,首先反射肯定比直接调用要慢,其次使用反射大量生成类文件可能引起Full GC造成性能影响,因为字节码文件加载后会存放在JVM运行时区的方法区(或者叫持久代)中,当方法区满的时候,会引起Full GC,所以当你大量使用动态代理时,可以将持久代设置大一些,减少Full GC次数。 

分享到:
评论

相关推荐

    动态代理实现AOP机制

    动态代理是实现AOP的一种常见手段,尤其在Java中应用广泛。 动态代理主要分为JDK动态代理和CGLIB动态代理两种方式: 1. **JDK动态代理**: JDK动态代理基于接口实现,它通过`java.lang.reflect.Proxy`类和`java....

    利用反射和动态代理机制实现自定义拦截器Interceptor

    利用反射和动态代理机制实现自定义拦截器Interceptor 在本文中,我们将探讨如何利用反射和动态代理机制来实现自定义拦截器Interceptor。拦截器Interceptor是一种常见的设计模式,用于在方法调用前后执行某些操作,...

    基于Java动态代理和反射机制实现ORM

    例如,我们可以定义一个`BaseDAO`接口,包含增删改查等通用方法,然后使用Proxy创建该接口的代理实现。InvocationHandler的invoke方法会处理这些方法调用,生成对应的SQL并执行。 反射则用于在运行时获取类的信息,...

    java动态代理机制

    Java动态代理机制是Java语言提供的一种强大的功能,它允许在运行时创建代理对象来实现特定接口,从而可以灵活地扩展或增强已有代码的功能。在Java中,动态代理主要通过两个类来实现:`java.lang.reflect.Proxy` 和 `...

    AOP动态代理(反射机制)

    AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,旨在提供一种方法来组织和模块化系统中的...通过阅读《AOP动态代理(反射机制)》这本书,你可以深入理解这些概念,并通过丰富的示例掌握其实现方法。

    Java动态代理两种实现方式

    Java提供了两种主要的动态代理实现方式:JDK自身的动态代理(基于接口)和Cglib库。 ### JDK动态代理 JDK动态代理是通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现的。Proxy类...

    springboot中的 动态代理的选择测试代码

    在Spring Boot中,AOP(面向切面编程)是通过动态代理实现的,用于解耦核心业务逻辑和横切关注点,如日志、事务管理等。Spring AOP提供了两种动态代理方式:JDK代理和CGLIB代理。选择哪种代理方式取决于目标对象是否...

    Java实现动态代理

    动态代理通常用于实现AOP(面向切面编程)或者提供一种机制来增强对象的功能,比如日志、事务管理、性能监控等。 Java中的动态代理主要有两种实现方式:JDK动态代理和CGLIB动态代理。 1. **JDK动态代理**: JDK...

    ioc框架,通过动态代理实现.

    在学习和应用IOC框架,尤其是通过动态代理实现时,我们需要了解以下知识点: 1. IOC的基本原理和实现机制,包括依赖注入(Dependency Injection)的概念。 2. 如何配置IOC容器,如XML配置或注解配置。 3. 如何使用...

    反射实现 AOP 动态代理模式(Spring AOP 的实现原理)

    在Java开发中,反射机制是实现动态代理的关键技术之一。反射提供了在运行时访问和操作类的能力,这使得动态代理的实现成为可能。在Java中,可以使用java.lang.reflect包下的相关类和接口实现动态代理。 例如,通过...

    jdk与cglib动态代理与底层实现

    JDK动态代理基于Java的接口机制实现,因此,要使用JDK动态代理,被代理的目标类必须实现至少一个接口。`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口是JDK动态代理的核心。Proxy类用于...

    java代理机制 JDK动态代理和cglib代理 详解

    本文将深入探讨两种主要的Java代理实现:JDK动态代理和CGLIB代理。 一、JDK动态代理 JDK动态代理基于接口实现,它要求被代理的类必须实现至少一个接口。在运行时,Java会动态地创建一个新的类,这个类实现了与原始...

    三种动态代理的实现

    下面将详细讨论三种主要的动态代理实现方式:静态代理、JDK动态代理和CGLIB动态代理。 1. 静态代理: 静态代理是最基础的形式,它需要程序员手动编写代理类。代理类和真实对象实现了相同的接口,代理类在调用实际...

    JAVA的反射机制与动态代理

    在Java中,有两种实现动态代理的方式:`java.lang.reflect.Proxy`类和`java.util.function.InvocationHandler`接口,以及JDK动态代理;另一种是基于CGLIB或ASM等第三方库的字节码操作,实现类级别的代理。 1. JDK...

    Java反射机制与动态代理

    在实际应用中,Java的反射和动态代理常常结合使用,比如Spring框架中的AOP就是基于动态代理实现的。通过反射,Spring可以获取到bean的类信息,然后根据配置决定是否需要创建代理对象,如果需要,就会使用动态代理来...

Global site tag (gtag.js) - Google Analytics