Spring
动态代理时提供了 JDK
代理和 CGLIB
两种方式,一般而言,代理的目标是接口时 AOP
使用 JDK
代理来实现,CGLIB
则负责对类进行代理,两种代理方法结合使用。
一、JDK代理代码实现
package com.nineya.spring.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface JdkTargetInterface {
String test(String str);
}
class JdkTargetObject implements JdkTargetInterface {
@Override
public String test(String str) {
System.out.println("被代理函数执行:" + str);
return "被代理函数的返回结果";
}
}
class JdkProxyHandler implements InvocationHandler {
private Object target;
public JdkProxyHandler() {
}
public JdkProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理函数执行:" + method);
if (target == null) {
return "代理函数的返回结果";
}
return method.invoke(target, args);
}
}
public class JdkProxyMain {
private static void proxyInterface() {
// 创建代理类
JdkTargetInterface proxyTargetInterface = (JdkTargetInterface) Proxy.newProxyInstance(
JdkProxyMain.class.getClassLoader(), new Class[]{JdkTargetInterface.class}, new JdkProxyHandler());
String result = proxyTargetInterface.test("这是请求参数");
System.out.println("执行结果:" + result);
}
private static void proxyObject() {
// 被代理对象
JdkTargetObject targetObject = new JdkTargetObject();
// 创建代理类
JdkTargetInterface proxyTargetInterface = (JdkTargetInterface) Proxy.newProxyInstance(
JdkProxyMain.class.getClassLoader(), new Class[]{JdkTargetInterface.class}, new JdkProxyHandler(targetObject));
String result = proxyTargetInterface.test("这是请求参数");
System.out.println("执行结果:" + result);
}
public static void main(String[] args) {
System.out.println("## Jdk 代理接口");
proxyInterface();
System.out.println("## Jdk 代理对象");
proxyObject();
}
}
二、CGLIB代理代码实现
package com.nineya.spring.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
interface CglibTargetInterface {
String test(String str);
}
class CglibTargetObject {
public String test(String str) {
System.out.println("被代理函数执行:" + str);
return "被代理函数的返回结果";
}
}
/**
* cglib代理处理类
*/
class CglibProxyHandler implements MethodInterceptor {
private Object target;
public CglibProxyHandler() {
}
public CglibProxyHandler(Object target){
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理函数执行:" + method);
if (target == null) {
return "代理函数的返回结果";
}
return methodProxy.invoke(target, objects);
}
}
public class CglibProxyMain {
private static void proxyObject() {
// 被代理对象
CglibTargetObject targetObject = new CglibTargetObject();
Enhancer enhancer = new Enhancer();
// 被代理的类
enhancer.setSuperclass(CglibTargetObject.class);
// cglib代理处理类
enhancer.setCallback(new CglibProxyHandler(targetObject));
// 取得代理后的对象
CglibTargetObject proxyTargetObject = (CglibTargetObject) enhancer.create();
String result = proxyTargetObject.test("这是请求参数");
System.out.println("执行结果:" + result);
}
private static void proxyInterface() {
Enhancer enhancer = new Enhancer();
// 被代理的类
enhancer.setSuperclass(CglibTargetInterface.class);
// cglib代理处理类
enhancer.setCallback(new CglibProxyHandler());
// 取得代理后的对象
CglibTargetInterface proxyTargetObject = (CglibTargetInterface) enhancer.create();
String result = proxyTargetObject.test("这是请求参数");
System.out.println("执行结果:" + result);
}
public static void main(String[] args) {
System.out.println("## CGLIB 代理接口");
proxyInterface();
System.out.println("## CGLIB 代理对象");
proxyObject();
}
}
三、对比
-
CGLIB
需要导入第三方依赖包,JDK
代理可以直接实现。 -
通过观察上面两份代码可以看到,
CGLIB
可以直接对接口和类进行代理,而JDK
代理方式则只能对接口进行处理,要代理的实现类也必须要实现接口。JDK
代理如果对非接口类进行处理将抛出异常。Exception in thread "main" java.lang.IllegalArgumentException: com.nineya.spring.proxy.JdkTargetObject is not an interface at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590) at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557) at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230) at java.lang.reflect.WeakCache.get(WeakCache.java:127) at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419) at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719) at com.nineya.spring.proxy.JdkProxyMain.proxyInterface(JdkProxyMain.java:43) at com.nineya.spring.proxy.JdkProxyMain.main(JdkProxyMain.java:63)
CGLIB
的目标类如果被final
关键字修饰也会抛出异常,这是由于CGLIB
通过创建目标类的子类来实现直接对目标类的代理。不直接对类进行代理的JDK
代理方式不受影响。Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class com.nineya.spring.proxy.CglibTargetObject at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:660) at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:358) at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:585) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108) at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134) at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572) at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:387) at com.nineya.spring.proxy.CglibProxyMain.proxyObject(CglibProxyMain.java:53) at com.nineya.spring.proxy.CglibProxyMain.main(CglibProxyMain.java:75)
-
CGLIB
性能优于JDK
代理。基于上述的代码,删除控制台打印命令,分别对
CGLIB
和JDK
两种代理方式执行 5 组,每组循环调用一千万次代理方法的任务。for (int i = 0; i < 5; i++) { long start = System.currentTimeMillis(); for (int j = 0; j < 10000000; j++) { proxyInterface(); proxyObject(); } long end = System.currentTimeMillis(); System.out.println("耗时:" + (end - start)); }
统计得到的耗时结果如下:
# CGLIB 输出 耗时:7409 耗时:4137 耗时:4211 耗时:4146 耗时:4018 # JDK 输出 耗时:10784 耗时:9181 耗时:9586 耗时:9325 耗时:9111