这篇文章,主要解决以下几个问题:
在实际的开发中,我们可能会遇到这种问题,如:现在有一个订单的服务接口,以及其实现类如下:
/**
* 订单服务
*/
public interface OrderService{
/**
* 创建订单
* @throws InterruptedException
*/
void createOrder() throws InterruptedException;
}
/**
* 订单服务实现
*/
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder() {
System.out.println("创建订单");
}
}
我们现在有新的需求,需要知道这个服务中createOrder方法的执行时间,我们会怎么做呢,最简单最容易的想法就是去修改createOrder方法,或修改调用createOrder方法的代码,在调用createOrder前后增加一些逻辑。然而这样需要修改源代码,不符合开闭原则。
我们可以通过静态代理的方式去实现这个需求,具体做法如下:
前三步实现如下:
public class OrderServiceProxy implements OrderService {
/**
* 代理对象持有被代理对象的引用
*/
private OrderService orderService;
private Long startTime = 0L;
/**
* 创建代理对象的时候,传入被代理的对象
* @param orderService
*/
public OrderServiceProxy(OrderService orderService){
this.orderService = orderService;
}
@Override
public void createOrder() {
beforeTime("createOrder");
//调用被代理对象的方法
orderService.createOrder();
afterTime("createOrder");
}
/**
* 方法调用之前执行
* @param methodName
*/
public void beforeTime(String methodName){
startTime = System.currentTimeMillis();
System.out.println(methodName + "开始执行时间:" + startTime);
}
/**
* 方法调用之后执行
* @param methodName
*/
public void afterTime(String methodName){
System.out.println(methodName + "执行总共用时:" + (System.currentTimeMillis() - startTime));
}
}
public class StaticProxyTest {
public static void main(String[] args) throws Exception{
OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
orderService.createOrder();
}
}
这样就完成了,程序执行createOrder的时候,调用过程如下:
这样就起到了对被代理类OrderServiceImpl功能的增强。
执行结果如下:
(1)如果OrderService中增加新的功能,如增加queryOrder方法,此时,代理类也需要手动实现新增加的方法。如下:
/**
* 订单服务
*/
public interface OrderService{
/**
* 创建订单
* @throws InterruptedException
*/
void createOrder();
/**
* 查询订单
*/
void queryOrder();
}
public class OrderServiceProxy implements OrderService {
/**
* 代理对象持有被代理对象的引用
*/
private OrderService orderService;
private Long startTime = 0L;
/**
* 创建代理对象的时候,传入被代理的对象
* @param orderService
*/
public OrderServiceProxy(OrderService orderService){
this.orderService = orderService;
}
@Override
public void createOrder() {
beforeTime("createOrder");
//调用被代理对象的方法
orderService.createOrder();
afterTime("createOrder");
}
@Override
public void queryOrder() {
orderService.createOrder();
}
/**
* 方法调用之前执行
* @param methodName
*/
public void beforeTime(String methodName){
startTime = System.currentTimeMillis();
System.out.println(methodName + "开始执行时间:" + startTime);
}
/**
* 方法调用之后执行
* @param methodName
*/
public void afterTime(String methodName){
System.out.println(methodName + "执行总共用时" + (System.currentTimeMillis() - startTime));
}
}
(2)现在不仅仅有订单服务,还有支付服务,同样支付服务业需要知道支付过程中方法的性能,需要打印时间。此时我们需要为支付服务手动增加一个代理类,在这个代理类中手动实现所有的支付接口,并手动为每个方法增加before和after方法打印方法的执行时间。如下支付接口:
/**
* 支付服务
*/
public interface PaymentService {
/**
* 支付
*/
void pay();
}
/**
* 支付服务实现
*/
public class PaymentServiceImpl implements PaymentService {
@Override
public void pay() {
System.out.println("支付");
}
}
显然,OrderServiceProxy是不能够代理支付的服务,因为OrderServiceProxy中持有的是订单服务OrderService的引用。
所以,需要为 PaymentService创建一个自己的代理类 PaymentServiceProxy,实现 PaymentService接口,并持有 PaymentService接口引用,如下:
public class PaymentServiceProxy implements PaymentService {
private PaymentService paymentService;
private Long startTime = 0L;
public PaymentServiceProxy(PaymentService paymentService){
this.paymentService = paymentService;
}
@Override
public void pay() {
beforeTime("pay");
paymentService.pay();
afterTime("pay");
}
/**
* 方法调用之前执行
* @param methodName
*/
public void beforeTime(String methodName){
startTime = System.currentTimeMillis();
System.out.println(methodName + "开始执行时间:" + startTime);
}
/**
* 方法调用之后执行
* @param methodName
*/
public void afterTime(String methodName){
System.out.println(methodName + "执行总共用时" + (System.currentTimeMillis() - startTime));
}
}
这样,每次需要对一个服务进行功能增强,都需要手动为这个服务增加一个属于自己的代理类,这样会导致类的数量增多,增加系统的复杂程度,不易于维护。
前面我们知道,静态代理是存在两个问题的:
动态代理能够很好的解决这两个问题,目前动态代理有两种方式,JDK动态代理和CGLib动态代理。我们先看看Jdk动态代理是如何使用的:
首先,需要实现自己的InvocationHandler,这里的InvocationHandler是对被代理对象和增强功能的封装,jdk动态创建的代理类对象会持有InvocationHandler的引用,具体实现如下:
public class JdkInvocationHandler implements InvocationHandler {
/**
* 被代理的对象
*/
private Object object;
public JdkInvocationHandler(Object object){
this.object = object;
}
/**
* 这个方法由代理类调用,此时代理类还没有被创建,是在程序执行的时候动态创建的
* @param proxy 代理类引用,代理类调用此方法的时候传递自身引用this
* @param method 要执行被代理类的哪个方法,这个是代理类获取被代理类接口中的Method作为参数传来的。
* jdk动态创建的代理类和被代理类会实现相同的接口,所以这个Method对象很容易获得
* @param args 要执行被代理类的方法的参数列表
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用前做一些事情,before");
Object result = method.invoke(object, args);
System.out.println("调用后做一些事情,after");
return result;
}
}
InvocationHandler中有一个方法invoke,invoke供代理类调用,有三个参数:
参数1:代理类引用,代理类调用此方法的时候传递自身引用this。
参数2:要执行被代理类的哪个方法,invoke中通过method.invoke(object, args)调用被代理对象object的相应方法。
参数3:要执行方法的参数列表。
动态创建代理对象,以及调用的过程如下:
public class JdkProxyTest {
public static void main(String[] args) {
//创建代理对象
ClassLoader classLoader = JdkProxyTest.class.getClassLoader();
OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(
OrderService.class.getClassLoader(),
OrderServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new OrderServiceImpl()));
orderServiceProxy.createOrder();
}
}
通过调用Proxy.newProxyInstance()动态创建代理类对象,newProxyInstance有三个参数:
参数1:类加载器,运行的时候动态创建的代理类会通过这个类加载器进行加载。
参数2:被代理类实现的接口列表,将来被代理类也会实现这些接口。
参数3:前面实现的InvocationHandler对象,将来被创建的代理类会持有这个对象的引用,用于调用InvocationHandler中的invoke方法。
到这里,Jdk动态代理就写好了,jdk动态代理看似神秘,其实原理很简单:
具体调用过程如下:
为了能更清楚的看清楚jdk动态代理的真实面目,下面,我们将jdk动态生成的代理类class字节码写到文件中,通过反编译工具进行反编译,看看动态生成的代理类类是什么样的。
输出动态代理类class代码如下:
public class DynamicProxyTest {
public static void main(String[] args) throws Exception{
byte[] bytes = ProxyGenerator.generateProxyClass("Proxy0", new Class[]{OrderService1.class});
FileOutputStream os = new FileOutputStream("d:\\install\\jad\\Proxy.class");
os.write(bytes);
os.close();
}
}
对Proxy.class文件进行反编译结果如下:
public final class Proxy0 extends Proxy implements OrderService1 {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
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 createOrder() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void createOrder(int var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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("blog.designpatterns.proxy.OrderService1").getMethod("createOrder");
m4 = Class.forName("blog.designpatterns.proxy.OrderService1").getMethod("createOrder", Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
很清楚就能看到,动态生成的代理类:
动态代理能够很好的解决静态代理的两个问题,原因是:
jdk动态代理还有两个细节:
这里是仿照jdk的动态代理,实现自己的动态代理,旨在更进一步的了解jdk动态代理的原理。
要仿照jdk动态代理实现自己的动态代理,主要分为两步:
和jdk的InvocationHandler一样定义,具体如下:
public interface MyInvocationHandler {
Object invoke(Object proxy, Method method, Object[] args);
}
动态创建代理类对象可以拆分为以下几个步骤:
四个步骤定义如下:
public class MyProxy {
public static Object newProxyInstance(MyClassLoader classLoader, Class[] interfaces, MyInvocationHandler invocationHandler) throws Exception {
//1、代码构建Java程序,可以是文本文件,可以只是字符串,这个组装为一个字符串
String proxyJavaSource = buildProxyJavaSource(interfaces);
//2、编译java程序,生成Class byte数组
byte[] classBytes = compailerJavaSource(proxyJavaSource);
//3、通过构造器加载Class
Class proxyClass = loadClass(classLoader, classBytes);
//4、创建代理对象
Object proxy = createdProxyInstance(proxyClass, invocationHandler);
return proxy;
}
}
下面就具体实现这四个步骤
构建java代码,其实就是通过程序去写程序,过程很简单,只需要注意一些实现细节即可。这里写法很捞,具体实现如下:
/**
* 代码构建Java程序,可以是文本文件,可以只是字符串,这个组装为一个字符串
* @param interfaces 被代理类实现的接口
* @return java源程序字符串
*/
private static String buildProxyJavaSource(Class[] interfaces) {
StringBuilder builder = new StringBuilder();
builder.append("package blog.designpatterns.proxy.dynamicproxy.mydynamicproxy;\n");
//包导入
builder.append("import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.MyInvocationHandler;\n");
builder.append("import java.lang.reflect.Method;\n");
for (Class ainterface : interfaces) {
builder.append("import " + ainterface.getName() + ";\n");
}
//实现接口
builder.append("public class Proxy0 implements ");
for (Class ainterface : interfaces) {
builder.append(" " + ainterface.getSimpleName() + ",");
}
builder.deleteCharAt(builder.length() - 1);
builder.append("{\n");
//方法变量
int index = 0;
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
Method[] declaredMethods = anInterface.getDeclaredMethods();
for (int j = 0; j < declaredMethods.length; j++) {
builder.append("private static Method m" + index + ";\n");
index ++;
}
}
//静态代码块
index = 0;
builder.append("static {\n");
builder.append("try{\n");
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
Method[] declaredMethods = anInterface.getDeclaredMethods();
for (int j = 0; j < declaredMethods.length; j++) {
Method method = declaredMethods[j];
builder.append("m" + index + " = Class.forName(\""+anInterface.getName()+"\").getMethod(\""+method.getName()+"\",");
for (Class> parameterType : method.getParameterTypes()) {
builder.append(parameterType.getName() + ".class,");
}
builder.deleteCharAt(builder.length() - 1);
builder.append(");\n");
index ++;
}
}
builder.append("} catch (Exception e){ \n");
builder.append("throw new RuntimeException(e);\n");
builder.append("}\n");
builder.append("}\n");
//handler和构造函数
builder.append("private MyInvocationHandler myInvocationHandler;\n");
builder.append("public Proxy0(MyInvocationHandler myInvocationHandler) throws Exception {\n");
builder.append("this.myInvocationHandler = myInvocationHandler;\n");
builder.append(" }\n");
//重写方法
index = 0;
for (Class ainterface : interfaces) {
Method[] declaredMethods = ainterface.getDeclaredMethods();
for (Method method : declaredMethods){
builder.append("@Override\n");
builder.append("public ");
builder.append(method.getReturnType().getName() + " ");
builder.append(method.getName() + "( ");
Class>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
builder.append(parameterTypes[i].getName() + " var" + i + ",");
}
if (parameterTypes.length > 0){
builder.deleteCharAt(builder.length() - 1);
}
builder.append("){\n");
builder.append("try{\n");
//调用handler的invoke方法
if (method.getReturnType().getName().equals("void")){
builder.append("myInvocationHandler.invoke(this, m" + index+ ",");
}else {
builder.append("Object result = myInvocationHandler.invoke(this, m" + index+ ",");
}
if (parameterTypes.length == 0){
builder.append("null");
}else {
builder.append("new Object[]{");
for (int i = 0; i < parameterTypes.length; i++) {
builder.append(" var" + i + ",");
}
builder.deleteCharAt(builder.length() - 1);
builder.append("}");
}
builder.append(");\n");
if (!method.getReturnType().getName().equals("void")){
builder.append("return ("+method.getReturnType().getName()+")result;\n");
}
builder.append("} catch (Exception e){ \n");
builder.append("throw new RuntimeException(e);\n");
builder.append("}\n");
builder.append("}\n");
index ++;
}
}
builder.append("}\n");
return builder.toString();
}
定义测试OrderService接口如下:
/**
* 订单服务
*/
public interface OrderService {
/**
* 创建订单
* @throws InterruptedException
*/
void createOrder();
/**
* 查询订单
*/
String createOrder(int a);
}
执行上面的方法构建代理类java源程序结果如下:
package blog.designpatterns.proxy.dynamicproxy.mydynamicproxy;
import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.MyInvocationHandler;
import java.lang.reflect.Method;
import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService;
public class Proxy0 implements OrderService{
private static Method m0;
private static Method m1;
static {
try{
m0 = Class.forName("blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService").getMethod("createOrder");
m1 = Class.forName("blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService").getMethod("createOrder",int.class);
} catch (Exception e){
throw new RuntimeException(e);
}
}
private MyInvocationHandler myInvocationHandler;
public Proxy0(MyInvocationHandler myInvocationHandler) throws Exception {
this.myInvocationHandler = myInvocationHandler;
}
@Override
public void createOrder( ){
try{
myInvocationHandler.invoke(this, m0,null);
} catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public java.lang.String createOrder( int var0){
try{
Object result = myInvocationHandler.invoke(this, m1,new Object[]{ var0});
return (java.lang.String)result;
} catch (Exception e){
throw new RuntimeException(e);
}
}
}
编译上面写好的java程序字符串。编译使用jdk提供的tools进行编译,细节不讲,直接上代码:
/**
* 编译java程序,生成Class byte数组
* @param proxyJavaSource java源程序
* @return 编译后的class byte数组
*/
private static byte[] compailerJavaSource(String proxyJavaSource) throws Exception {
String name = "Proxy0";
URI uri = URI.create("string:///" + name.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension);
MyJavaFile javafile = new MyJavaFile(uri, JavaFileObject.Kind.SOURCE);
javafile.setContent(proxyJavaSource);
List javaFileObjects = Lists.newArrayList(javafile);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
MyJavaFileManager myJavaFileManager = new MyJavaFileManager(
compiler.getStandardFileManager(null, null, null));
JavaCompiler.CompilationTask task = compiler.getTask(
null, myJavaFileManager, null, null, null, javaFileObjects);
task.call();
MyJavaFile javaFile = myJavaFileManager.getByteArrayJavaFileObjects().get(0);
ByteArrayOutputStream outputStream = (ByteArrayOutputStream) javaFile.openOutputStream();
return outputStream.toByteArray();
}
编译过程中需要定义自己的JavaFile和JavaFileManager:
JavaFile:
public class MyJavaFile extends SimpleJavaFileObject {
private String content;
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
public MyJavaFile(URI uri, Kind kind) {
super(uri, kind);
}
public void setContent(String content){
this.content = content;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return content;
}
@Override
public OutputStream openOutputStream() throws IOException {
return outputStream;
}
}
JavaFileManager:
public class MyJavaFileManager extends ForwardingJavaFileManager {
/**
* Creates a new instance of ForwardingJavaFileManager.
*
* @param fileManager delegate to this file manager
*/
public MyJavaFileManager(JavaFileManager fileManager) {
super(fileManager);
}
private Set javaFiles = new HashSet<>();
public Set getByteArrayJavaFileObjects() {
return javaFiles;
}
// 有字节码的输出的时候 我们自定义一个JavaFileObject 来接受输出
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
if (JavaFileObject.Kind.CLASS == kind) {
MyJavaFile javaFile = new MyJavaFile(URI.create("bytes:///" + className.replace(".", "/") + ".class"), kind);
javaFiles.add(javaFile);
return javaFile;
} else {
return super.getJavaFileForOutput(location, className, kind, sibling);
}
}
}
将java源程序编译成class字节码数组后,接下来就要对编译好的class进行加载:
/**
* 通过构造器加载Class
* @param classLoader 自定义类加载器
* @param classBytes class byte数组
* @return Class对象
*/
private static Class loadClass(MyClassLoader classLoader, byte[] classBytes) throws Exception {
Class> clazz = classLoader.myLoadClass(classBytes);
return clazz;
}
这里需要定义自己的类加载器,简单实现一下:
public class MyClassLoader extends ClassLoader {
public Class> myLoadClass(byte[] bytes) throws ClassNotFoundException {
return defineClass(bytes, 0, bytes.length);
}
}
最后就是创建代理类对象了:
/**
* 创建代理对象
* @param proxyClass
* @param invocationHandler
* @return
*/
private static Object createdProxyInstance(Class proxyClass, MyInvocationHandler invocationHandler) throws Exception {
Constructor constructor = proxyClass.getDeclaredConstructor(MyInvocationHandler.class);
constructor.setAccessible(true);
Object proxy = constructor.newInstance(invocationHandler);
return proxy;
}
到这里就成功实现了自己的动态代理,下面测试一下功能如何:
定义两个测试接口:
/**
* 订单服务
*/
public interface OrderService {
/**
* 创建订单
* @throws InterruptedException
*/
void createOrder();
/**
* 查询订单
*/
String createOrder(int a);
}
/**
* 支付服务
*/
public interface PaymentService {
/**
* 支付
*/
void pay();
}
定义一个实现类,同时实现这两个接口:
/**
* 订单服务
*/
public class OrderServiceImpl implements OrderService, PaymentService {
@Override
public void createOrder() {
System.out.println("创建订单");
}
@Override
public String createOrder(int a) {
System.out.println("接受到参数 :a = " + a);
return "返回结果数据";
}
@Override
public void pay() {
System.out.println("开始支付了");
}
}
实现自己的InvocationHandler:
public class MyInvocationHandlerImpl implements MyInvocationHandler {
private Object object;
public MyInvocationHandlerImpl(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
try {
System.out.println("开始调用了");
Object invoke = method.invoke(object, args);
System.out.println("调用结束了");
return invoke;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
具体测试如下,发现创建动态代理类的过程是不是和Jdk的很类似。
public class MyProxyTest {
public static void main(String[] args) throws Exception {
//动态创建自己的代理类
OrderService orderService = (OrderService) MyProxy.newProxyInstance(
new MyClassLoader(),
OrderServiceImpl.class.getInterfaces(),
new MyInvocationHandlerImpl(new OrderServiceImpl()));
//调用createOrder
orderService.createOrder();
//调用带参数的createOrder
String result = orderService.createOrder(3);
System.out.println(result);
//再次动态创建代理类
PaymentService paymentService = (PaymentService) MyProxy.newProxyInstance(new MyClassLoader(),
OrderServiceImpl.class.getInterfaces(), new MyInvocationHandlerImpl(new OrderServiceImpl()));
//调用pay
paymentService.pay();
}
}
测试结果如下:
结果也符合预期,到这里就完成了自己动态代理类的实现。
先看看CgLib动态代理是如何使用的:
首先要实现MethodInterceptor接口
public class CglibPrintTimeProxyInterceptor implements MethodInterceptor {
/**
*
* @param obj 被代理对象
* @param method 要执行的方法
* @param args 要执行的方法参数列表
* @param proxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("调用前执行");
Object result = proxy.invokeSuper(obj, args);
System.out.println("调用后执行");
return result;
}
}
MethodInterceptor和Jdk动态代理中InvocationHandler相似,但是此处有一个很大的不同的地方,就是此时调用实际的被代理类的方法的时候,使用的是proxy.invokeSuper(obj, args);,很明显是调用的是父类的方法。
创建代理类并调用:
public class CglibTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//创建代理类对象
OrderService orderService = (OrderServiceImpl)enhancer.create();
orderService.createOrder();
}
}
为了清楚的了解Cglib的实现原理,可以添加如下代码,就可以打印出动态代理生成的代理类Class:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
"F:\\document\\me\\java\\cglib_proxy_class");
执行之后,在文件夹中,找到生成动态代理类的地方,发现生成了四个class文件:
可以通过执行程序打印类型方式找到生成的代理类class,然后对其反编译:
发现代理类继承了被代理类,重写代理类中createOrder方法实现如下:
public final void createOrder() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$createOrder$0$Method, CGLIB$emptyArgs, CGLIB$createOrder$0$Proxy);
} else {
super.createOrder();
}
}
发现,调用代理类的createOrder,实际调用的还是MethodInterceptor的intercept方法,然后在MethodInterceptor的intercept中调用的是父类的方法,也就是被代理类的createOrder方法。
这里,其实只是简单的分析了一下Cglib的原理,其实其粗略的原理就是这样:
但是CGlib真正的实现,要比这复杂的多,本人也没有做过多的研究, 但是下面简单说一个细节,为了后面 jdk动态代理和CGlib动态代理比较做铺垫。
MethodInterceptor中调用intercept方法的时候,调用的是MethodProxy对象的invokeSuper方法。
其中fci.f2是FastClass类型:
其实FastClass也是动态生成的,我们前面打印cglib动态生成的class的时候,不是发现有四个class,其中有两个是FastClass。
重点来了:这里的两个动态生成的FastClass,只有当调用动态代理类被代理的方法的时候,才会动态生成。如果只是创建代理类而不调用被代理的方法,是不会动态创建的。
测试如下:
public class CglibTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
"F:\\document\\me\\java\\cglib_proxy_class");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//创建代理类对象
OrderService orderService = (OrderServiceImpl)enhancer.create();
// orderService.createOrder();
}
}
打印生成的class如下,发现,确实没有生成FastClass类。
好了,到这里,cglib的实现分析告一段落,更细节的我也没有再研究,想研究的,可以自行继续研究实现细节。
Jdk动态代理:被代理对象必须实现接口,不能代理没有接口的类。
cglib动态代理:对类本身没有限制,但是cglib有个坑,就是不能代理final方法,因为继承不能重写final方法,这个在使用中要注意。
那实际中我们应该选择jdk还是cglib?
这个需要两个因素,一个是代理本身的局限性,二是代理的性能问题。
代理本身的局限性已经提到了,jdk代理的对象必须要有接口,所以相比,cglib功能更强大。
对于性能问题,因为jdk和cglib都是用到了反射, 是比较耗性能的,所以需要写代码去测试,主要测试两个点,一个是代理类的创建性能,一个是执行方法的性能。
这里使用的jdk版本是1.8, cglib版本是3.2.1,不管是创建对象还是执行方法,我们都会先执行预热一下,原因是前面提到的,Cglib有的动态Class,不是在创建代理类的时候就生成了,而是在方法具体的执行的时候才创建的,所以会导致第一次执行会比较耗时。
这里比较创建的性能,是模仿为大量不同的对象创建代理类,而非多次为同一个对象创建不同的代理类,因为多次为同一个对象创建代理类,JDK和CGLib对其会进行优化,不能够看出明显的区别。并且在Spring中,所有的代理类都是代理的,也并不需要重复为同一个类创建多次代理。
这里主要为三个类创建代理,OrderService, PaymentService,InvoiceService三个类进行测试:具体测试代码如下:
public class JdkCglibCreateCompiler {
public static void main(String[] args) {
Long jdkStart1 = System.currentTimeMillis();
OrderService jdkOrderServiceProxy = (OrderService) Proxy.newProxyInstance(
OrderService.class.getClassLoader(),
OrderServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new OrderServiceImpl()));
Long jdkEnd1 = System.currentTimeMillis();
System.out.println("JDK创建第一个代理类 : " + (jdkEnd1 - jdkStart1));
Long jdkStart2 = System.currentTimeMillis();
PaymentService jdkPaymentServiceProxy = (PaymentService) Proxy.newProxyInstance(
PaymentService.class.getClassLoader(),
PaymentServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new PaymentServiceImpl()));
Long jdkEnd2 = System.currentTimeMillis();
System.out.println("JDK创建第二个代理类 : " + (jdkEnd2 - jdkStart2));
Long jdkStart3 = System.currentTimeMillis();
InvoiceService jdkPaymentServiceProxy = (InvoiceService) Proxy.newProxyInstance(
InvoiceService.class.getClassLoader(),
InvoiceServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new InvoiceServiceImpl()));
Long jdkEnd3 = System.currentTimeMillis();
System.out.println("JDK创建第三个代理类 : " + (jdkEnd3 - jdkStart3));
///******************Cglib*********************
Long cglibStart1 = System.currentTimeMillis();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//创建代理类对象
OrderService orderService = (OrderServiceImpl)enhancer.create();
Long cglibEnd1 = System.currentTimeMillis();
System.out.println("CGLIB创建第一个代理类 : " + (cglibEnd1 - cglibStart1));
Long cglibStart2 = System.currentTimeMillis();
enhancer.setSuperclass(PaymentServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//创建代理类对象
PaymentService paymentService = (PaymentService)enhancer.create();
Long cglibEnd2 = System.currentTimeMillis();
System.out.println("CGLIB创建第二个代理类 : " + (cglibEnd2 - cglibStart2));
Long cglibStart3 = System.currentTimeMillis();
enhancer.setSuperclass(InvoiceServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//创建代理类对象
InvoiceService invoiceService = (InvoiceService)enhancer.create();
Long cglibEnd3 = System.currentTimeMillis();
System.out.println("CGLIB创建第三个代理类 : " + (cglibEnd3 - cglibStart3));
}
}
因为只有三个测试类,为了模拟创建大量创建不同的对象,我这里上面代码重复执行40次,也就是jdk和cglib各自创建120个对象,得到如下结果:
从图中能够看出,不管是jdk还是cglib,第一次创建对象都会比较耗时,这里把第一次作为预热,只考虑后两次,也就是各自创建80个对象,对其进行加和,结果是Jdk创建80个对象,需要168ms,平均每个2.1ms, Cglib创建80个对象需要227ms,平均每个2.8ms。
结论是:Jdk1.8创建代理类对象的效率略高于cglib3.2.1
这里采用先预热10次,然后各自循环执行5万次,取执行5万次用时:
public class JdkCglibExecCompiler {
public static void main(String[] args) {
OrderService jdkOrderServiceProxy = (OrderService) Proxy.newProxyInstance(
OrderService.class.getClassLoader(),
OrderServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new OrderServiceImpl()));
//预热
for (int i = 0; i < 10; i++){
jdkOrderServiceProxy.createOrder();
}
Long jdkStart1 = System.currentTimeMillis();
for (int i = 0; i < 50000; i++){
jdkOrderServiceProxy.createOrder();
}
Long jdkEnd1 = System.currentTimeMillis();
System.out.println("jdk创建代理类 : " + (jdkEnd1 - jdkStart1));
System.out.println((jdkEnd1 - jdkStart1));
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
OrderService cglibOrderServiceProxy = (OrderServiceImpl)enhancer.create();
//预热
for (int i = 0; i < 10; i++){
cglibOrderServiceProxy.createOrder();
}
Long cglibStart1 = System.currentTimeMillis();
for (int i = 0; i < 50000; i++){
cglibOrderServiceProxy.createOrder();
}
Long cglibEnd1 = System.currentTimeMillis();
System.out.println("cglib创建代理类 : " + (cglibEnd1 - cglibStart1));
}
}
为了防止单次执行5万次不具有普遍性,这里执行10次,取每次执行5万次的时间如下:
结论:Jdk1.8代理类执行效率略高于cglib3.2.1
最后说一点,Spring中默认使用的是Jdk代理,但是可以再配置文件中配置实用其他代理方式。