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. 执行流程
- 调用代理对象的
sing()或dance()方法; - JVM自动将调用转发给
InvocationHandler的invoke()方法; - 在
invoke()中执行前置增强逻辑(经纪人工作); - 通过反射
method.invoke(star, args)调用目标对象的核心方法; - 返回目标方法的结果给调用者。
3. 关键注意点
- 动态代理只能代理接口,不能直接代理类(如果需要代理类,可使用CGLIB框架);
- 代理对象和目标对象实现了相同的接口,因此调用者可以无缝切换使用代理对象或目标对象;
- 增强逻辑与核心业务解耦,无需修改目标对象代码,符合「开闭原则」。
五、动态代理的实际应用场景
除了案例中的「经纪人模式」,动态代理在Java开发中还有很多经典应用:
- 日志记录:拦截方法调用,记录方法入参、出参、执行时间;
- 权限控制:调用方法前验证用户权限,无权限则拦截;
- 事务管理 :Spring框架中,通过动态代理实现声明式事务(
@Transactional); - 缓存增强:方法调用前先查询缓存,无缓存再执行方法并缓存结果;
- 性能监控:统计方法执行耗时,分析系统瓶颈。
六、为什么是"动态"代理?
"动态"体现在两个关键点:
- 动态生成:在运行时(不是编译时)生成代理类的字节码,不需要提前编写代理类。
- 动态绑定:可以在运行时指定目标对象和增强逻辑,更加灵活。
七、总结
本文通过「明星+经纪人」的生动案例,带你掌握了Java JDK动态代理的实现方式和核心原理:
- 动态代理的核心是「运行时生成代理对象」,无需手动编写代理类;
- 核心依赖
Proxy类和InvocationHandler接口,通过反射实现方法拦截; - 优势是解耦、灵活、可复用,能在不修改目标对象的前提下增强功能。
如果你的项目中需要为多个对象添加相同的辅助功能(如日志、权限),动态代理绝对是高效的解决方案。赶紧把这个案例跑起来,动手实践一下吧!
重要提示:动态代理是Spring AOP(面向切面编程)的基础,理解了动态代理,你就能更好地理解Spring框架的底层实现。