Spring AOP
本文记述AOP相关的代理模式
静态代理
1.定义
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。通过代理对象访问目标对象,防止直接访问目标对象造成系统复杂性提升.
2.原理
Subject : 接口,为RealSubject和ProxySubject提供一致性的接口
RealSubject : 目标访问类,实际的主体,实现Subject接口的类
ProxySubject : 代理类,处理Client的请求,只有当代理类无法回复请求时,才会调用目标访问类
Client : 请求者
3.适用场景
Virtual Proxy : 对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象提供一个虚拟代理。在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象。
Remote Proxy : 远程代理使得客户端可以访问远程主机上的对象,远程代理可以把网络的细节隐藏起来,使得客户端不必考虑网络的存在,客户端完全可以认为调用的远程代理对象在本地,而不是在远程
Cache Proxy : 某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,从而可以避免某些方法的重复执行,优化系统性能。
Access Proxy : 可以在调用RealSubject时做访问限制,通过代理过滤不可以访问的对象
4.实现
Printable.java
public interface Printable { void setPrintName(String name); String getPrintName(); void print(String str); }
Printer.java
public class Printer implements Printable { private String name; public Printer() { heavyJob("正在生成Printer的实例"); } public Printer(String name) { this.name = name; heavyJob("正在生成(Printer)" + name + "的实例"); } @Override public void setPrintName(String name) { this.name = name; } @Override public String getPrintName() { return this.name; } @Override public void print(String str) { System.out.println("==" + name + "=="); System.out.println(str); } public void heavyJob(String msg) { System.out.println(msg); for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print("."); } System.out.println("over"); } }
PrintProxy.java
public class PrintProxy implements Printable { private String name; private Printer real; public PrintProxy(String name) { this.name = name; } @Override public synchronized void setPrintName(String name) { if (real != null) { real.setPrintName(name); } this.name = name; } @Override public String getPrintName() { return this.name; } @Override public void print(String str) { realize(); real.print(str); } public synchronized void realize() { if (real == null) { real = new Printer(name); } } }
Main.java
public class Main { public static void main(String[] args) { Printable p = new PrintProxy("kristin"); System.out.println("name: " + p.getPrintName()); p.setPrintName("kkk"); System.out.println("new name: " + p.getPrintName()); p.print("hello world"); } }
Printable相当于Subject
Printer相当于RealSubject
PrinterProxy相当于ProxySubject
Main相当于Client
5.优缺点
优点:
协调调用者与被调用者,降低耦合度
作为客户端对象与目标对象的中介,可以有效地保护目标对象
缺点:
在客户端与目标对象之间增加了代理对象,处理请求的速度可能会变慢
如果代理的实现复杂,可能会增加系统实现的复杂性
如果想要为多个类进行代理,需要创建多个代理类,维护难度加大
JDK动态代理
1.定义
代理类在程序运行时被创建,并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的.
2.原理
Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。
代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
3.应用
如果想对代理类的所有方法都加上日志,可以通过动态代理可以对代理类的所有方法进行统一的处理,而不用一一更改每一个方法.
UserDao.java
/** * 真实类的接口 */ public interface UserDao { void add(); }
UserDaoImpl.java
/** * 真实类 */ public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("this is method-add running"); } }
MyInvocationHandler.java
/** * 运行时生成代理类的Handler */ public class MyInvocationHandler implements InvocationHandler { /** * 真实类对象 */ private Object target; /** * 将真实类的实例对象设置到自定义InvocationHandler中 * * @param target */ public MyInvocationHandler(Object target) { this.target = target; } /** * 运行时生成真实类的代理类 * * @param proxy * @param 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(target, args); System.out.println("--------------------after-------------------"); return result; } }
Main.java
public class Main { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { UserDao user = new UserDaoImpl(); //实例化真实类 MyInvocationHandler handler = new MyInvocationHandler(user); //保存class文件到本地,可以在项目根目录下的com.sun.proxy文件夹中看到$Proxy.class System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //运行时生成代理类 /** * 方法一: */ Class classProxy = Proxy.getProxyClass(user.getClass().getClassLoader(), UserDao.class); Constructor constructor = classProxy.getConstructor(InvocationHandler.class); UserDao userDao = (UserDao) constructor.newInstance(new MyInvocationHandler(user)); userDao.add(); /** * 方法二 */ // UserDao userDao = (UserDao) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), handler); // UserDao userDao = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(), new Class[]{UserDao.class}, handler); // userDao.add(); //调用代理类的方法 } }
4.实现原理
4.1相关的类和接口
Class : java.lang.reflect.Proxy
Interface : java.lang.reflect.InvocationHandler
4.2代理步骤
1.Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)</p>
方法参数:
1. loader : 类加载器
2. interfaces : 目标对象实现的接口
3. h : InvocationHandler的实现类
以下是Proxy.java的newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); // 如果h为null,就会抛出空指针异常,所以一定要有InvocationHandler的实现类 final Class<?>[] intfs = interfaces.clone(); //拷贝目标对象所实现的接口 final SecurityManager sm = System.getSecurityManager(); //进行一些安全检查 if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 根据类加载器和接口生成代理类 */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //获取代理类的构造函数对象 //参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class }; final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; 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); } }
以下是Proxy类的getProxyClass0方法, 生成代理类
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); }
以下是Proxy的内部类ProxyClassFactory,看看ProxyClassFactory是怎样生成的代理类
/** * A factory function that generates, defines and returns the proxy class given * the ClassLoader and array of interfaces. */ private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 所有代理类的名称前缀为"$Proxy" private static final String proxyClassNamePrefix = "$Proxy"; // 使用唯一的编号作为代理类名称的一部分 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityMyMyHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * 验证指定类加载器加载的Class对象是否与intf加载的Class对象相同 */ 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; // 声明代理类所在包 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * 验证传入的接口是否为非public的,保证非public接口的代理类将被定义在同一个包中 */ 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) { // 如果没有非public接口,那么这些代理类都放在com.sun.proxy包中 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * 将nextUniqueNumber通过cas加1,对proxy类名编号进行generate,初始为1 */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; //代理类的全限定名,如com.sun.proxy.$Proxy0.calss, /* * 生成代理类的字节码文件 */ 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()); } } }
再看一看是怎样生成字节码文件的呢?
以下是sun.misc.ProxyGenerator的静态方法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<Void>() { 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)); Files.createDirectories(var3); var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); } else { var2 = Paths.get(var0 + ".class"); } Files.write(var2, var4, new OpenOption[0]); return null; } catch (IOException var4x) { throw new InternalError("I/O exception saving generated file: " + var4x); } } }); } return var4; }
以下是sun.misc.ProxyGenerator的内部静态方法,也是真正生成代理类字节码文件的方法
private byte[] generateClassFile() { /* 将Object的三个方法,hashCode、equals、toString添加到代理类容器中 * hashCodeMethod方法位于静态代码块中通过Object对象获得, * hashCodeMethod=Object.class.getMethod("hashCode",new Class[0]), * 相当于从Object中继承过来了这三个方法equalsMethod,toStringMethod */ this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; //将所有接口中的所有方法添加到代理方法中 for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; Method[] var5 = var4.getMethods(); int var6 = var5.length; for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } Iterator var11 = this.proxyMethods.values().iterator(); List var12; while(var11.hasNext()) { var12 = (List)var11.next(); checkReturnTypes(var12); //验证具有相同方法签名的的方法的返回值类型是否一致,因为不可能有两个方法名相同,参数相同,而返回值却不同的方法 } //以下步骤是写代理类文件 Iterator var15; try { this.methods.add(this.generateConstructor()); //生成代理类构造函数 var11 = this.proxyMethods.values().iterator(); while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator(); while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); /* 将代理字段声明为Method,10为ACC_PRIVATE和ACC_STATAIC的与运算,表示该字段的修饰符为private static * 所以代理类的字段都是private static Method XXX */ this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); //生成代理类的代理方法 this.methods.add(var16.generateMethod()); } } //为代理类生成静态代码块,对一些字段进行初始化 this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); } if (this.methods.size() > 65535) { //代理方法数量超过65535将抛出异常 throw new IllegalArgumentException("method limit exceeded"); } else if (this.fields.size() > 65535) { //代理类的字段数量超过65535将抛出异常 throw new IllegalArgumentException("field limit exceeded"); } else { //从这里开始就是写代理类字节码文件啦 this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); var1 = this.interfaces; var2 = var1.length; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; this.cp.getClass(dotToSlash(var4.getName())); } this.cp.setReadOnly(); ByteArrayOutputStream var13 = new ByteArrayOutputStream(); DataOutputStream var14 = new DataOutputStream(var13); try { var14.writeInt(-889275714); var14.writeShort(0); var14.writeShort(49); this.cp.write(var14); var14.writeShort(this.accessFlags); var14.writeShort(this.cp.getClass(dotToSlash(this.className))); var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); var14.writeShort(this.interfaces.length); Class[] var17 = this.interfaces; int var18 = var17.length; for(int var19 = 0; var19 < var18; ++var19) { Class var22 = var17[var19]; var14.writeShort(this.cp.getClass(dotToSlash(var22.getName()))); } var14.writeShort(this.fields.size()); var15 = this.fields.iterator(); while(var15.hasNext()) { ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next(); var20.write(var14); } var14.writeShort(this.methods.size()); var15 = this.methods.iterator(); while(var15.hasNext()) { ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next(); var21.write(var14); } var14.writeShort(0); return var13.toByteArray(); } catch (IOException var9) { throw new InternalError("unexpected I/O Exception", var9); } } }
是怎样将Object方法添加到代理类容器的呢?下面我们看一看sun.misc.ProxyGenerator中的addProxyMethod方法
private void addProxyMethod(Method var1, Class<?> var2) { String var3 = var1.getName(); // 方法名 Class[] var4 = var1.getParameterTypes(); // 方法参数类型 Class var5 = var1.getReturnType(); // 方法返回值类型 Class[] var6 = var1.getExceptionTypes(); // 方法异常类型 String var7 = var3 + getParameterDescriptors(var4); // 方法签名 Object var8 = (List)this.proxyMethods.get(var7); //根据方法签名获得方法对象 if (var8 != null) { // 处理多个代理接口中的重复方法 Iterator var9 = ((List)var8).iterator(); while(var9.hasNext()) { ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next(); if (var5 == var10.returnType) { ArrayList var11 = new ArrayList(); collectCompatibleTypes(var6, var10.exceptionTypes, var11); collectCompatibleTypes(var10.exceptionTypes, var6, var11); var10.exceptionTypes = new Class[var11.size()]; var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes); return; } } } else { var8 = new ArrayList(3); this.proxyMethods.put(var7, var8); } ((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null)); }
通过下面这个设置,可以将代理类字节码文件保存到项目根目录下的com.sun.proxy文件夹中看到$Proxy.class
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
下面我们看一看$Proxy0.class
package com.sun.proxy; import com.kristin.design_pattern.proxy.jdk_proxy.UserDao; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements UserDao { private static Method m1; private static Method m2; private static Method m3; 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 add() throws { try { /** * this: 当前生成的$Proxy0实例 * m3: 就是我们定义的接口方法 * (Object)null: 被传入的被调用的接口方法的实际参数 * * 可以看到实际上这个方法中的3个参数Object proxy, Method method, Object[] args就是$Proxy0通过h.invoke(this, m3, new Object[]{var1});传递给我们的, * 也就是说,我们实现的InvocationHandler的invoke()实际上是一个回调,也就是我们预先定义好的,然后JDK生成的类$Proxy0回过来调用的。 */ super.h.invoke(this, m3, (Object[])null); //注意这里super.h其实就是InvocationHandler的对象h } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } 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("com.kristin.design_pattern.proxy.jdk_proxy.UserDao").getMethod("add"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
IN ALL:
JDK动态代理的基本原理就是 我们定义好接口和默认实现,JDK根据通过生成class文件的方式”动态”的生成一个代理类,这个代理类实现了我们定义的接口,并在接口实现方法中回调了我们通过InvocationHandler定义的处理流程,这个处理流程中我们回去调用默认实现,并提供增强。
不足:
JDK动态代理只能代理实现了接口的类,而Cglib动态代理可以代理没有实现接口的类
JDK vs CGLIB:
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
结论
1.同样情况下,cglib两种实现方式,invokeSuper + setSuperClass 永远比 invoke + setInterfaces慢
2.cglib invoke + setInterfaces 在方法数量较少的时候,在函数平均调用的情况下 比jdkProxy快,随着函数增多,优势越来越不明显,到达某个数量级一定比jdk动态代理慢
3.cglib invoke + setInterfaces 在调用特定函数(在switch中靠后的case) 会比jdk动态代理慢
思考
cglib的瓶颈在于:
调用net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])时需要switch的case:
如果有n个函数,那么就要一个个比较,复杂度O(n)
这里如果有一个key -> behavior的映射就好了,目前并没有。
如果可以用asm在这里写成一个二分搜索,cglib就会快多了,变成O(log2 n),时间上就是一个飞跃,只不过这个fastclass就会看起来很丑。(目前最新3.2.5的版本也没有改动这一块)
CGLIB动态代理
1.定义
cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个类,并覆盖其中方法实现增强
代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理.
2.应用
UserDao.java
public interface UserDao { void add(); }
UserDaoImpl.java
public class UserDaoImpl implements UserDao { public void add() { System.out.println("the method is running"); } }
Interceptor.java
public class Interceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("-------------------before------------------"); proxy.invokeSuper(obj, args); System.out.println("--------------------after-------------------"); return null; } }
Main.java
public class Main { @org.junit.jupiter.api.Test public void test() { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserDaoImpl.class); enhancer.setCallback(new Interceptor()); UserDao userDao = (UserDao) enhancer.create(); userDao.add(); } }
3.实现原理
代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理
创建代理对象的几个步骤:
1.生成代理类的二进制字节码文件;
2.加载二进制字节码,生成Class对象( 例如使用Class.forName()方法 );
3.通过反射机制获得实例构造,并创建代理类对象
代理类Class文件反编译后的Java代码:
import java.lang.reflect.Method; import net.sf.cglib.core.ReflectUtils; import net.sf.cglib.core.Signature; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 extends UserDaoImpl implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$add$0$Method; private static final MethodProxy CGLIB$add$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$equals$1$Method; private static final MethodProxy CGLIB$equals$1$Proxy; private static final Method CGLIB$toString$2$Method; private static final MethodProxy CGLIB$toString$2$Proxy; private static final Method CGLIB$hashCode$3$Method; private static final MethodProxy CGLIB$hashCode$3$Proxy; private static final Method CGLIB$clone$4$Method; private static final MethodProxy CGLIB$clone$4$Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.kristin.design_pattern.proxy.cglib_proxy.UserDaoImpl$$EnhancerByCGLIB$$3ef42b63"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$1$Method = var10000[0]; CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1"); CGLIB$toString$2$Method = var10000[1]; CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2"); CGLIB$hashCode$3$Method = var10000[2]; CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3"); CGLIB$clone$4$Method = var10000[3]; CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4"); CGLIB$add$0$Method = ReflectUtils.findMethods(new String[]{"add", "()V"}, (var1 = Class.forName("com.kristin.design_pattern.proxy.cglib_proxy.UserDaoImpl")).getDeclaredMethods())[0]; CGLIB$add$0$Proxy = MethodProxy.create(var1, var0, "()V", "add", "CGLIB$add$0"); } final void CGLIB$add$0() { super.add(); } public final void add() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$add$0$Method, CGLIB$emptyArgs, CGLIB$add$0$Proxy); } else { super.add(); } } final boolean CGLIB$equals$1(Object var1) { return super.equals(var1); } public final boolean equals(Object var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy); return var2 == null ? false : (Boolean)var2; } else { return super.equals(var1); } } final String CGLIB$toString$2() { return super.toString(); } public final String toString() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString(); } final int CGLIB$hashCode$3() { return super.hashCode(); } public final int hashCode() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy); return var1 == null ? 0 : ((Number)var1).intValue(); } else { return super.hashCode(); } } final Object CGLIB$clone$4() throws CloneNotSupportedException { return super.clone(); } protected final Object clone() throws CloneNotSupportedException { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone(); } public static MethodProxy CGLIB$findMethodProxy(Signature var0) { String var10000 = var0.toString(); switch(var10000.hashCode()) { case -1422568652: if (var10000.equals("add()V")) { return CGLIB$add$0$Proxy; } break; case -508378822: if (var10000.equals("clone()Ljava/lang/Object;")) { return CGLIB$clone$4$Proxy; } break; case 1826985398: if (var10000.equals("equals(Ljava/lang/Object;)Z")) { return CGLIB$equals$1$Proxy; } break; case 1913648695: if (var10000.equals("toString()Ljava/lang/String;")) { return CGLIB$toString$2$Proxy; } break; case 1984935277: if (var10000.equals("hashCode()I")) { return CGLIB$hashCode$3$Proxy; } } return null; } public UserDaoImpl$$EnhancerByCGLIB$$3ef42b63() { CGLIB$BIND_CALLBACKS(this); } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { CGLIB$THREAD_CALLBACKS.set(var0); } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { CGLIB$STATIC_CALLBACKS = var0; } private static final void CGLIB$BIND_CALLBACKS(Object var0) { UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var1 = (UserDaoImpl$$EnhancerByCGLIB$$3ef42b63)var0; if (!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if (var10000 == null) { var10000 = CGLIB$STATIC_CALLBACKS; if (CGLIB$STATIC_CALLBACKS == null) { return; } } var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; } } public Object newInstance(Callback[] var1) { CGLIB$SET_THREAD_CALLBACKS(var1); UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; } public Object newInstance(Callback var1) { CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1}); UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; } public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63; switch(var1.length) { case 0: var10000.<init>(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; default: throw new IllegalArgumentException("Constructor not found"); } } public Callback getCallback(int var1) { CGLIB$BIND_CALLBACKS(this); MethodInterceptor var10000; switch(var1) { case 0: var10000 = this.CGLIB$CALLBACK_0; break; default: var10000 = null; } return var10000; } public void setCallback(int var1, Callback var2) { switch(var1) { case 0: this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2; default: } } public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this); return new Callback[]{this.CGLIB$CALLBACK_0}; } public void setCallbacks(Callback[] var1) { this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0]; } static { CGLIB$STATICHOOK1(); } }
CGLIB在进行代理的时候都进行了哪些工作呢
1.生成的代理类UserDaoImpl$$EnhancerByCGLIB$$3ef42b63.class继承被代理类UserDaoImpl。在这里我们需要注意一点:如果委托类被final修饰,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理;
2.代理类会为委托方法生成两个方法,一个是重写的add方法,另一个是CGLIB$add$0方法,我们可以看到它是直接调用父类的add方法;
3.当执行代理对象的add方法时,会首先判断一下是否存在实现了MethodInterceptor接口的CGLIB$CALLBACK_0;,如果存在,则将调用MethodInterceptor中的intercept方法。
在intercept方法中,我们除了会调用委托方法,还会进行一些增强操作。在Spring AOP中,典型的应用场景就是在某些敏感方法执行前后进行操作日志记录。
调用委托方法是通过代理方法的MethodProxy对象调用invokeSuper方法来执行的,下面我们看看invokeSuper方法:
/** * Invoke the original (super) method on the specified object. * @param obj the enhanced object, must be the object passed as the first * argument to the MethodInterceptor * @param args the arguments passed to the intercepted method; you may substitute a different * argument array as long as the types are compatible * @see MethodInterceptor#intercept * @throws Throwable the bare exceptions thrown by the called method are passed through * without wrapping in an <code>InvocationTargetException</code> */ public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { init(); FastClassInfo fci = fastClassInfo; return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } }
private static class FastClassInfo { FastClass f1; FastClass f2; int i1; int i2; }
f1指向委托类对象
f2指向代理类对象
i1和i2分别代表着add方法以及CGLIB$add$0方法在对象信息数组中的下标。
总结
代理方式 | 实现 | 特点 | 优点 | 缺点 |
静态代理 | 代理类与委托类实现同一接口,并且在代理类中需要硬编码接口 | 好像没啥特点 | 实现简单,容易理解 | 代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低 |
JDK动态代理 | 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 | 底层使用反射机制进行方法的调用 | 不需要硬编码接口,代码复用率高 | 只能够代理实现了接口的委托类 |
CGLIB动态代理 | 代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 | 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用 | 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 | 不能对final类以及final方法进行代理 |
参考:
https://www.cnblogs.com/MOBIN/p/5597215.html
https://since1986.github.io/blog/bf178159.html
https://www.jianshu.com/p/1aaacf92e2cd
https://h2pl.github.io/2018/06/03/spring5/
https://www.cnblogs.com/cruze/p/3865180.html