Java 代理模式深度解析:从静态到动态的实现与原理
目录
一、引言
二、静态代理:手动实现的基础形式
1. 定义业务接口与实现类
2. 创建代理类
3. 组合使用代理类
4. 优缺点分析
三、JDK 动态代理:基于接口的运行时代理
1. 核心实现
定义 InvocationHandler
客户端调用
2. 核心原理
代理类结构分析
模拟 JDK 动态代理实现
3. 优缺点
四、CGLIB 动态代理:基于继承的字节码增强
1. 核心实现
定义 MethodInterceptor
客户端调用
2. 核心原理
字节码增强流程
关键类解析
模拟 CGLIB 动态代理实现
3. 优缺点
五、代理模式对比与选择
六、Java动态代理的各种实现方式
一、引言
在 Java 开发中,代理模式(Proxy Pattern)是一种常用的设计模式。它通过引入中间层(代理类)来控制对目标对象的访问,允许在不修改原始代码的前提下添加额外功能。当我们遇到以下场景时,代理模式往往能发挥关键作用:
- 在业务方法前后添加日志、监控等通用逻辑
- 实现业务代码与通用功能的解耦
- 处理仅有接口而无具体实现的场景(如 MyBatis Mapper)
- 理解框架底层动态代理实现(如 Spring AOP)
本文将通过具体案例,深入探讨 Java 代理模式的实现方式与核心原理,并结合代码进行详细解析。
二、静态代理:手动实现的基础形式
静态代理通过手动编写代理类实现功能增强,是代理模式的基础形式。以下是其核心实现步骤:
1. 定义业务接口与实现类
// MyService接口
public interface MyService {
void doTask();
}
// MyServiceImpl实现类
public class MyServiceImpl implements MyService {
@Override
public void doTask() {
System.out.println("Task doing");
try { Thread.sleep(10); } catch (InterruptedException e) { /* ... */ }
System.out.println("Task done");
}
}
2. 创建代理类
// 日志代理类
public class MyServiceLogProxy implements MyService {
private final MyService target;
public MyServiceLogProxy(MyService target) { this.target = target; }
@Override public void doTask() {
System.out.println("----do task log start----");
target.doTask();
System.out.println("----do task log end---");
}
}
// 监控代理类
public class MyServiceMonitorProxy implements MyService {
private final MyService target;
public MyServiceMonitorProxy(MyService target) { this.target = target; }
@Override public void doTask() {
long start = System.currentTimeMillis();
target.doTask();
System.out.printf("Cost: %d ms%n", System.currentTimeMillis() - start);
}
}
3. 组合使用代理类
// 客户端代码
public class MyServiceClient {
public static void main(String[] args) {
MyService service = new MyServiceImpl();
MyService proxied = new MyServiceLogProxy(
new MyServiceMonitorProxy(service)
);
proxied.doTask();
}
}
4. 优缺点分析
- 优点:实现简单,易于理解
- 缺点:
- 代理类与接口强绑定,无法复用
- 无法处理无接口的场景
三、JDK 动态代理:基于接口的运行时代理
JDK 动态代理通过反射机制在运行时生成代理类,是 Spring AOP 的默认实现方式。
1. 核心实现
定义 InvocationHandler
// 日志处理器
public class JDKDynamicLogProxy implements InvocationHandler {
private Object target;
public JDKDynamicLogProxy(Object target) {
this.target = target;
}
/**
* 获取被代理接口实例对象
* @param
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----do task log start----");
Object result = method.invoke(target, args);
System.out.println("----do task log end---");
return result;
}
}
//JDK动态代理-监控
public class JDKDynamicMonitorProxy implements InvocationHandler {
private Object target;
public JDKDynamicMonitorProxy(Object target) {
this.target = target;
}
/**
* 获取被代理接口实例对象
* @param
* @return
*/
public <T> T getProxy() {
/*
Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
Class[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
*/
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("do task start time:" + start);
// 调用业务方法
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("do task end time:" + end);
System.out.println("do task cost " + (end - start) + "ms");
return result;
}
}
客户端调用
// JDKDynamicClient
public class JDKDynamicClient {
public static void main(String[] args) {
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
MyService service = new MyServiceImpl();
MyService proxied = new JDKDynamicLogProxy(service).getProxy();
proxied.doTask();
}
}
2. 核心原理
JDK 动态代理的核心在于java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
的配合。当调用代理对象的方法时,实际执行的是InvocationHandler
的invoke
方法。其核心流程如下:
- 字节码生成:通过
Proxy.newProxyInstance
生成动态代理类字节码,该类会实现目标接口。 - 方法拦截:代理类的所有方法调用都会委派给
InvocationHandler
的invoke
方法。 - 反射调用:在
invoke
方法中,通过反射调用目标对象的真实方法。
代理类结构分析
生成的代理类(如$Proxy0
)会实现目标接口,并持有InvocationHandler
实例:
public final class $Proxy0 implements MyService {
private final InvocationHandler h;
public $Proxy0(InvocationHandler h) { this.h = h; }
public void doTask() {
h.invoke(this, MyService.class.getMethod("doTask"), null);
}
}
模拟 JDK 动态代理实现
代理逻辑的入口
public interface InvocationHandler {
void invoke(Object o, Method m);
}
public class MonitorHandler implements InvocationHandler {
private Object target;
public MonitorHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
long start = System.currentTimeMillis();
System.out.println("do task start time:" + start);
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("do task end time:" + end);
System.out.println("do task cost " + (end - start) + "ms");
}
}
模拟JDK动态代理类
public class Proxy {
public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
String methodStr = "";
Method[] methods = infce.getMethods();
for (Method m : methods) {
methodStr += " @Override\n" +
" public void " + m.getName() + "() {\n" +
" try {\n" +
" Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");\n" +
" h.invoke(this, md);\n" +
" } catch(Exception e) {\n" +
" e.printStackTrace();\n" +
" }\n" +
" }\n";
}
String sourceCode = "package cn.dmlove.proxy;\n" +
"\n" +
"import java.lang.reflect.Method;\n" +
"import cn.dmlove.demo3.InvocationHandler;\n" +
"\n" +
"public class $JDKDynamicProxy1 implements " + infce.getName() + "{\n" +
"\n" +
" private InvocationHandler h;\n" +
"\n" +
" public $JDKDynamicProxy1(InvocationHandler h) {\n" +
" this.h = h;\n" +
" }\n" +
"\n" +
methodStr +
"}";
// 生成java源码文件
String javaFileName = System.getProperty("user.dir") + "/src/main/java/cn/dmlove/proxy/$JDKDynamicProxy1.java";
File javaFile = new File(javaFileName);
FileWriter fileWriter = new FileWriter(javaFile);
fileWriter.write(sourceCode);
fileWriter.flush();
fileWriter.close();
// 获取编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 获取java文件管理器
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// 要编译的java文件
Iterable javaFileObjects = fileManager.getJavaFileObjects(javaFile);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 指定class文件目录
String flag = "-d";
String outDir = "";
try {
File classPath = new File(classLoader.getResource("").toURI());
outDir = classPath.getAbsolutePath() + File.separator;
} catch (URISyntaxException e1) {
e1.printStackTrace();
}
Iterable options = Arrays.asList(flag, outDir);
// 获取编译任务
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, javaFileObjects);
// 执行编译任务
task.call();
// 关闭java文件管理器
fileManager.close();
// class文件load到内存
Class clazz = classLoader.loadClass("cn.dmlove.proxy.$JDKDynamicProxy1");
Constructor cons = clazz.getConstructor(InvocationHandler.class);
return cons.newInstance(h);
}
}
public class $JDKDynamicProxy1 implements cn.dmlove.MyService {
private InvocationHandler h;
public $JDKDynamicProxy1(InvocationHandler h) {
this.h = h;
}
@Override
public void doTask() {
try {
Method md = cn.dmlove.MyService.class.getMethod("doTask");
h.invoke(this, md);
} catch(Exception e) {
e.printStackTrace();
}
}
}
模拟JDK动态代理调用
public class Client {
public static void main(String[] args) throws Exception {
MyService myService = new MyServiceImpl();
InvocationHandler h = new MonitorHandler(myService);
MyService m = (MyService)Proxy.newProxyInstance(MyService.class, h);
m.doTask();
}
}
运行结果:
生成代理类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import cn.dmlove.MyService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements MyService {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void doTask() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("cn.dmlove.MyService").getMethod("doTask");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
3. 优缺点
- 优点:
- 完全运行时生成,无需手动编码
- 支持接口方法拦截
- 缺点:
- 仅支持接口代理
- 反射调用存在性能损耗
四、CGLIB 动态代理:基于继承的字节码增强
CGLIB 通过字节码生成技术为类创建子类,实现对非接口类的代理。
1. 核心实现
定义 MethodInterceptor
/**
* cglib动态监控代理
*/
public class CglibMonitorProxy implements MethodInterceptor {
private Object target;
public final Object getInstance(final Object target) {
this.target = target;
//cglib 中字节码加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
// 设置要创建动态代理的类
enhancer.setSuperclass(this.target.getClass());
// 设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截
enhancer.setCallback(this);
// 返回实例
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("do task start time:" + start);
Object result = methodProxy.invokeSuper(object, args);
long end = System.currentTimeMillis();
System.out.println("do task end time:" + end);
System.out.println("do task cost " + (end - start) + "ms");
return result;
}
客户端调用
public class CglibProxyClient {
public static void main(String[] args) {
//获取代理
MyServiceImpl myServiceImplProxy = (MyServiceImpl) new CglibMonitorProxy().getInstance(new MyServiceImpl());
// 执行
myServiceImplProxy.doTask();
}
}
2. 核心原理
字节码增强流程
- 子类生成:CGLIB 通过
Enhancer
生成目标类的子类 - 方法拦截:重写父类方法,通过
MethodInterceptor
实现逻辑增强 - 快速调用:使用
MethodProxy
优化方法调用,避免反射开销(文档 3-59)
关键类解析
- Enhancer:字节码增强器,用于设置父类和回调
- MethodInterceptor:定义拦截逻辑,类似 JDK 的
InvocationHandler
- MethodProxy:缓存方法调用,提升性能
模拟 CGLIB 动态代理实现
import java.lang.reflect.Method;
// 目标类
class TargetClass {
public void doSomething() {
System.out.println("Target is doing something.");
}
}
// 方法拦截器接口
interface MethodInterceptor {
Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}
// 方法代理类
class MethodProxy {
private Method method;
public MethodProxy(Method method) {
this.method = method;
}
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
return method.invoke(obj, args);
}
}
// 模拟 Enhancer 类,用于生成代理类
class Enhancer {
private Class<?> superclass;
private MethodInterceptor callback;
public void setSuperclass(Class<?> superclass) {
this.superclass = superclass;
}
public void setCallback(MethodInterceptor callback) {
this.callback = callback;
}
public Object create() {
try {
// 这里模拟生成代理类,实际上 CGLIB 会动态生成字节码
// 这里简单使用反射创建目标类的实例
TargetClass target = (TargetClass) superclass.getDeclaredConstructor().newInstance();
return new ProxyClass(target, callback);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 模拟生成的代理类
private static class ProxyClass extends TargetClass {
private TargetClass target;
private MethodInterceptor callback;
public ProxyClass(TargetClass target, MethodInterceptor callback) {
this.target = target;
this.callback = callback;
}
@Override
public void doSomething() {
try {
Method method = TargetClass.class.getMethod("doSomething");
MethodProxy proxy = new MethodProxy(method);
callback.intercept(this, method, new Object[0], proxy);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// 测试代码
public class SimulateCglibProxy {
public static void main(String[] args) {
// 创建 Enhancer 实例
Enhancer enhancer = new Enhancer();
// 设置目标类
enhancer.setSuperclass(TargetClass.class);
// 设置方法拦截器
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method execution.");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method execution.");
return result;
}
});
// 创建代理对象
TargetClass proxy = (TargetClass) enhancer.create();
// 调用代理对象的方法
proxy.doSomething();
}
}
3. 优缺点
- 优点:
- 支持非接口类代理
- 性能优于 JDK 代理
- 缺点:
- 无法代理
final
类 / 方法 - 大量使用可能导致 PermGen 内存溢出
- 无法代理
五、代理模式对比与选择
特性 | 静态代理 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|---|
代理机制 | 手动实现 | 接口实现 | 子类继承 |
生成时机 | 编译期 | 运行期 | 运行期 |
性能 | 高 | 中(反射调用) | 高(字节码优化) |
灵活性 | 低 | 中 | 高 |
适用场景 | 简单功能扩展 | 接口类代理 | 非接口类代理 |