Java动态代理实战:手把手教你实现明星经纪人模式

Java动态代理实战:手把手教你实现明星经纪人模式

在日常开发中,我们经常遇到这样的场景:某个对象需要专注于核心业务,而一些非核心的辅助工作(如前置准备、后续处理)希望交给其他对象来完成。这时候,代理模式就派上了用场。今天我们就通过一个「明星+经纪人」的生动案例,带你吃透Java动态代理的实现原理和使用场景。

一、场景引入:为什么明星需要经纪人?

想象一下:明星的核心工作是唱歌、跳舞等表演,但在表演前需要准备场地、收取报酬,这些杂事如果让明星自己处理,会分散其精力。这时候就需要一个「经纪人」(代理对象),帮明星处理这些辅助工作,明星只需要专注于表演本身。

对应到Java开发中:

  • 明星 = 目标对象(专注核心业务)
  • 经纪人 = 代理对象(处理辅助工作,拦截核心方法)
  • 唱歌/跳舞 = 目标方法(需要被代理的核心行为)

这就是代理模式的核心思想:职责分离、功能增强,在不修改目标对象代码的前提下,为其添加额外功能。

二、动态代理 vs 静态代理

在Java中,代理分为「静态代理」和「动态代理」:

  • 静态代理:需要手动为每个目标类编写代理类,实现目标接口,代码冗余,灵活性差。
  • 动态代理:无需手动编写代理类,在运行时动态生成代理对象,只需指定目标接口和增强逻辑,灵活高效。

本文重点讲解JDK自带的动态代理(基于接口实现),也是开发中最常用的动态代理方式。

三、代码实现:一步步搭建明星经纪人系统

1. 项目结构

先看一下整体项目结构,清晰明了:

arduino 复制代码
com.wmh.demo4proxy
 StarService.java       // 明星行为接口(定义需要代理的方法)
 Star.java              // 明星类(目标对象,实现核心业务)
 ProxyUtil.java         // 代理工具类(创建动态代理对象)
 Test.java              // 测试类(验证代理效果)

2. 第一步:定义行为接口(StarService)

动态代理基于接口实现,我们首先定义明星的核心行为接口,明确需要被代理的方法:

arduino 复制代码
package com.wmh.demo4proxy;

// 明星行为接口:定义代理需要实现的方法
public interface StarService {
    // 唱歌方法(带参数)
    void sing(String name);
    // 跳舞方法(有返回值)
    String dance();
}

3. 第二步:实现目标对象(Star类)

明星类实现StarService接口,专注于核心业务逻辑(唱歌、跳舞),不关心任何辅助工作:

typescript 复制代码
package com.wmh.demo4proxy;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

// Lombok注解:自动生成getter、setter、构造方法,简化代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Star implements StarService {
    private String name; // 明星姓名

    // 核心业务:唱歌
    @Override
    public void sing(String name) {
        System.out.println(this.name + "表演唱歌" + name);
    }

    // 核心业务:跳舞
    @Override
    public String dance() {
        System.out.println(this.name + "表演跳舞:魅力四射!");
        return "谢谢!谢谢! ";
    }
}

4. 第三步:编写代理工具类(ProxyUtil)

这是动态代理的核心!我们通过java.lang.reflect.Proxy类创建代理对象,通过InvocationHandler接口定义代理逻辑(辅助工作)。

java 复制代码
package com.wmh.demo4proxy;

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

/**
 * 代理工具类 = 经纪人公司:负责创建代理对象,处理辅助工作
 */
public class ProxyUtil {

    // 为明星创建代理对象(参数:目标对象Star,返回:代理对象StarService)
    public static StarService createProxy(Star star) {
        /**
         * Proxy.newProxyInstance():生成代理对象的核心方法,3个关键参数
         * 参数1:类加载器(用当前工具类的类加载器加载代理类)
         * 参数2:目标对象实现的接口(代理类需要实现和目标对象相同的接口,保证方法一致)
         * 参数3:InvocationHandler(代理逻辑处理器,拦截目标方法并添加额外功能)
         */
        StarService proxy = (StarService) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(), // 类加载器
                star.getClass().getInterfaces(),  // 目标对象的接口
                new InvocationHandler() {         // 代理逻辑
                    /**
                     * invoke():代理对象的方法被调用时,会自动触发该方法
                     * 参数1:proxy:代理对象本身(一般不用)
                     * 参数2:method:当前被调用的目标方法(如sing、dance)
                     * 参数3:args:当前方法的参数(如sing的歌曲名)
                     * 返回值:目标方法的返回值(需返回给调用者)
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 1. 代理的辅助工作(前置增强)
                        String methodName = method.getName(); // 获取当前调用的方法名
                        if ("sing".equals(methodName)) {
                            System.out.println("准备话筒,收钱20万!");
                        } else if ("dance".equals(methodName)) {
                            System.out.println("准备场地,收钱100万!");
                        }

                        // 2. 调用目标对象的核心方法(明星真正干活)
                        Object result = method.invoke(star, args); // 反射调用目标方法

                        return result; // 返回目标方法的结果
                    }
                }
        );
        return proxy;
    }
}

5. 第四步:测试代理效果(Test类)

创建明星对象和代理对象,调用代理方法,验证是否触发代理逻辑:

java 复制代码
package com.wmh.demo4proxy;

public class Test {
    public static void main(String[] args) {
        // 1. 创建目标对象:明星张三
        Star star = new Star("张三");

        // 2. 创建代理对象:为张三分配经纪人
        StarService proxy = ProxyUtil.createProxy(star);

        // 3. 调用代理对象的方法(间接调用明星的核心方法)
        proxy.sing("《浪漫手机》");
        System.out.println(proxy.dance());
    }
}

6. 运行结果

复制代码
准备话筒,收钱20万!
张三表演唱歌《浪漫手机》
准备场地,收钱100万!
张三表演跳舞:魅力四射!
谢谢!谢谢! 

完美!代理对象成功拦截了目标方法,先执行经纪人的辅助工作(收钱、准备),再调用明星的核心业务,最后返回结果。

四、动态代理核心原理拆解

1. 核心类和接口

  • Proxy类:JDK提供的代理工厂类,newProxyInstance()方法在运行时动态生成代理类的字节码,并创建代理对象。
  • InvocationHandler接口:代理逻辑的核心,invoke()方法会在代理对象的方法被调用时自动触发,负责拦截目标方法并添加增强逻辑。

2. 执行流程

  1. 调用代理对象的sing()dance()方法;
  2. JVM自动将调用转发给InvocationHandlerinvoke()方法;
  3. invoke()中执行前置增强逻辑(经纪人工作);
  4. 通过反射method.invoke(star, args)调用目标对象的核心方法;
  5. 返回目标方法的结果给调用者。

3. 关键注意点

  • 动态代理只能代理接口,不能直接代理类(如果需要代理类,可使用CGLIB框架);
  • 代理对象和目标对象实现了相同的接口,因此调用者可以无缝切换使用代理对象或目标对象;
  • 增强逻辑与核心业务解耦,无需修改目标对象代码,符合「开闭原则」。

五、动态代理的实际应用场景

除了案例中的「经纪人模式」,动态代理在Java开发中还有很多经典应用:

  1. 日志记录:拦截方法调用,记录方法入参、出参、执行时间;
  2. 权限控制:调用方法前验证用户权限,无权限则拦截;
  3. 事务管理 :Spring框架中,通过动态代理实现声明式事务(@Transactional);
  4. 缓存增强:方法调用前先查询缓存,无缓存再执行方法并缓存结果;
  5. 性能监控:统计方法执行耗时,分析系统瓶颈。

六、为什么是"动态"代理?

"动态"体现在两个关键点:

  1. 动态生成:在运行时(不是编译时)生成代理类的字节码,不需要提前编写代理类。
  2. 动态绑定:可以在运行时指定目标对象和增强逻辑,更加灵活。

七、总结

本文通过「明星+经纪人」的生动案例,带你掌握了Java JDK动态代理的实现方式和核心原理:

  • 动态代理的核心是「运行时生成代理对象」,无需手动编写代理类;
  • 核心依赖Proxy类和InvocationHandler接口,通过反射实现方法拦截;
  • 优势是解耦、灵活、可复用,能在不修改目标对象的前提下增强功能。

如果你的项目中需要为多个对象添加相同的辅助功能(如日志、权限),动态代理绝对是高效的解决方案。赶紧把这个案例跑起来,动手实践一下吧!

重要提示:动态代理是Spring AOP(面向切面编程)的基础,理解了动态代理,你就能更好地理解Spring框架的底层实现。

相关推荐
代码笔耕2 小时前
面向对象开发实践之消息中心设计(四)--- 面向变化的定力
java·设计模式·架构
唐叔在学习2 小时前
buildozer打包详解:细说那些我踩过的坑
android·后端·python
Wang15302 小时前
c++与Java谁的性能更胜一筹
java·c++
学习3人组2 小时前
Conda虚拟环境迁移指南导出依赖库并跨设备重建环境
java·数据库·conda
清晓粼溪2 小时前
SpringCloud-04-Circuit Breaker断路器
后端·spring·spring cloud
woniu_maggie2 小时前
SAP导入WPS编辑的Excel文件报错处理
后端
Seven972 小时前
剑指offer-56、删除链表中重复的节点
java