当前位置: 首页 > 编程笔记 >

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

范华清
2023-03-14
本文向大家介绍详解Java动态代理的实现机制,包括了详解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中本质上就行不通。

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

 类似资料:
  • 本文向大家介绍Java中的引用和动态代理的实现详解,包括了Java中的引用和动态代理的实现详解的使用技巧和注意事项,需要的朋友参考一下 我们知道,动态代理(这里指JDK的动态代理)与静态代理的区别在于,其真实的代理类是动态生成的。但具体是怎么生成,生成的代理类包含了哪些内容,以什么形式存在,它为什么一定要以接口为基础? 如果去看动态代理的源代码(java.lang.reflect.Proxy),会

  • 本文向大家介绍详解Java JDK动态代理,包括了详解Java JDK动态代理的使用技巧和注意事项,需要的朋友参考一下   今天来看看Java的另一种代理方式——JDK动态代理   我们之前所介绍的代理方式叫静态代理,也就是静态的生成代理对象,而动态代理则是在运行时创建代理对象。动态代理有更强大的拦截请求功能,因为可以获得类的运行时信息,可以根据运行时信息来获得更为强大的执(骚)行(操)力(作)。

  • 本文向大家介绍Spring JDK动态代理实现过程详解,包括了Spring JDK动态代理实现过程详解的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Spring JDK动态代理实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1. 创建项目 在 MyEclipse 中创建一个名称为 springDemo03 的 Web 项

  • 本文向大家介绍深入理解java动态代理机制,包括了深入理解java动态代理机制的使用技巧和注意事项,需要的朋友参考一下 retrofit是一个解耦性非常高的网络请求框架,最近在研究的时候发现了动态代理这个非常强大且实用的技术,这篇文章将作为retrofit的前置知识,让大家认识:动态代理有哪些应用场景,什么是动态代理,怎样使用,它的局限性在什么地方? 动态代理的应用场景 1. AOP—面向切面编程

  • 本文向大家介绍java中动态代理的实现,包括了java中动态代理的实现的使用技巧和注意事项,需要的朋友参考一下 动态代理的实现 使用的模式:代理模式。 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。类似租房的中介。 两种动态代理: (1)jdk动态代理,jdk动态代理是由Java内部的反射机制来实现的,目标类基于统一的接口(InvocationHandler) (2)cglib动

  • 本文向大家介绍详解 Java静态代理,包括了详解 Java静态代理的使用技巧和注意事项,需要的朋友参考一下   今天要介绍的是一个Java中一个很重要的概念——代理。   什么是代理?联系生活想想看,代理似乎并不陌生,最形象的代表便是经纪人,明星一般都有经纪人,经纪人作为中间人,负责代理明星的相关事宜,比如说,有人要请明星去唱歌表演,一般不会直接跟明星联系,而是联系他的经纪人,他的经纪人来负责安排