详解Java动态代理的实现机制

一、概述
  代理是一种设计模式,其目的是为其他对象提供一个代理以控制对某个对象的访问,代理类负责为委托类预处理消息,过滤消息并转发消息以及进行消息被委托类执行后的后续处理。为了保持行为的一致性,代理类和委托类通常会实现相同的接口。

  按照代理的创建时期,代理类可分为两种:

静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,也就是说在程序运行前代理类的.class文件就已经存在。
动态代理:在程序运行时运用反射机制动态创建生成。
  下面在将动态代理的实现机制之前先简单介绍一下静态代理。

二、静态代理
  上面说过,代理类和委托类一般都要实现相同的接口,下面先定义这个接口:

public interface Service
{
  public void add();
}

委托类作为接口的一种实现,定义如下:

public class ServiceImpl implements Service
{
  public void add()
  {
    System.out.println("添加用户!");

  }
}

假如我们要对委托类加一些日志的操作,代理类可做如下定义:

public class ServiceProxy implements Service
{
  private Service service;
  public ServiceProxy(Service service)
  {
    super();
    this.service = service;
  }
  public void add()
  {
    System.out.println("服务开始");
    service.add();
    System.out.println("服务结束");
  }
}

编写测试类:

public class TestMain
{
  public static void main(String[] args)
  {
    Service serviceImpl=new ServiceImpl();
    Service proxy=new ServiceProxy(serviceImpl);
    proxy.add();
  }
}

运行测试程序,结果如下图:

  从上面的代码可以看到,静态代理类只能为特定的接口服务,如果要服务多类型的对象,就要为每一种对象进行代理。我们就会想是否可以通过一个代理类完成全部的代理功能,于是引入的动态代理的概念。

三、动态代理
  Java的动态代理主要涉及两个类,Proxy和InvocationHandler。

  Proxy:提供了一组静态方法来为一组接口动态地生成代理类及其对象。

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)

  InvocationHandler:它是调用处理器接口,自定义了一个invok方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问

// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
Object invoke(Object proxy, Method method, Object[] args)

实现Java的动态代理,具体有以下四个步骤:

1、通过实现InvocationHandler接口创建自己的调用处理器
2、通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类
3、通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器类接口类型
4、通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

下面根据上述的四个步骤来实现自己的动态代理的示例:

接口和接口的实现类(即委托类)跟上面静态代理的代码一样,这里我们来实现InvocationHandler接口创建自己的调用处理器

public class ServiceHandle implements InvocationHandler
{
  private Object s;

  public ServiceHandle(Object s)
  {
    this.s = s;
  }
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable
  {
    System.out.println("服务开始");
    //invoke表示对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
    Object result=method.invoke(s, args);
    System.out.println("服务结束");
    return result;
  }
}

编写测试类:

public class TestMain
{
  public static void main(String[] args)
  {
    Service service=new ServiceImpl();
    InvocationHandler handler=new ServiceHandle(service);
    Service s=(Service) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), handler);
    s.add();
  }
}

  运行测试程序,结果同静态代理。我们可以看到上述代码并没有我们之前说的步骤2和3,这是因为Prox的静态方法newProxyInstance已经为我们封装了这两个步骤。具体的内部实现如下:

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

newProxyInstance函数的内部实现为:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
  {
       //检查h不为空,否则抛异常
      Objects.requireNonNull(h);
      //获得与制定类装载器和一组接口相关的代理类类型对象
      final Class<?>[] intfs = interfaces.clone();

      //检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的
      final SecurityManager sm = System.getSecurityManager();
      if (sm != null)
      {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
      }
      //获得与制定类装载器和一组接口相关的代理类类型对象
      Class<?> cl = getProxyClass0(loader, intfs);
      try
      {
        if (sm != null)
        {
          checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        // 通过反射获取构造函数对象并生成代理类实例
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers()))
        {
          AccessController.doPrivileged(new PrivilegedAction<Void>()
          {
            public Void run()
            {
            cons.setAccessible(true);
            return null;
            }
          });
        }
        return cons.newInstance(new Object[]{h});
      }
      catch (IllegalAccessException|InstantiationException e)
      {
        throw new InternalError(e.toString(), e);
      }
      catch (InvocationTargetException e)
      {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException)
        {
          throw (RuntimeException) t;
        }
        else
        {
          throw new InternalError(t.toString(), t);
        }
      }
      catch (NoSuchMethodException e)
      {
        throw new InternalError(e.toString(), e);
      }
 }

四、模拟实现Proxy类

根据上面的原理介绍,我们可以自己模拟实现Proxy类:

public class Proxy
{
  public static Object newProxyInstance(Class inface,InvocationHandle h) throws Exception
  {
    String rt="\r\n";
    String methodStr="";
    Method[] methods=inface.getMethods();
    for(Method m:methods)
    {
      methodStr+="@Override"+rt+
           "public void "+m.getName()+"()"+rt+"{" + rt +
          "  try {"+rt+
          " Method md="+inface.getName()+".class.getMethod(\""+m.getName()+"\");"+rt+
            "h.invoke(this,md);"+rt+
          "  } catch(Exception e){e.printStackTrace();}"+rt+

          "}";
    }
    String src="package test;"+rt+
        "import java.lang.reflect.Method;"+rt+
        "public class ServiceImpl2 implements "+inface.getName()+ rt+
        "{"+rt+
          "public ServiceImpl2(InvocationHandle h)"+rt+
          "{"+rt+
            "this.h = h;"+rt+
          "}"+rt+
          " test.InvocationHandle h;"+rt+
          methodStr+
        "}";
    String fileName="d:/src/test/ServiceImpl2.java";
    //compile
    compile(src, fileName);
    //load into memory and create instance
    Object m = loadMemory(h);

    return m;
  }
  private static void compile(String src, String fileName) throws IOException
  {
    File f=new File(fileName);
    FileWriter fileWriter=new FileWriter(f);
    fileWriter.write(src);
    fileWriter.flush();
    fileWriter.close();
    //获取此平台提供的Java编译器
    JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
    //获取一个标准文件管理器实现的新实例
    StandardJavaFileManager fileManager=compiler.getStandardFileManager(null,null, null);
    //获取表示给定文件的文件对象
    Iterable units=fileManager.getJavaFileObjects(fileName);
    //使用给定组件和参数创建编译任务的 future
    CompilationTask t=compiler.getTask(null, fileManager, null, null, null, units);
    //执行此编译任务
    t.call();
    fileManager.close();
  }
  private static Object loadMemory(InvocationHandle h)
      throws MalformedURLException, ClassNotFoundException,
      NoSuchMethodException, InstantiationException,
      IllegalAccessException, InvocationTargetException
  {
    URL[] urls=new URL[] {new URL("file:/"+"d:/src/")};
    //从路径d:/src/加载类和资源
    URLClassLoader ul=new URLClassLoader(urls);
    Class c=ul.loadClass("test.ServiceImpl2");
    //返回Class对象所表示的类的指定公共构造方法。
    Constructor ctr=c.getConstructor(InvocationHandle.class);
    //使用此 Constructor对象ctr表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
    Object m = ctr.newInstance(h);
    return m;
  }
}

五、总结
  1、所谓的动态代理就是这样一种class,它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后改class就宣称它实现了这些interface,但是其实它不会替你作实质性的工作,而是根据你在生成实例时提供的参数handler(即InvocationHandler接口的实现类),由这个Handler来接管实际的工作。

  2、Proxy的设计使得它只能支持interface的代理,Java的继承机制注定了动态代理类无法实现对class的动态代理,因为多继承在Java中本质上就行不通。

以上就是本文的全部内容,希望对大家的学习有所帮助。

(0)

相关推荐

  • 详解java中动态代理实现机制

    代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. JAVA各种动态代理实现的比较 接口 interface AddInterface{ int add(int a, int b); } interface SubInterfa

  • java代理模式与动态代理模式详解

    1.代理模式 所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 生活中的例子:过年加班比较忙,没空去买火车票,这时可以打个电话到附近的票务中心,叫他们帮你买张回家的火车票,当然这会附加额外的劳务费.但要清楚票务中心自己并不卖票,只有火车站才真正卖票,票务中心卖给你的票其实是通过火车站实现的.这点很重要!

  • java实现动态代理方法浅析

    一些Java项目中在mybatis与spring整合中有MapperScannerConfigurer的使用,该类通过反向代理自动生成基于接口的动态代理类. 有鉴于此,本文浅析了java的动态代理. 本文使用动态代理模拟处理事务的拦截器. 接口: public interface UserService { public void addUser(); public void removeUser(); public void searchUser(); } 实现类: public class

  • java动态代理详解

    代理都知道吧,你去买东西就有很多的代理商,他们就是卖原厂的东西.比如,你天天要买肉,猪是农民伯伯养的,但你是从屠夫手上买到肉的,这个屠夫就可以当成是代理.那为什么要代理呢,代理有什么用呢,当然是有事给他做了,对于屠夫这个代理就好理解了,因为你自己不可能去宰猪吧,所以代理就是去买活猪,然后宰掉再卖给你,当然屠夫有可能给肉注点水,关键看他坏不坏,所以屠夫的整个流程就是: 这个流程用代码怎么实现呢:我们应该要用三个类You.Butcher.Farmer分别指你.屠夫.农民伯伯.其中农民伯伯又提供一个买

  • 代理模式之Java动态代理实现方法

    今天一个偶然的机会我突然想看看JDK的动态代理,因为以前也知道一点,而且只是简单的想测试一下使用,使用很快里就写好了这么几个接口和类:接口类:UserService.java 复制代码 代码如下: package com.yixi.proxy;public interface UserService {    public int save() ;    public void update(int id);} 实现类:UserServiceImpl.java 复制代码 代码如下: packag

  • 十分钟理解Java中的动态代理

    若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. 一.概述 1. 什么是代理 我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家"委托"代理为其销售商品.关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,"委托者"对我们来说是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就

  • Java动态代理实现_动力节点Java学院整理

    动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程. 通常情况下,代理模式中的每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称之为静态代理(Static Proxy).那么有没有一种机制能够让系统在运行时动态创建代理类?答案就是本文将要介绍的动态代理(Dynamic Proxy).动态代理是一种较为高级的代理模式,它在事务管理.AOP(Aspect-Ori

  • Java动态代理的应用详解

    动态代理其实就是java.lang.reflect.Proxy类动态的根据您指定的所有接口生成一个class byte,该class会继承Proxy类,并实现所有你指定的接口(您在参数中传入的接口数组):然后再利用您指定的classloader将 class byte加载进系统,最后生成这样一个类的对象,并初始化该对象的一些值,如invocationHandler,以即所有的接口对应的Method成员. 初始化之后将对象返回给调用的客户端.这样客户端拿到的就是一个实现你所有的接口的Proxy对象

  • 基于接口实现java动态代理示例

    Subject.java 复制代码 代码如下: package _20140416_; import java.util.List; public interface Subject {   public String say(String name,int age);   public List<Person> getAllList(String name);} RealSubject.java 复制代码 代码如下: package _20140416_; import java.util.

  • 详解Java动态代理的实现机制

    一.概述 代理是一种设计模式,其目的是为其他对象提供一个代理以控制对某个对象的访问,代理类负责为委托类预处理消息,过滤消息并转发消息以及进行消息被委托类执行后的后续处理.为了保持行为的一致性,代理类和委托类通常会实现相同的接口. 按照代理的创建时期,代理类可分为两种: 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,也就是说在程序运行前代理类的.class文件就已经存在. 动态代理:在程序运行时运用反射机制动态创建生成. 下面在将动态代理的实现机制之前先简单介绍一下静态代理. 二

  • 详解Java动态代理的实现及应用

    详解Java动态代理的实现及应用 Java动态代理其实写日常业务代码是不常用的,但在框架层一起RPC框架的客户端是非常常见及重要的.spring的核心思想aop的底层原理实现就使用到了java的动态代理技术. 使用代理可以实现对象的远程调用以及aop的实现. java的动态代理的实现,主要依赖InvoctionHandler(接口)和Proxy(类)这两个. 下面是一个例子 实现的代理的一般需要有个接口 package com.yasin.ProxyLearn; public interface

  • 详解JAVA动态代理

    文档更新说明 2018年09月24日 v1.0 初稿 代理在生活中很常见,比如说婚介网站,其实就是找对象的代理:还有社保代理.人事代理:还有找黄牛抢票,其实也是一种代理:而这些代理,在JAVA中也是有对应实现的. 1.为什么要动态代理 动态代理的作用其实就是在不修改原代码的前提下,对已有的方法进行增强. 关键点: 不修改原来已有的代码(满足设计模式的要求) 对已有方法进行增强 2.举个栗子 我们用一个很简单的例子来说明:Hello类,有一个introduction方法. 现在我们的需求就是不修改

  • 详解java动态代理模式

    本文针对java动态代理进行知识点整理,具体内容如下 一. JAVA的动态代理(比较官方说法) 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处 理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的 对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提 供特定的服务. 按照代理的创建时期,代理类可以分为两种. 静态代理:由程序员创建或特定工

  • 详解java动态代理的2种实现方式

    java的动态代理在接java的api上有说明,这里就不写了.我理解的代理: 对特定接口中特定方法的功能进行扩展,这就是代理.代理是通过代理实例关联的调用处理程序对象调用方法. 下面通过一个例子看一下: 接口: public interface Num { void show(); int getNum(); int getProduct(int x); } 实现类: public class MyNum implements Num { @Override public int getNum(

  • Java动态代理和反射机制详解

    反射机制 Java语言提供的一种基础功能,通过反射,我们可以操作这个类或对象,比如获取这个类中的方法.属性和构造方法等. 动态代理:分为JDK动态代理.cglib动态代理(spring中的动态代理). 静态代理 预先(编译期间)确定了代理者与被代理者之间的关系,也就是说,若代理类在程序运行前就已经存在了,这种情况就叫静态代理 动态代理 代理类在程序运行时创建的代理方式.也就是说,代理类并不是在Java代码中定义的,而是在运行期间根据我们在Java代码中的"指示"动态生成的. 动态代理比

  • 详解Java 类的加载机制

    一.类的加载机制 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接

  • 详解Java动态字节码技术

    对 Debug 的好奇 初学 Java 时,我对 IDEA 的 Debug 非常好奇,不止是它能查看断点的上下文环境,更神奇的是我可以在断点处使用它的 Evaluate 功能直接执行某些命令,进行一些计算或改变当前变量. 刚开始语法不熟经常写错代码,重新打包部署一次代码耗时很长,我就直接面向 Debug 开发.在要编写的方法开始处打一个断点,在 Evaluate 框内一次次地执行方法函数不停地调整代码,没问题后再将代码复制出来放到 IDEA 里,再进行下一个方法的编写,这样就跟写 PHP 类似的

  • 详解JAVA Spring 中的事件机制

    说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎么实现的呢?说listener之前,我们先从设计模式开始讲起. 观察者模式 观察者模式一般包含以下几个对象: Subject:被观察的对象.它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify().目标类可以是接口,也可以是抽象类或具体类. ConcreteSubject:具体的

  • 详解 Java静态代理

    今天要介绍的是一个Java中一个很重要的概念--代理. 什么是代理?联系生活想想看,代理似乎并不陌生,最形象的代表便是经纪人,明星一般都有经纪人,经纪人作为中间人,负责代理明星的相关事宜,比如说,有人要请明星去唱歌表演,一般不会直接跟明星联系,而是联系他的经纪人,他的经纪人来负责安排行程,而真正唱歌表演的还是明星本人,经纪人仅仅作为一个附加物存在. 在Java中,代理也是这样的概念,来看个栗子: 先来创建一个明星类Stars: public class Stars implements ISta

随机推荐