在 Java 编程中,动态代理是一种常用的设计模式。它允许我们在运行时动态地创建一个对象的代理,并通过代理对象来访问目标对象。这样,我们就可以在不修改目标对象代码的情况下,为目标对象添加额外的功能。

目前,Java 中常用的动态代理方式有两种:JDK 动态代理和 CGLIB 动态代理。这两种动态代理方式有一些区别,本文将对它们进行简要介绍,并给出一些简单的代码演示案例。

JDK 动态代理

JDK 动态代理是 Java 自带的一种动态代理方式。它通过实现 java.lang.reflect.InvocationHandler 接口来创建自定义的调用处理器,并通过 java.lang.reflect.Proxy 类来创建动态代理对象。

JDK 动态代理的一个重要特点是,它只能对实现了接口的类生成代理。也就是说,如果一个类没有实现任何接口,那么我们就无法使用 JDK 动态代理来为它创建代理对象。

下面是一个简单的 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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Hello {
void sayHello();
}

class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello, World!");
}
}

class MyInvocationHandler implements InvocationHandler {
private Object target;

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

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

public class JdkProxyDemo {
public static void main(String[] args) {
Hello hello = new HelloImpl();
MyInvocationHandler handler = new MyInvocationHandler(hello);
Hello proxy = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
handler);
proxy.sayHello();
}
}

上面的代码中,我们定义了一个 Hello 接口和它的实现类 HelloImpl。然后我们创建了一个自定义的调用处理器 MyInvocationHandler,并在其中实现了 invoke 方法。最后,我们使用 Proxy.newProxyInstance 方法创建了一个 Hello 接口的动态代理对象,并通过该代理对象调用了 sayHello 方法。

运行上面的代码,你会看到如下输出:

1
2
3
Before method call
Hello, World!
After method call

可以看到,在调用 sayHello 方法时,我们成功地在方法调用前后添加了额外的逻辑。

CGLIB 动态代理

CGLIB(Code Generation Library)是一个第三方的动态代理库,它通过继承目标类并生成子类的方式来实现动态代理。

与 JDK 动态代理不同,CGLIB 可以对任意类(无论是否实现了接口)生成代理。但是,由于 CGLIB 是通过继承来实现动态代理的,所以它无法对 final 类或方法进行代理。

下面是一个简单的 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
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

class Hello {
public void sayHello() {
System.out.println("Hello, World!");
}
}

class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method call");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method call");
return result;
}
}

public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
enhancer.setCallback(new MyMethodInterceptor());
Hello proxy = (Hello) enhancer.create();
proxy.sayHello();
}
}

上面的代码中,我们定义了一个普通类 Hello,并在其中实现了 sayHello 方法。然后我们创建了一个自定义的方法拦截器 MyMethodInterceptor,并在其中实现了 intercept 方法。最后,我们使用 Enhancer 类创建了一个 Hello 类的动态代理对象,并通过该代理对象调用了 sayHello 方法。

运行上面的代码,你会看到如下输出:

1
2
3
Before method call
Hello, World!
After method call

可以看到,在调用 sayHello 方法时,我们成功地在方法调用前后添加了额外的逻辑。

总结

  • 如果目标类实现了接口,那么可以使用 JDK 动态代理。

  • 如果目标类没有实现接口,或者需要对类本身进行代理(而不是对接口进行代理),那么可以使用 CGLIB 动态代理。

希望本文能够帮助读者更好地了解JDK和CGLIB动态代理的区别。