Java动态代理

  • 代理模式

  • Java动态代理

  • 动态代理实现


代理是一种设计模式。在一些情况下,一个对象并不能或者并不希望被其他某些调用,代理就可以在这其中起到中介的作用。
这样既可以使用真实对象的方法,又可以保护目标对象,还可以在真实方法调用的前后进行一些操作。

常见的代理模式实现方式如下:
定义一个接口:

public interface Subject {
    String doSth(String name);
    }

实现接口:

    public class RealSubject implements Subject{
    public String doSth(String name) {
        return String.format("I'm %s, i am doing Real thing now...",new String[]{name});
    }
    }

如果有这样的情况,我们并不想让测试类直接调用RealSubject的doSth方法
实现接口,创建一个代理的类,让代理类来调用RealSubject对象的方法 :

    public class ProxySubject implements Subject{
    private  Subject realSubject;

    public  ProxySubject(Subject realSubject)  {this.realSubject = realSubject ;}

    public String doSth(String name)  {
        System.out.println("Proxy start -----");
        System.out.println(realSubject.doSth(name));  // 此处执行真实对象的request方法
        System.out.println("Proxy end -----");
        return "OK";
    }
    }

测试:

    public class Test {

    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject subject = new ProxySubject(realSubject);

        subject.doSth("Irving");
    }
    }

返回的结果:
Proxy start —–
I’m Irving, i am doing Real thing now…
Proxy end —–

上面实现了一个简单的代理模式,被代理对象的类(RealSubject)和代理类(ProxySubject)都需要实现同一个接口,这会导致
1. 增加维护的复杂度;将来修改接口的时候要改动两个实现,
2. 增加代码量:而且ProxySubject在这里只做了一个“壳”的作用,特意给它写个类实在有点不划算。

此时的代理方式,在程序运行之前就已经确定了谁代理谁。然后按照固定的方式将被代理的对象传入代理类,然后再调用代理类中的方法。

这是静态代理方式。

JDK提供了一种“动态代理”方式:
动态代理类的源码是在程序运行期间由JVM根据反射等动态机制生成的,所以不存在代理类的字节码文件,代理类和被代理的类是在运行期间确定的代理关系。

接下来看看如何使用动态代理的方式生成上面的接口实现RealSubject的代理。

先实现一个处理器,实现InvocationHandler,invoke方法定义了如何处理方法调用时的逻辑,简单处理下:

    public class DynamicProxy implements InvocationHandler{
    private Object object;

    public DynamicProxy(Object obj){
        object = obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Dynamic Proxy start -----");
        System.out.println(method.invoke(object, args));
        System.out.println("Dynamic Proxy end -----");
        return "OK";
    }
    }

调用类实现:

    public class TestDynamic {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();

        InvocationHandler invocationHandler = new DynamicProxy(realSubject);

        Class cls = realSubject.getClass();

        Subject subject = (Subject)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),invocationHandler);
        subject.doSth("IRVING");
    }
    }

从DEBUG说起,可以DEBUG看看生成的Subject subject是什么类型的:
这里写图片描述

$Proxy0类型的,这明显不是我们自定义的类型。

内部类的定义有规范是 A $ B 的格式,但是这里是以$开头的。感觉应该是标记说明是运行时生成的类对象。
观察发现这个对象可以转化成Subject对象,说明这个类可能是实现了这个接口(也可能继承了这个接口的实现类)。

这个对象是如何生成的?
Proxy.newProxyInstance方法传入了三个参数:
加载被代理类的ClassLoader、被代理类型的接口列表、自定义的InvocationHandler实现。
该方法中,有这样一句:

    Class<?> cl = getProxyClass0(loader, interfaces);

核心的,这个方法有如下代码:

     String proxyName = proxyPkg + proxyClassNamePrefix + num;
                /*
                 * Verify that the class loader hasn't already
                 * defined a class with the chosen name.
                 */

                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces);
                try {
                    proxyClass = defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {

其中的proxyClassNamePrefix 就是:

        /** prefix for all proxy class names */
    private final static String proxyClassNamePrefix = "$Proxy";

所以我们的代理类命名为:$Proxy加上一个数字。

    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces);

这句生成了代理类的字节数组,并且结合后面一句,从内存中加载出来创建了代理类。

再简要看看ProxyGenerator的generateProxyClass方法:

        public static byte[] generateProxyClass(final String var0, Class[] var1) {
        ProxyGenerator var2 = new ProxyGenerator(var0, var1);
        final byte[] var3 = var2.generateClassFile();
        if(saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Void run() {
                    try {
                        FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");
                        var1.write(var3);
                        var1.close();
                        return null;
                    } catch (IOException var2) {
                        throw new InternalError("I/O exception saving generated file: " + var2);
                    }
                }
            });
        }
        return var3;
    }

除了返回class的字节数组,甚至还发现,如果saveGeneratedFiles参数为true的时候,还能把该class保存到磁盘。
稍微修改下main方法:

    public class TestDynamic {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();

        InvocationHandler invocationHandler = new DynamicProxy(realSubject);

        Class cls = realSubject.getClass();

        byte[] data = ProxyGenerator.generateProxyClass("$myProxy0", cls.getInterfaces());

        FileOutputStream out = null;

        try {
            out = new FileOutputStream("E:\\$myProxy0.class");
            out.write(data);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
    }

用反编译工具查看class的内容:

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

public final class $myProxy0 extends Proxy
  implements Subject
{
  private static Method m1;
  private static Method m0;
  private static Method m3;
  private static Method m2;

  public $myProxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String doSth(String paramString)
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m3, new Object[] { paramString });
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m3 = Class.forName("Subject").getMethod("doSth", new Class[] { Class.forName("java.lang.String") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
    }
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
}

$myProxy0类继承自Proxy并且实现了Subject接口。
除了重写了hashCode和equals方法之外,它也有一个doSth方法,这和我们之前静态代理模式的代理是一个方式,并且doSth方法触发了InvocationHandler对象h的invoke方法。也就是我们自己实现的Handler。
即,调用代理的doSth方法,也就是调用了InvocationHandler的invoke方法。

return (String)this.h.invoke(this, m3, new Object[] { paramString });
### Java 动态代理的实现原理与用法 #### 1. Java 动态代理概述 Java 动态代理是一种在运行时动态生成代理对象的技术,它允许开发者无需提前定义具体的代理类即可完成方法拦截和增强功能。这种机制广泛应用于 AOP(面向切面编程)、事务管理以及日志记录等领域[^2]。 #### 2. 动态代理的核心组件 动态代理主要依赖于 `java.lang.reflect.Proxy` 类和 `InvocationHandler` 接口来实现。以下是其核心组成部分: - **Proxy 类**: 提供了用于创建动态代理实例的方法。 - **InvocationHandler 接口**: 定义了一个处理方法调用的回调接口,通过该接口可以自定义代理行为。 当客户端调用代理对象上的某个方法时,实际执行的是由 InvocationHandler 处理逻辑所指定的操作[^3]。 #### 3. JDK 原生动态代理实现 JDK 动态代理基于反射技术,在运行期间为一组接口动态生成代理类及其实例。具体流程如下: - 创建一个实现了 `InvocationHandler` 接口的对象。 - 使用 `Proxy.newProxyInstance()` 方法传入目标类加载器、目标类实现的一组接口列表以及上述 handler 对象,从而获得代理实例。 下面是一个简单的示例代码展示如何利用 JDK 动态代理实现基本的日志打印功能: ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 定义服务接口 interface GreetingService { void sayHello(String name); } // 实现服务接口的具体业务逻辑 class SimpleGreetingServiceImpl implements GreetingService { @Override public void sayHello(String name) { System.out.println("Hello, " + name); } } // 自定义 InvocationHandler 来拦截并扩展方法调用 class LoggingInvocationHandler implements InvocationHandler { private Object target; // 被代理的目标对象 public LoggingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("[Before] Executing: " + method.getName()); // 执行原始方法 Object result = method.invoke(target, args); System.out.println("[After] Finished execution."); return result; } } public class JdkDynamicProxyDemo { public static void main(String[] args) { // 初始化真实的服务实现 GreetingService greetingService = new SimpleGreetingServiceImpl(); // 构建带有日志功能的代理对象 GreetingService proxyInstance = (GreetingService) Proxy.newProxyInstance( greetingService.getClass().getClassLoader(), greetingService.getClass().getInterfaces(), new LoggingInvocationHandler(greetingService)); // 测试代理效果 proxyInstance.sayHello("World"); } } ``` 此程序展示了如何通过动态代理增加额外的功能而不修改原有代码结构[^1]。 #### 4. CGLib 动态代理实现 除了 JDK 内置支持外,还可以借助第三方库如 CGLib 进行动态代理操作。CGLib 主要针对那些未继承任何接口或者希望直接操控实体类场景下更为适用。它的内部工作机理涉及字节码层面操作,能够子类化任意非 final 的普通类,并重写其中的方法达到相同目的。 需要注意的是,由于两者设计初衷不同,在性能表现上可能会有所差异;通常情况下,如果仅需对接口做封装,则推荐优先考虑更轻量级也更加直观易懂的 JDK 方案。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值