Java 动态代理详解

1. 引言

在 Java 开发中,代理模式是一种常见的设计模式,它可以为对象提供额外的功能,例如权限控制、日志记录、事务管理等。Java 提供了两种代理方式:静态代理和动态代理。本文重点讨论动态代理的概念、实现方式及其应用场景。

2. Java 代理模式概述

代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过代理对象访问目标对象。代理对象可以在不修改目标对象的情况下,增强其功能。

静态代理 是在编译期就确定代理类,需要为每个目标类编写代理类,代码重复度较高。

动态代理 则是在运行时动态生成代理对象,减少代码冗余,提高灵活性。

3. Java 动态代理的实现方式

Java 提供了两种主要的动态代理实现方式:

  • JDK 动态代理(基于 java.lang.reflect.Proxy
  • CGLIB 动态代理(基于字节码增强)

3.1 JDK 动态代理

JDK 提供了 Proxy 类和 InvocationHandler 接口来实现动态代理。其核心思想是创建一个实现目标接口的代理类,并在方法调用时,使用 InvocationHandler 进行方法拦截。

3.1.1 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 目标接口
interface Service {
void serve();
}

// 目标类
class RealService implements Service {
@Override
public void serve() {
System.out.println("Executing real service...");
}
}

// 代理处理器
class ServiceInvocationHandler implements InvocationHandler {
private final Object target;

public ServiceInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoking method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After invoking method: " + method.getName());
return result;
}
}

// 测试代理
public class JDKProxyDemo {
public static void main(String[] args) {
Service realService = new RealService();
Service proxyInstance = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new ServiceInvocationHandler(realService)
);
proxyInstance.serve();
}
}

3.1.2 运行结果

1
2
3
Before invoking method: serve
Executing real service...
After invoking method: serve

3.2 CGLIB 动态代理

JDK 动态代理要求目标类必须实现接口,而 CGLIB 通过继承目标类并生成子类的方式进行代理,因此可以代理没有实现接口的类。

3.2.1 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 目标类(没有实现接口)
class RealServiceCGLIB {
public void serve() {
System.out.println("Executing real service...");
}
}

// CGLIB 代理工厂
class CGLIBProxy implements MethodInterceptor {
private final Object target;

public CGLIBProxy(Object target) {
this.target = target;
}

public Object createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before invoking method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After invoking method: " + method.getName());
return result;
}
}

// 测试代理
public class CGLIBProxyDemo {
public static void main(String[] args) {
RealServiceCGLIB realService = new RealServiceCGLIB();
RealServiceCGLIB proxyInstance = (RealServiceCGLIB) new CGLIBProxy(realService).createProxy();
proxyInstance.serve();
}
}

3.2.2 运行结果

1
2
3
Before invoking method: serve
Executing real service...
After invoking method: serve

4. Java 动态代理的应用场景

  1. AOP(面向切面编程):如 Spring 框架的事务管理、日志记录等。
  2. RPC(远程过程调用):动态代理用于拦截调用并执行远程调用。
  3. 权限控制:在访问目标方法前进行权限验证。
  4. 缓存:在调用目标方法前检查缓存,提高性能。

5. JDK 动态代理与 CGLIB 代理的对比

特性 JDK 动态代理 CGLIB 动态代理
是否需要接口
代理方式 生成接口实现类 继承目标类
性能 略慢 较快
Spring 默认选择 是(如果有接口) 否(无接口时)

6. 总结

Java 动态代理是实现代理模式的重要方式,JDK 动态代理适用于接口代理,而 CGLIB 代理适用于类代理。在实际开发中,应根据需求选择合适的动态代理方式。例如,在 Spring AOP 中,默认使用 JDK 动态代理,但如果目标类没有实现接口,则使用 CGLIB 代理。

了解并灵活运用 Java 动态代理,有助于提升代码的复用性和可维护性,在实际项目中带来更高的开发效率。