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

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

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

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

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

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

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

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

下面看一个例子:

代理接口:IComputer.java

package com.proxy;

 public interface IComputer {
     void execute();
}

2.被代理的对象:Lattop.java

package com.proxy;

//笔记本电脑
public class Laptop implements IComputer {

    public void execute() {
        System.out.println("电脑正在执行中......");
    }

}

  3.调用处理类:TimeHander.java

package com.proxy;

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

public class TimeHander implements InvocationHandler {
    private Object object;
    public TimeHander(Object object) {
        this.object = object;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("start:"+start);
        method.invoke(object, args);
        Thread.sleep((int)(Math.random()*2000));
        long end = System.currentTimeMillis();
        System.out.println("end:"+end);
        System.out.println("total:"+(end-start));
        return null;
    }

}

  4.测试程序

package com.proxy;

import java.lang.reflect.Proxy;

public class ProxyTest {

    public static void main(String[] args) {
        Laptop laptop = new Laptop();
        TimeHander hander = new TimeHander(laptop);
        IComputer computer = (IComputer)Proxy.newProxyInstance(laptop.getClass().getClassLoader(), laptop.getClass().getInterfaces(), hander);
        computer.execute();
    }

}

  

二、动态代理运行机制

  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

package com.test;

public interface Moveable {
    void move();
 }

  2.被代理对象:Tank.java

package com.test;

import java.util.Random;

public class Tank implements Moveable {

    public void move() {
        System.out.println("Tank moving...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

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

package com.test;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class Proxy {
    public static Object newProxyInstance(Class interfaces,InvocationHandler h)throws Exception{
        StringBuffer methodStr = new StringBuffer();
        String tr = "\r\n";
        Method[] methods = interfaces.getMethods();
        //拼接代理类的方法
        for (Method method : methods) {
            methodStr.append(
            "    public "+ method.getReturnType()+ " " +method.getName()+"() {" + tr +
            "        try {" + tr +
            "            java.lang.reflect.Method md = " + interfaces.getName() + "." + "class.getMethod(\""  + method.getName() + "\");" + tr +
            "            h.invoke(this,md);" + tr +
            "        }catch(Exception e) {e.printStackTrace();}" + tr +
            "    }" + tr
            );
        }

        //拼接代理类
        String src = "package com.test;" + tr +
        "import com.test.Moveable;" + tr +
        "public class TimeProxy implements " + interfaces.getName() + " {" + tr +
        "    private com.test.InvocationHandler h;" + tr +
        "    public TimeProxy(com.test.InvocationHandler h) {" + tr +
        "        this.h = h;" + tr +
        "    }" + tr +
        methodStr.toString() + tr +
        "}";
        //创建代理类
        String fileName = System.getProperty("user.dir") + "/src/com/test/TimeProxy.java";
        File file = new File(fileName);
        FileWriter writer = new FileWriter(file);
        writer.write(src);
        writer.flush();
        writer.close();
        //编译
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileName);
        CompilationTask ct = compiler.getTask(null, fileMgr, null, null, null, units);
        ct.call();
        fileMgr.close();
        //加载类到内存:
        Class c = ClassLoader.getSystemClassLoader().loadClass("com.test.TimeProxy");
        Constructor constructor = c.getConstructor(InvocationHandler.class); //得到参数为InvocationHandler类型的构造方法
        Object m = constructor.newInstance(h); //通过该构造方法得到实例
        return m;

    }
}

  4.TankProxy.java

package com.test;

import java.lang.reflect.Method;

public class TankProxy {
    public static <T> T getBean(final Object tank) throws Exception{
        return (T)Proxy.newProxyInstance(tank.getClass().getInterfaces()[0], new InvocationHandler(){
            public void invoke(Object proxy, Method method) {
                long start = System.currentTimeMillis();
                System.out.println("start:"+start);
                try {
                    method.invoke(tank, new Object[]{});
                } catch (Exception e) {
                    e.printStackTrace();
                }
                long end = System.currentTimeMillis();
                System.out.println("end:"+end);
                System.out.println("time:"+(end-start));
            }

        });
    }
}

  5.测试程序:

package com.test;

import java.util.List;

import com.extend.Tank2;
import com.extend.Tank3;
import com.juhe.LogProxy;
import com.juhe.TimeProxy;

public class Test {
    public static void main(String[] args) throws Exception {
        Tank tank = new Tank();
        Moveable m =  TankProxy.getBean(tank);
        m.move();

    }

}

  

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

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

package com.test;
import com.test.Moveable;
public class TimeProxy implements com.test.Moveable {
    private com.test.InvocationHandler h;
    public TimeProxy(com.test.InvocationHandler h) {
        this.h = h;
    }
    public void move() {
        try {
            java.lang.reflect.Method md = com.test.Moveable.class.getMethod("move");
            h.invoke(this,md);
        }catch(Exception e) {e.printStackTrace();}
    }

}

  

看完这个例子就对动态代理实现机制应该有一定的了解了。

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

  

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

时间: 2024-11-14 12:53:52

动态代理的使用以及其实现机制的相关文章

java反射机制与动态代理

在学习HadoopRPC时,用到了函数调用,函数调用都是采用的java的反射机制和动态代理来实现的,所以现在回顾下java的反射和动态代理的相关知识. 一.反射 JAVA反射机制定义: JAVA反射机制是java程序在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. 反射就是把Java类中的各种成分映射成相应的Java类. Java反射机制主要提供了以下功能: 1

简谈Java 反射机制,动态代理

谈谈 Java 反射机制,动态代理是基于什么原理?小编整理了一些java进阶学习资料和面试题,需要资料的请加JAVA高阶学习Q群:701136382 这是小编创建的java高阶学习交流群,加群一起交流学习深造.群里也有小编整理的2019年最新最全的java高阶学习资料! 反射机制 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect,官方用语)的能力.可以在运行时通过提供完整的"包名+类名.class"得到某个对象的类型. 功能 在运行时能判断任意一个对象所属的

深入剖析动态代理

动态代理是指在运行时,动态生成代理类.代理类的字节码将在运行时生成并载入当前的ClassLoader. 生成动态代理类的方法很多,如JDK自带的动态代理.CGLIB.Javassist或者ASM库. JDK动态代理使用简单,它内置在JDK中,因此不需要引入第三方Jar包,但相对功能比较弱.CGLIB和Javassist都是高级的字节码生成库,总体性能比JDK自带的动态代理好,而且功能十分强大.ASM是低级的字节码生成工具,使用ASM已经近乎在于使用Javabytecode编程,对开发人员要求较高

Java反射学习总结四(动态代理使用实例和内部原理解析)

通过上一篇文章介绍的静态代理Java反射学习总结三(静态代理)中,大家可以发现在静态代理中每一个代理类只能为一个接口服务,这样一来必然会产生过多的代理,而且对于每个实例,如果需要添加不同代理就要去添加相应的代理类.解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能或者说去动态的生成这个代理类,那么此时就必须使用动态代理完成. 动态代理知识点: Java动态代理类位于java.lang.reflect包下,主要有以下一个接口和一个类: 1.InvocationHandler接口:    

Java开发纯接口的动态代理

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 相关类及接口 java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象.// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器static InvocationHandler getInvocationHandler(Object pr

系统架构设计——设计模式之代理模式(二)CGLIB动态代理实现

像上一篇所说的代理模式其实是静态代理,在实际开发中其实应用不大,因为他需要事先知道被代理对象是谁,而且被代理对象和代理对象实现了公共的接口.实际情况往往并不能满足这些条件,我们往往在写代理模式的时候并不知道到时候被代理的对象是谁.解决办法就是--动态代理.以下我们将使用CGLIB实现动态代理. 一.动态代理概述 程序在运行期而不是编译器,生成被代理对象的代理对象,并且被代理对象并不需要和代理对象实现共同的接口.基于此,我们可以利用代理对象,提供一种以控制对被代理对象的访问. 1.1 动态代理的原

Spring AOP高级——源码实现(1)动态代理技术

在正式进入Spring AOP的源码实现前,我们需要准备一定的基础也就是面向切面编程的核心——动态代理. 动态代理实际上也是一种结构型的设计模式,JDK中已经为我们准备好了这种设计模式,不过这种JDK为我们提供的动态代理有2个缺点: 只能代理实现了接口的目标对象: 基于反射,效率低 鉴于以上2个缺点,于是就出现了第二种动态代理技术——CGLIB(Code Generation Library).这种代理技术一是不需要目标对象实现接口(这大大扩展了使用范围),二是它是基于字节码实现(这比反射效率高

Java动态代理、CGLIB动态代理

开篇 Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理.为啥要这样呢, 是因为使用代理有 2 个优势: 可以隐藏委托类的实现 可以实现客户与委托类之间的解耦, 在不修改委托类代码的情况下能够做一些额外的处理 我们举个很常见的例子: 工厂会生产很多的玩具, 但是我们买玩具都是到商店买的, 而不是到工厂去买的, 工厂怎么生产我们并不关心, 我们只知道到商店可以买到自己想要的玩具,并且,如果我们需要送人的话商店可以把这些玩具使用礼品盒包装.这个工厂就是

java动态代理机制

首先了解代理设计模式,其思想是为其他对象提供一种代理以控制对这个对象的访问. java动态代理就是遵循这种思想,spring中的AOP实现原理就是java的动态代理. 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的. 每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通