一、引言

在 Java 开发中,代理模式是一种常用的设计模式,它能在不修改原始类代码的前提下对方法进行增强。而 JDK 动态代理作为 JDK 原生支持的代理实现方式,在框架开发和业务增强中有着广泛应用。本文将通过一个完整案例,带大家掌握 JDK 动态代理的实现原理与使用方法。

二、什么是jdk动态代理

JDK 动态代理是 Java 语言自带的代理机制,它基于反射实现,允许在运行时动态生成代理类。其核心特点包括:

  • 必须基于接口实现(这是与 CGLIB 代理的主要区别)
  • 通过java.lang.reflect.Proxy类动态生成代理对象
  • 通过InvocationHandler接口定义方法增强逻辑
  • 代理类在程序运行时动态生成,无需手动编写

动态代理的典型应用场景有:日志记录、性能监控、事务管理、权限控制等,它能有效降低代码耦合度,符合开闭原则。

三、实战案例:明星代理系统

下面我们通过一个 "明星代理" 案例来理解 JDK 动态代理的实现过程。场景需求:明星(目标对象)的演出活动需要经纪人(代理对象)提前做准备工作(方法增强)。

1. 定义接口(核心前提)

JDK 动态代理必须基于接口,我们首先定义明星的行为接口Star

package com.heaboy.proxy;

public interface Star {
    String sing();  // 唱歌方法
    void dance();   // 跳舞方法
}

2. 实现目标类

创建实现Star接口的具体明星类MyStar,这是我们需要增强的目标对象:

package com.heaboy.proxy;

public class MyStar implements Star {

    private String name;
    
    // 构造方法初始化明星姓名
    public MyStar(String name) {
        this.name = name;
    }
    
    @Override
    public String sing() {
        System.out.println(name + "开始唱歌");
        return "谢谢大家的掌声";
    }
    
    @Override
    public void dance() {
        System.out.println(name + "开始跳舞");
    }
}

3. 编写代理工具类

这是实现动态代理的核心部分,我们需要通过Proxy类创建代理对象,并通过InvocationHandler定义增强逻辑:

package com.heaboy.proxy;

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

public class StarProxyUtil {
    
    /**
     * 创建明星代理对象
     * @param target 目标明星对象
     * @return 代理对象
     */
    public static Star createProxy(MyStar target) {
        // 1. 获取类加载器(通常使用当前类的类加载器)
        ClassLoader loader = StarProxyUtil.class.getClassLoader();
        
        // 2. 指定代理对象要实现的接口(与目标对象相同)
        Class<?>[] interfaces = new Class[]{Star.class};
        
        // 3. 定义代理逻辑(InvocationHandler匿名实现类)
        InvocationHandler handler = new InvocationHandler() {
            /**
             * 代理对象的方法执行时会调用此方法
             * @param proxy 代理对象本身(一般不使用)
             * @param method 目标方法
             * @param args 方法参数
             * @return 目标方法的返回值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 方法增强:根据不同方法执行不同的前置操作
                if (method.getName().equals("sing")) {
                    System.out.println("经纪人:调试音响设备");
                } else if (method.getName().equals("dance")) {
                    System.out.println("经纪人:准备舞蹈场地");
                }
                
                // 调用目标对象的原始方法
                Object result = method.invoke(target, args);
                
                // 可在此处添加后置增强逻辑(如演出收尾工作)
                return result;
            }
        };
        
        // 4. 动态生成并返回代理对象
        return (Star) Proxy.newProxyInstance(loader, interfaces, handler);
    }
}

4. 测试代理效果

编写测试类验证动态代理的执行结果:

package com.heaboy.proxy;

public class Test {
    public static void main(String[] args) {
        // 1. 创建目标对象
        MyStar myStar = new MyStar("杨超越");
        
        // 2. 获取代理对象(经纪人)
        Star proxy = StarProxyUtil.createProxy(myStar);
        
        // 3. 通过代理对象调用方法
        proxy.dance();  // 调用跳舞方法
        String response = proxy.sing();  // 调用唱歌方法
        System.out.println(response);
    }
}

执行结果:

三、JDK 动态代理核心原理分析

  1. 代理对象的生成Proxy.newProxyInstance()方法在运行时动态生成一个实现了指定接口的代理类字节码,并通过类加载器加载生成代理对象。

  2. 方法调用机制:当调用代理对象的方法时,不会直接执行目标方法,而是先调用InvocationHandlerinvoke()方法,在该方法中我们可以:

    • 执行前置增强逻辑(如权限校验、资源准备)
    • 通过method.invoke(target, args)调用目标对象的原始方法
    • 执行后置增强逻辑(如日志记录、事务提交)
  3. 为什么必须基于接口:JDK 动态代理生成的代理类已经继承了Proxy类,而 Java 是单继承机制,因此只能通过实现接口的方式生成代理对象。

四、总结与扩展

本文通过明星代理案例详细讲解了 JDK 动态代理的实现过程,核心要点包括:

  • 必须定义接口,目标类实现该接口
  • 通过Proxy.newProxyInstance()生成代理对象
  • 通过InvocationHandler实现方法增强逻辑

在实际开发中,Spring AOP 的底层实现就大量使用了 JDK 动态代理(当目标对象实现接口时)。掌握动态代理思想,能帮助我们更好地理解框架原理,写出更灵活、低耦合的代码。

如果需要代理没有实现接口的类,可以考虑 CGLIB 代理(基于继承实现),大家可以对比学习两种代理方式的异同点,在实际项目中根据场景选择合适的方案。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐