Java · #java#reflection#dynamic-proxy#cglib

Java反射与动态代理深入解析

2025.02.19 Java 9 min 3.5k
// 目录 · contents

引言

反射(Reflection)和动态代理(Dynamic Proxy)是Java语言中最强大的元编程特性,也是Spring、MyBatis、Hibernate等主流框架的底层基石。反射让程序可以在运行时检查和操作类的结构,动态代理则允许在不修改目标代码的前提下对方法调用进行拦截和增强。

本文将系统性地讲解反射API的核心用法、JDK动态代理与CGLIB代理的实现原理,并深入分析Spring AOP中的代理选择策略。

反射的核心概念

graph TD
    ClassLoader[ClassLoader 加载字节码] --> ClassObj[Class 对象]
    ClassObj --> Fields[Field 字段]
    ClassObj --> Methods[Method 方法]
    ClassObj --> Constructors[Constructor 构造器]
    ClassObj --> Annotations[Annotation 注解]
    ClassObj --> Interfaces[Interface 接口]
    ClassObj --> SuperClass[SuperClass 父类]

    Fields --> GetSet[get/set 字段值]
    Methods --> Invoke[invoke 调用方法]
    Constructors --> NewInstance[newInstance 创建实例]

    style ClassObj fill:#e3f2fd

Java反射的一切操作都围绕java.lang.Class对象展开。每个类在JVM中有且仅有一个Class对象,它包含了该类的完整元信息。

反射API详解

获取Class对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ReflectionBasics {

public static void main(String[] args) throws Exception {
// 方式1: 通过类名直接获取
Class<String> clazz1 = String.class;

// 方式2: 通过实例的getClass()
String str = "hello";
Class<? extends String> clazz2 = str.getClass();

// 方式3: 通过全限定类名动态加载(最常用于框架)
Class<?> clazz3 = Class.forName("java.lang.String");

// 三种方式获取的是同一个Class对象
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz2 == clazz3); // true
}
}

操作Field、Method、Constructor

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class ReflectionAdvanced {

static class User {
private Long id;
private String name;
private int age;

private User() {}

public User(Long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}

private String formatInfo() {
return String.format("User{id=%d, name='%s', age=%d}", id, name, age);
}

public String getName() { return name; }
}

public static void main(String[] args) throws Exception {
Class<User> clazz = User.class;

// ===== Constructor操作 =====
// 获取私有无参构造器
Constructor<User> privateConstructor = clazz.getDeclaredConstructor();
privateConstructor.setAccessible(true); // 突破访问权限
User user = privateConstructor.newInstance();

// 获取公有带参构造器
Constructor<User> publicConstructor = clazz.getDeclaredConstructor(
Long.class, String.class, int.class);
User user2 = publicConstructor.newInstance(1L, "张三", 28);

// ===== Field操作 =====
// 获取私有字段并设值
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(user, "李四");
System.out.println(nameField.get(user)); // "李四"

// 遍历所有字段
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
System.out.printf("字段名: %s, 类型: %s, 值: %s%n",
field.getName(),
field.getType().getSimpleName(),
field.get(user2));
}

// ===== Method操作 =====
// 调用私有方法
Method formatInfo = clazz.getDeclaredMethod("formatInfo");
formatInfo.setAccessible(true);
String info = (String) formatInfo.invoke(user2);
System.out.println(info); // User{id=1, name='张三', age=28}

// 调用带参方法
Method getName = clazz.getMethod("getName");
String name = (String) getName.invoke(user2);
System.out.println(name); // "张三"
}
}

实用工具:通用Bean拷贝器

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
47
48
49
50
51
52
53
54
/**
* 基于反射的通用Bean属性拷贝工具
* 将源对象的同名同类型字段值拷贝到目标对象
*/
public class BeanCopier {

private static final Map<Class<?>, Map<String, Field>> FIELD_CACHE =
new ConcurrentHashMap<>();

public static <S, T> T copyProperties(S source, Class<T> targetClass) {
try {
T target = targetClass.getDeclaredConstructor().newInstance();
copyProperties(source, target);
return target;
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Bean拷贝失败", e);
}
}

public static void copyProperties(Object source, Object target) {
Map<String, Field> sourceFields = getFields(source.getClass());
Map<String, Field> targetFields = getFields(target.getClass());

for (Map.Entry<String, Field> entry : sourceFields.entrySet()) {
String fieldName = entry.getKey();
Field sourceField = entry.getValue();
Field targetField = targetFields.get(fieldName);

if (targetField != null
&& targetField.getType().isAssignableFrom(sourceField.getType())) {
try {
targetField.set(target, sourceField.get(source));
} catch (IllegalAccessException e) {
throw new RuntimeException("字段赋值失败: " + fieldName, e);
}
}
}
}

private static Map<String, Field> getFields(Class<?> clazz) {
return FIELD_CACHE.computeIfAbsent(clazz, c -> {
Map<String, Field> fieldMap = new LinkedHashMap<>();
Class<?> current = c;
while (current != null && current != Object.class) {
for (Field field : current.getDeclaredFields()) {
field.setAccessible(true);
fieldMap.putIfAbsent(field.getName(), field);
}
current = current.getSuperclass();
}
return fieldMap;
});
}
}

JDK动态代理

JDK动态代理是Java标准库提供的代理机制,它要求目标对象必须实现接口。代理对象会实现相同的接口,并将方法调用委托给InvocationHandler

sequenceDiagram
    participant Client as 调用方
    participant Proxy as 代理对象 $Proxy0
    participant Handler as InvocationHandler
    participant Target as 目标对象

    Client->>Proxy: 调用接口方法
    Proxy->>Handler: invoke(proxy, method, args)
    Handler->>Handler: 前置增强(日志/鉴权等)
    Handler->>Target: method.invoke(target, args)
    Target-->>Handler: 返回结果
    Handler->>Handler: 后置增强(日志/异常处理等)
    Handler-->>Proxy: 返回最终结果
    Proxy-->>Client: 返回结果

JDK动态代理实现

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 定义接口
public interface UserService {
User findById(Long id);
List<User> findAll();
void save(User user);
}

// 实现类
public class UserServiceImpl implements UserService {
@Override
public User findById(Long id) {
// 模拟数据库查询
return new User(id, "张三", 28);
}

@Override
public List<User> findAll() {
return List.of(new User(1L, "张三", 28), new User(2L, "李四", 35));
}

@Override
public void save(User user) {
System.out.println("保存用户: " + user);
}
}

// InvocationHandler实现
public class LoggingInvocationHandler implements InvocationHandler {

private final Object target;
private static final Logger log = LoggerFactory.getLogger(LoggingInvocationHandler.class);

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

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();
String params = args == null ? "" : Arrays.toString(args);

log.info("[BEFORE] {} 参数: {}", methodName, params);
long start = System.nanoTime();

try {
Object result = method.invoke(target, args);
long elapsed = (System.nanoTime() - start) / 1_000_000;
log.info("[AFTER] {} 耗时: {}ms 返回: {}", methodName, elapsed, result);
return result;
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
log.error("[ERROR] {} 异常: {}", methodName, cause.getMessage());
throw cause;
}
}
}

// 创建代理
public class ProxyFactory {

@SuppressWarnings("unchecked")
public static <T> T createJdkProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LoggingInvocationHandler(target)
);
}

public static void main(String[] args) {
UserService realService = new UserServiceImpl();
UserService proxyService = createJdkProxy(realService);

// 通过代理调用方法,会自动添加日志
User user = proxyService.findById(1L);
// 输出: [BEFORE] UserService.findById 参数: [1]
// 输出: [AFTER] UserService.findById 耗时: 2ms 返回: User{id=1, name='张三', age=28}
}
}

CGLIB代理

CGLIB(Code Generation Library)通过生成目标类的子类来实现代理,因此不要求目标类实现接口,但无法代理final类和final方法。

graph TD
    subgraph JDK动态代理
        Interface[接口定义] --> ProxyClass["$Proxy0 实现接口"]
        ProxyClass --> IH[InvocationHandler]
        IH --> TargetA[目标对象]
    end

    subgraph CGLIB代理
        TargetClass[目标类] --> SubClass["Target$$EnhancerByCGLIB 子类"]
        SubClass --> MI[MethodInterceptor]
        MI --> TargetB[目标对象<br/>通过super调用]
    end

    style Interface fill:#e3f2fd
    style TargetClass fill:#fff3e0

CGLIB代理实现

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 目标类,不需要实现接口
public class OrderService {

public Order createOrder(String productName, int quantity) {
Order order = new Order();
order.setOrderNo(UUID.randomUUID().toString());
order.setProductName(productName);
order.setQuantity(quantity);
System.out.println("创建订单: " + order.getOrderNo());
return order;
}

// final方法不会被代理
public final String getServiceName() {
return "OrderService";
}
}

// CGLIB MethodInterceptor
public class TransactionInterceptor implements MethodInterceptor {

private static final Logger log = LoggerFactory.getLogger(TransactionInterceptor.class);

/**
* @param obj 代理对象实例(CGLIB生成的子类实例)
* @param method 被拦截的方法
* @param args 方法参数
* @param proxy 方法代理,用于调用父类方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
log.info("[TX] 开启事务");
try {
// invokeSuper调用父类(原始类)的方法,不会再次触发拦截
Object result = proxy.invokeSuper(obj, args);
log.info("[TX] 提交事务");
return result;
} catch (Exception e) {
log.error("[TX] 回滚事务, 原因: {}", e.getMessage());
throw e;
}
}
}

// 创建CGLIB代理
public class CglibProxyFactory {

@SuppressWarnings("unchecked")
public static <T> T createCglibProxy(Class<T> targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(new TransactionInterceptor());
return (T) enhancer.create();
}

public static void main(String[] args) {
OrderService proxy = createCglibProxy(OrderService.class);

// 普通方法会被拦截
proxy.createOrder("iPhone", 1);
// 输出: [TX] 开启事务
// 输出: 创建订单: xxx-xxx
// 输出: [TX] 提交事务

// final方法不会被拦截,直接调用原始方法
String name = proxy.getServiceName(); // "OrderService"
}
}

多个拦截器的CallbackFilter

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
public class MultiCallbackExample {

public static <T> T createProxy(Class<T> targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);

// 定义多个回调
Callback[] callbacks = {
new TransactionInterceptor(), // index 0: 事务拦截
new LoggingInterceptor(), // index 1: 日志拦截
NoOp.INSTANCE // index 2: 不拦截
};
enhancer.setCallbacks(callbacks);

// 根据方法选择不同的回调
enhancer.setCallbackFilter(method -> {
if (method.isAnnotationPresent(Transactional.class)) {
return 0; // 使用事务拦截器
} else if (method.getName().startsWith("find")
|| method.getName().startsWith("get")) {
return 1; // 使用日志拦截器
}
return 2; // 不拦截
});

return (T) enhancer.create();
}
}

Spring AOP中的代理选择

Spring AOP会根据目标对象的特征自动选择代理策略:

flowchart TD
    Start[Spring创建Bean] --> HasInterface{目标类是否<br/>实现了接口?}

    HasInterface -->|是| CheckConfig{proxyTargetClass<br/>=true?}
    HasInterface -->|否| CGLIB[使用CGLIB代理]

    CheckConfig -->|否| JDK[使用JDK动态代理]
    CheckConfig -->|是| CGLIB

    JDK --> ProxyObj1[代理对象<br/>实现相同接口]
    CGLIB --> IsFinal{目标类是final?}

    IsFinal -->|否| ProxyObj2[代理对象<br/>目标类的子类]
    IsFinal -->|是| Error[抛出异常<br/>无法代理]

    style JDK fill:#e3f2fd
    style CGLIB fill:#fff3e0
    style Error fill:#ffcdd2
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
// Spring Boot中的代理配置
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB
public class AopConfig {
}

// 自定义切面
@Aspect
@Component
public class PerformanceAspect {

private static final Logger log = LoggerFactory.getLogger(PerformanceAspect.class);

@Around("@annotation(com.example.annotation.Monitored)")
public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable {
String signature = joinPoint.getSignature().toShortString();
long start = System.nanoTime();

try {
Object result = joinPoint.proceed();
long elapsed = (System.nanoTime() - start) / 1_000_000;
log.info("{} 执行成功, 耗时 {}ms", signature, elapsed);
return result;
} catch (Throwable e) {
long elapsed = (System.nanoTime() - start) / 1_000_000;
log.error("{} 执行失败, 耗时 {}ms, 异常: {}", signature, elapsed, e.getMessage());
throw e;
}
}
}

性能对比

反射和动态代理相比直接调用有性能开销,以下是关键数据和优化建议:

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
47
48
49
50
51
52
53
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 10, time = 1)
@Fork(2)
public class ProxyBenchmark {

private UserService directService;
private UserService jdkProxy;
private UserService cglibProxy;
private Method cachedMethod;

@Setup
public void setup() throws Exception {
directService = new UserServiceImpl();

jdkProxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
(proxy, method, args) -> method.invoke(directService, args)
);

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) ->
methodProxy.invokeSuper(obj, args));
cglibProxy = (UserService) enhancer.create();

cachedMethod = UserService.class.getMethod("findById", Long.class);
cachedMethod.setAccessible(true);
}

@Benchmark
public User directCall() {
return directService.findById(1L);
}

@Benchmark
public User jdkProxyCall() {
return jdkProxy.findById(1L);
}

@Benchmark
public User cglibProxyCall() {
return cglibProxy.findById(1L);
}

@Benchmark
public User reflectionCall() throws Exception {
return (User) cachedMethod.invoke(directService, 1L);
}
}

典型性能对比(数据仅供参考):

调用方式 平均耗时 (ns) 相对开销
直接调用 ~5 1x
反射调用(缓存Method) ~20 4x
JDK动态代理 ~25 5x
CGLIB代理 ~15 3x
反射调用(未缓存) ~500 100x

性能优化建议

  1. 缓存反射对象Class.forName()getMethod()getDeclaredField()等调用代价高,结果应缓存复用。
  2. 使用setAccessible(true):跳过访问权限检查可以显著提升反射调用速度。
  3. 优先使用MethodHandle:Java 7引入的MethodHandle在JIT优化后性能接近直接调用。
  4. CGLIB的MethodProxy.invokeSuper:比Method.invoke快,因为它直接调用生成的子类方法,避免了反射。
  5. 生产环境考虑编译时增强:如Lombok的@Delegate、MapStruct等编译时代码生成方案,零运行时开销。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// MethodHandle示例:JIT优化后性能接近直接调用
public class MethodHandleExample {

private static final MethodHandle FIND_BY_ID;

static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
FIND_BY_ID = lookup.findVirtual(
UserServiceImpl.class,
"findById",
MethodType.methodType(User.class, Long.class)
);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}

public static User callViaMethodHandle(UserServiceImpl service, Long id) throws Throwable {
return (User) FIND_BY_ID.invoke(service, id);
}
}

总结

反射和动态代理是Java元编程的两大核心能力:

  1. 反射API提供了运行时类信息访问和动态调用的能力,是框架开发的基础。核心操作围绕ClassFieldMethodConstructor四个类展开。

  2. JDK动态代理基于接口生成代理类,通过InvocationHandler拦截方法调用。它是Java标准库的一部分,无需额外依赖,但要求目标对象实现接口。

  3. CGLIB代理通过字节码生成目标类的子类实现代理,不要求接口但无法代理final类/方法。Spring默认在Boot 2.x+中使用CGLIB作为AOP代理。

  4. 性能方面,缓存反射对象、使用setAccessible(true)、考虑MethodHandle替代是关键优化手段。在性能极敏感的场景,编译时增强方案(如APT、字节码插桩)是更好的选择。

  5. Spring AOP的代理选择遵循明确的规则:有接口默认JDK代理,无接口或配置proxyTargetClass=true时使用CGLIB。理解这一机制有助于排查自注入、类型转换等常见问题。

作者 · authorzt
发布 · date2025-02-19
篇幅 · length3.5k 字 · 9 min
许可 · licenseCC BY-SA 4.0
$ echo "comments" · 评论