本文主要介绍CGLib和JDK动态代理的使用,不对源码进行深入分析。代码可直接复制使用。
类型 | 机制 | 回调方式 | 适用场景 | 效率 |
JDK动态代理 | 委托机制。代理类和目标类都实现了同样的接口。InvocationHandler持有目标类。代理类委托InvocationHandler去调用目标类原始方法 | 反射 | 目标类实现接口 | 反射调用稍慢。 |
CGLIB动态代理 | 继承机制。代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑(底层使用到ASM技术,操作字节码生成代理类) | 通过FastClass方法索引调用 | 非final类,非final方法 | 第一次调用因为要生成多个Class对象较]DK慢,但是调用时方法索引较反射方式快 |
代码框架:
类UserInterface
package com.cocoa.dao;
public interface UserInterface {
public void test();
}
类UserService
package com.cocoa.dao;
public class UserService implements UserInterface{
@Override
public void test() {
System.out.println("UserService test() -- print");
}
}
类CGLIBDemo
method.invoke()使用的还是反射机制;但是methodProxy.invoke使用的不是反射,而是FastClass机制,通过建立代理类的索引,快速执行代理的方法。(所以比JDK快)
MethodIntercept的入参:
o:目标对象的实例(被代理的对象);
method:被代理的方法;
objects:方法调用时的入参;
methodProxy:用于调用原始方法的代理。
package com.cocoa.enhancer;
import com.cocoa.dao.UserService;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLIBDemo {
public static void Main(String[] args) {
// 动态代理生成的字节码存储到本地
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "com.cocoa.enhancer");
final UserService target = new UserService();
// 增强器
Enhancer enhancer = new Enhancer();
// enhancer.setUseCache(false);// 使用缓存
// 设置代理的类
enhancer.setSuperclass(UserService.class);
// 设置代理逻辑
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("test")){
System.out.println("before...");
method.invoke(target, objects);
System.out.println("after...");
}
return null;
}
});
// 使用代理类
UserService userService = (UserService) enhancer.create();// create会将第一次产生的代理类缓存下来
userService.test();
}
}
要避免使用method.invoke(),应该使用methodProxy。
类CGLIBDemo1
使用methodProxy.invoke执行方法(通过FastCLass索引机制)
methodProxy.invoke(target, objects);// test() 正常运行,直接执行代理方法
methodProxy.invoke(o, objects);// o 表示代理对象,这样会导致死循环
methodProxy.invokeSuper(target, objects);// CGLIB$test$4() 因为target中没有代理对象的方法
methodProxy.invokeSuper(o, objects);// CGLIB$test$4() 执行代理对象o中的test方法 正常运行
package com.cocoa.enhancer;
import com.cocoa.dao.UserService;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* MethodProxy的使用
*/
public class CGLIBDemo1 {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "com.cocoa.enhancer");
final UserService target = new UserService();
// 增强器
Enhancer enhancer = new Enhancer();
// enhancer.setUseCache(false);// 使用缓存
// 设置代理的类
enhancer.setSuperclass(UserService.class);
// 设置代理逻辑
enhancer.setCallback(new MethodInterceptor() {
@Override
// o代理对象 objects入参 method被代理的方法 methodProxy代理的方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("test")){
System.out.println("before...");
// MethodProxy 表示方法代理,代理了两个方法 test()
// methodProxy.invoke(target, objects);// test() 可用
// methodProxy.invoke(o, objects);// o 表示代理对象,这样会导致死循环
// methodProxy.invokeSuper(target, objects);// CGLIB$test$4() 因为target中没有代理对象的方法
methodProxy.invokeSuper(o, objects);// CGLIB$test$4() 执行代理对象o中的test方法 可用
System.out.println("after...");
}
return null;
}
});
// 使用代理类
UserService userService = (UserService) enhancer.create();// create会将第一次产生的代理类缓存下来
userService.test();
}
}
类mainInterface
package com.cocoa.enhancer;
import com.cocoa.dao.UserInterface;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* CGLIB 可以代理接口
*/
public class mainInterface {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
// 设置代理的接口
enhancer.setSuperclass(UserInterface.class);
// 设置代理逻辑
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("切面逻辑...");
return null;
}
});
// 使用代理类
UserInterface userInterface = (UserInterface) enhancer.create();
userInterface.test();
}
}
类JDKDemo
使用proxy.newProxyInstance方法直接构造代理类,入参有:
1)真实对象的类加载器;
2)真实对象实现的接口;
3)代理类需要实现InvocationHandler接口,重写Invoke方法。
invoke方法的入参:
参数1:用Proxy.newProxyInstance方法产生的真实对象,注意,参数1并不显式地出现在方法体内;
参数2:要调用的目标方法;
参数3:目标方法中的参数,一般是 Object[ ] args。
package com.cocoa.jdkProxy;
import com.cocoa.dao.UserInterface;
import com.cocoa.dao.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKDemo {
public static void main(String[] args) {
// Proxy.newproxyInstance
// 类加载器、代理的接口、new InvocationHandler
UserService target = new UserService();
UserInterface userInterface = (UserInterface) Proxy.newProxyInstance(
JDKDemo.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("test");
method.invoke(target, args);
return null;
}
});
userInterface.test();
}
}
ASM技术尝鲜
通过ASM字节码技术,可以生成一个类。执行下面的代码,就可以生成下图中的类。
package com.cocoa.asmDemo;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* ASM 尝鲜使用
*/
public class ASMDemo {
public static void main(String[] args) throws IOException {
ClassWriter classWriter = new ClassWriter(0);
// 通过visit 方法确定类的头部信息
classWriter.visit(Opcodes.V1_8,// java版本
Opcodes.ACC_PUBLIC,// 类修饰符
"Person", // 类的全限定名
null, "java/lang/Object", null );
// 创建构造函数
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.AALOAD, 0);// 字节码指令
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1,1);
mv.visitEnd();
// 定义test方法
MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "test", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("hello zhouyu");
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream","println","(Ljava/lang/string;)V");
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(2,2);
methodVisitor.visitEnd();
classWriter.visitEnd();
// 使classWriter类已经完成
// 将classWriter转换成字节数组写到文件里面大
byte[] data =classWriter.toByteArray();
File file = new File( "E:\\java_shicao\\DynamicProxyDemo\\src\\main\\java\\People.class");
FileOutputStream fout = new FileOutputStream(file);
fout.write(data);
fout.close();
}
}