您的位置 首页 java

Java中JDK动态代理和cglib的区别原理分析

什么是 代理模式

代理模式:为其他对象提供一种代理以控制对这个对象的访问。

代理模式中有三种角色:Subject抽象主题角色、RealSubject真实主题角色、Proxy代理主题角色。Subject描述了业务行为,RealSubject执行具体的业务逻辑,Proxy代理会拦截对RealSubject对象方法的调用,并在方法调用前后做预处理以及一些善后工作。

代理模式可以很好地在不侵入原代码的情况下,拓展原来的功能。

下图为Proxy模式的静态类图:

下图为Proxy模式的调用关系:

原理

Java 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

CGLIB 动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现 AOP

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final

如何强制使用CGLIB实现AOP?

1)添加CGLIB库(aspectjrt-xxx. jar 、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)

2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=”true”/>

CGlib比JDK快吗

1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,

在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,

因为CGLib原理是动态生成被代理类的子类。

2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,

只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,

总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。

JDK动态代理实现

我们在JDK动态代理的测试类中发现代理类生成是通过Proxy类中的newProxyInstance来完成的,下面我们进入这个函数:

Proxy类中的newProxyInstance

 public static Object newProxyInstance(ClassLoader loader,
 Class<?>[] interfaces,
 InvocationHandler h)
 throws IllegalArgumentException
 {
 //如果h为空将抛出异常
 Objects.requireNonNull(h);

 final Class<?>[] intfs = interfaces.clone();//拷贝被代理类实现的一些接口,用于后面权限方面的一些检查
 final SecurityManager sm = System.getSecurityManager();
 if (sm != null) {
 //在这里对某些安全权限进行检查,确保我们有权限对预期的被代理类进行代理
 checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
 }

 /*
 * 下面这个方法将产生代理类
 */ Class<?> cl = getProxyClass0(loader, intfs);

 /*
 * 使用指定的调用处理程序获取代理类的构造函数对象
 */ try {
 if (sm != null) {
 checkNewProxyPermission(Reflection.getCallerClass(), cl);
 }

 final Constructor<?> cons = cl.getConstructor(constructorParams);
 final InvocationHandler ih = h;
 //假如代理类的构造函数是private的,就使用反射来set accessible
 if (!Modifier.isPublic(cl.getModifiers())) {
 AccessController.doPrivileged(new PrivilegedAction<Void>() {
 public Void run() {
 cons.setAccessible(true);
 return null;
 }
 });
 }
 //根据代理类的构造函数来生成代理类的对象并返回
 return cons.newInstance(new Object[]{h});
 } catch (IllegalAccessException|InstantiationException e) {
 throw new InternalError(e.toString(), e);
 } catch (InvocationTargetException e) {
 Throwable t = e.getCause();
 if (t instanceof RuntimeException) {
 throw (RuntimeException) t;
 } else {
 throw new InternalError(t.toString(), t);
 }
 } catch (NoSuchMethodException e) {
 throw new InternalError(e.toString(), e);
 }
 }
 

所以代理类其实是通过getProxyClass方法来生成的:

 /**
 * 生成一个代理类,但是在调用本方法之前必须进行权限检查
 */ private static Class<?> getProxyClass0(ClassLoader loader,
 Class<?>... interfaces) {
 //如果接口数量大于65535,抛出非法参数错误
 if (interfaces.length > 65535) {
 throw new IllegalArgumentException("interface limit exceeded");
 }

 // 如果在缓存中有对应的代理类,那么直接返回
 // 否则代理类将有 ProxyClassFactory 来创建
 return proxyClassCache.get(loader, interfaces);
 }
 

那么ProxyClassFactory是什么呢?

 /**
 * 里面有一个根据给定ClassLoader和Interface来创建代理类的工厂函数 
 *
 */ private static final class ProxyClassFactory
 implements BiFunction<ClassLoader, Class<?>[], Class<?>>
 {
 // 代理类的名字的前缀统一为“$Proxy”
 private static final String proxyClassNamePrefix = "$Proxy";

 // 每个代理类前缀后面都会跟着一个唯一的编号,如$Proxy0、$Proxy1、$Proxy2
 private static final AtomicLong nextUniqueNumber = new AtomicLong();

 @Override
 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
 for (Class<?> intf : interfaces) {
 /*
 * 验证类加载器加载接口得到对象是否与由apply函数参数传入的对象相同
 */ Class<?> interfaceClass = null;
 try {
 interfaceClass = Class.forName(intf.getName(), false, loader);
 } catch (ClassNotFoundException e) {
 }
 if (interfaceClass != intf) {
 throw new IllegalArgumentException(
 intf + " is not visible from class loader");
 }
 /*
 * 验证这个Class对象是不是接口
 */ if (!interfaceClass.isInterface()) {
 throw new IllegalArgumentException(
 interfaceClass.getName() + " is not an interface");
 }
 /*
 * 验证这个接口是否重复
 */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
 throw new IllegalArgumentException(
 "repeated interface: " + interfaceClass.getName());
 }
 }

 String proxyPkg = null; // 声明代理类所在的package
 int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

 /*
 * 记录一个非公共代理接口的包,以便在同一个包中定义代理类。同时验证所有非公共
 * 代理接口都在同一个包中
 */ for (Class<?> intf : interfaces) {
 int flags = intf.getModifiers();
 if (!Modifier.isPublic(flags)) {
 accessFlags = Modifier.FINAL;
 String name = intf.getName();
 int n = name.lastIndexOf('.');
 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
 if (proxyPkg == null) {
 proxyPkg = pkg;
 } else if (!pkg.equals(proxyPkg)) {
 throw new IllegalArgumentException(
 "non-public interfaces from different packages");
 }
 }
 }

 if (proxyPkg == null) {
 // 如果全是公共代理接口,那么生成的代理类就在com.sun.proxy package下
 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
 }

 /*
 * 为代理类生成一个name package name + 前缀+唯一编号
 * 如 com.sun.proxy.$Proxy0.class
 */ long num = nextUniqueNumber.getAndIncrement();
 String proxyName = proxyPkg + proxyClassNamePrefix + num;

 /*
 * 生成指定代理类的字节码文件
 */  byte [] proxyClassFile = ProxyGenerator.generateProxyClass(
 proxyName, interfaces, accessFlags);
 try {
 return defineClass0(loader, proxyName,
 proxyClassFile, 0, proxyClassFile.length);
 } catch (ClassFormatError e) {
 /*
 * A ClassFormatError here means that (barring bugs in the
 * proxy class generation code) there was some other
 * invalid aspect of the arguments supplied to the proxy
 * class creation (such as virtual machine limitations
 * exceeded).
 */ throw new IllegalArgumentException(e.toString());
 }
 }
 }
 

JDK代理字节码生成

由上方代码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);可以发现,其实生成代理类字节码文件的工作是通过 ProxyGenerate类中的generateProxyClass方法来完成的。

 public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
 ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
 // 真正用来生成代理类字节码文件的方法在这里
 final byte[] var4 = var3.generateClassFile();
 // 保存代理类的字节码文件
 if(saveGeneratedFiles) {
 AccessController.doPrivileged(new PrivilegedAction() {
 public Void run() {
 try {
 int var1 = var0.lastIndexOf(46);
 Path var2;
 if(var1 > 0) {
 Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), 
 new String[0]);
 Files.createDirectories(var3, new FileAttribute[0]);
 var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
 } else {
 var2 = Paths.get(var0 + ".class", new String[0]);
 }

 Files.write(var2, var4, new OpenOption[0]);
 return null;
 } catch (IOException var4x) {
 throw new InternalError("I/O exception saving generated file: " + var4x);
 }
 }
 });
 }

 return var4;
 } 

ASPECTJ 是什么

  1. AspectJ 是一个代码生成工具(Code Generator)。
  2. AspectJ 语法就是用来定义代码生成规则的语法。您如果使用过 Java Compiler Compiler (JavaCC),您会发现,两者的代码生成规则的理念惊人相似。
  3. AspectJ 有自己的语法编译工具,编译的结果是 Java Class 文件,运行的时候,classpath 需要包含 AspectJ 的一个 jar 文件(Runtime lib)。

文章来源:智云一二三科技

文章标题:Java中JDK动态代理和cglib的区别原理分析

文章地址:https://www.zhihuclub.com/191407.shtml

关于作者: 智云科技

热门文章

发表回复

您的电子邮箱地址不会被公开。

网站地图