Srping AOP的学习(二):java代理
1.代理的介绍
说的java的代理,就离不开java的代理模式。而java的代理模式就是通过引入一个代理对象来代替我们的实际对象进行操作,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。所以代理就是实现对功能的扩展。
2.代理的分类
代理按照大的分类可以分为静态代理和动态代理两种,而动态代理又可以分为jdk动态代理和cglib代理两种。
- 静态代理
静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件,对目标方法的增强都是手动提前写好的,如果增强的方法较多的话,这时我们使用静态代理写的就比较多了,因此静态代理会比较容易理解,但是其灵活性不够。 - 动态代理
动态代理时在类运行的时候进行生成字节码文件的,拥有较高的灵活度,不用针对每个目标类都创建一个代理类。- jdk动态代理:只能代理实现了接口的类 (使用java反射包中的类和接口实现动态代理功能,java.lang.reflect里面有三个类 InvocationHandler , Method , Proxy)
- cglib动态代理:这种是以生成目标类的子类来实现的,所以它的目标类不用必须实现接口 (第三方工具库,cglib通过继承目标类,创建它的子类,在子类中复写父类同名方法,实现功能的修改,所以目标类不能用final修饰)
InvocationHandler 接口,就一个方法invoke(),表示代理对象要执行的功能方法,你的代理类要完成功能就写在invoke()方法中。
代理类完成的功能:1.调用目标方法,执行目标方法的功能. 2.功能增强,在目标方法调用是,增加功能.
方法: public Object invoke(Object proxy, Method method, Object[] args)
Object proxy:JDK创建的代理对象,无需赋值
Method method:目标类中的方法,jdk提供的Method类
Object[] args:目标类中的参数,jdk提供的
InvocationHandler 表示你的代理要干什么;1.创建类实现InvocationHandler接口。 2.重写invoke()方法,代理类要完成的方法写在这里.
Method类,方法类,确切的说是目标类的方法。可以通过Method类执行目标类的方法,Method.invoke(目标对象,方法参数).
Proxy类,创建代理对象,之前创建对象都是new 类的构造方法。现在使用Proxy的静态方法创建对象,newProxyInstance()
静态方法:Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler); 返回值就是代理对象
ClassLoader classLoader:类加载器,负责向内存中加载对象的,使用反射获取对象ClassLoader,a.getClass.getClassLoader().
Class<?>[] interfaces:接口,目标对象实现的接口,也是反射获取的.
InvocationHandler myInvocationHandler:自己写的代理类完成的功能.
3.代理的详解
3.1 静态代理
静态代理的逻辑比较容易理解,看的比较清晰,就是创建一个新的代理类,来代替我们执行目标对象的一些方法,同时能够在执行目标对象的前后加上一些我们需要的操作。在下面的这个例子目标类就是输出当前的时间,而代理类又在输出当前时间之前与之后加了两行代码。
代理接口类:
public interface RootInterface { // 输出当前时间 void printNowTime(); }
代理接口的实现类,也就是目标对象:
public class RootInterfaceImpl implements RootInterface{ @Override public void printNowTime() { // 输出当前时间 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } }
代理类:
public class RootInterfaceHandler implements RootInterface{ /** * 目标对象 */ private RootInterfaceImpl target; public RootInterfaceHandler(RootInterfaceImpl target) { this.target = target; } @Override public void printNowTime() { System.out.println("输出时间之前"); // 调用目标方法 target.printNowTime(); System.out.println("输出时间之后"); } }
测试类:
public class RootInterfaceTest { public static void main(String[] args) { // 目标对象 RootInterfaceImpl rootInterface = new RootInterfaceImpl(); // 创建代理对象 RootInterface rootInterfaceProxy = new RootInterfaceHandler(rootInterface); // 执行代理方法 rootInterfaceProxy.printNowTime(); } }
运行结果:
3.2 动态代理
- 3.2.1 jdk动态代理
在jdk动态代理中最重要的就是代理类要实现InvocationHandler接口,这个接口中只有一个invoke方法,这个方法的主要作用就是执行目标对象中的方法,其有三个参数:proxy,menthod,args。具体的源码如下所示:
package java.lang.reflect; public interface InvocationHandler { /** * proxy:表示真正的代理对象,也就是生成的代理$Proxy0 * method:调用目标类的方法,通过method.invoke()执行目标类方法 * args:执行方法的参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
随后就是通过Proxy.newProxyInstance()
的方式创建出相应的代理就行了。具体的样例如下所示:
代理的接口:
public interface MyProxyInterface { void printDate(); }
目标类:
public class MyProxyInterfaceImpl implements MyProxyInterface{ private Date date; @Override public void printDate() { date = new Date(); System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); } }
代理类:
public class MyInvocationHandler implements InvocationHandler { /** * 目标对象 */ private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("执行方法之前"); // 执行目标方法 Object invoke = method.invoke(target, args); System.out.println("执行方法之后"); return invoke; } }
测试类:
public class MyProxy { public static void main(String[] args) { // 目标对象 MyProxyInterface myProxyInterface = new MyProxyInterfaceImpl(); // 生成目标对象的处理器 MyInvocationHandler myInvocationHandler = new MyInvocationHandler(myProxyInterface); // 获取目标对象的类加载器 ClassLoader classLoader = myProxyInterface.getClass().getClassLoader(); // 获取目标对象所实现的所有接口类,在这里只有MyProxyInterface接口 Class<?>[] interfaces = myProxyInterface.getClass().getInterfaces(); // 生成代理 MyProxyInterface adminProxy = (MyProxyInterface) Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler); // 调用代理方法 adminProxy.printDate(); } }
运行效果:
分析:
由上面例子可以看到代理是由Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
这句根据类加载器,接口类,以及自己实现的代理类来进行生成的,那么这个代理到底是如何进行生成的呢?这边就要具体的看它的源码实现了。
Proxy.newProxyInstance()源码:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 验证InvocationHandler是否为空 Objects.requireNonNull(h); // 复制代理类要实现的所有接口 final Class<?>[] intfs = interfaces.clone(); // 获取系统的安全管理器 final SecurityManager sm = System.getSecurityManager(); // 如果安全管理器不为空,则进行相关的验证 if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ // 获取代理类 Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { // 如果安全管理器存在,则对获取的代理类进行相关的验证 if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 获取代理类构造器 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; // 判断代理类的修饰符是否是public的,如果不是则破解其访问权限 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); } }
从上面源码的流程可以看出先进行非空判断,随后看是否需要进行相关的安全验证,需要的话则进行验证,之后在通过getProxyClass0()
方法获取代理类,获取到代理类之后在判断是否需要进行相关的安全验证,验证之后获取代理类的构造器,随后判断构造器的方法是否是公共的,不是的话则进行相应的破解,也就是cons.setAccessible(true);
这句取消了java语言的访问检查,随后就是根据获得的构造器生成新的实例了。其中newProxyInstsance 方法最重要的几个环节就是获得代理类、获得构造器,然后构造新实例这三步,下面接着说说获取代理类
getProxyClass0()源码:
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { // 进行校验实现的接口的数量,大于65535这个值则抛出个异常 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory // 从proxyClassCache缓存中获取代理类,如果缓存中不存在则利用ProxyClassFactory新建一个 return proxyClassCache.get(loader, interfaces); }
可以从其源码中看出,其是通过从缓存中获取代理对象的,如果代理对象不存在则会通过ProxyClassFactory新建一个代理对象,那么这边是怎么实现的呢?
首先我们先来看一下proxyClassCache.get()的源码:
public V get(K key, P parameter) { // 判断接口不为空 Objects.requireNonNull(parameter); // 清除过期的缓存 expungeStaleEntries(); // 生成缓存的key Object cacheKey = CacheKey.valueOf(key, refQueue); // lazily install the 2nd level valuesMap for the particular cacheKey // 根据缓存的key获取缓存的值 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); // 如果值不存在则创建一个空的对象加入到缓存中 if (valuesMap == null) { ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); // 此时是缓存中在加入的过程中有值了,则使用原先的值 if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier<V> stored by that // subKey from valuesMap // 生成subKey,二级缓存的key Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); // 获取值 Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; // 在这里一直循环,直到subKey获取出来的值不为空 while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue<V> instance V value = supplier.get(); if (value != null) { return value; } } // else no supplier in cache // or a supplier that returned null (could be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) // lazily construct a Factory if (factory == null) { // 新建一个Factory实例作为subKey对应的值 factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { // 如果subKey这里还是没有值,则将新建的Factory作为值放入 supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // successfully installed Factory supplier = factory; } // else retry with winning supplier } else { // 如果有线程修改了值, 那么就将原先的值替换 if (valuesMap.replace(subKey, supplier, factory)) { // successfully replaced // cleared CacheEntry / unsuccessful Factory // with our Factory supplier = factory; } else { // retry with current supplier supplier = valuesMap.get(subKey); } } } }
由上面的源码可以看出如果当缓存中没有值得话,会新创建一个Factory作为supplier的值,而返回的结果则是调用它的get方法,也就是Factory的get方法,接着来看看Factory的get方法:
可以看出它的值是通过调用valueFactory的apply方法获取的,那么valueFactory的值又是什么呢?
可以根据上图看到其是在构造函数中赋值的,而proxyClassCache在声明时给了一个初值new ProxyClassFactory()
因此当缓存中代理对象不存在的话则会通过ProxyClassFactory新建一个代理对象,就是这样有个流程。下面让我们来看看ProxyClassFactory创建代理的过程.
ProxyClassFactory源码:
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 代理类的前缀 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 IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * 验证类加载是否将此接口解析为相同的类对象 */ 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; // 代理类的包名 // 生成代理类的访问标志,默认为public final的 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; for (Class<?> intf : interfaces) { // 获取接口的修饰符 int flags = intf.getModifiers(); // 接口的修饰符不是public 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 + "."; } /* * 设置要生成的代理类的名称 */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 利用ProxyGenerator生成字节码 */ 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()); } } }
经过上面的代码可以看出前面的都是一些各种校验,生成代理名称之类的代码,最重要的是使用ProxyGenerator.generateProxyClass()方法进行生成字节码,这个方法点进去可以看到其最主要的是主要generateClassFile这个方法,
通过generateClassFile这个方法来将所有要生成的代理方法包装成ProxyMethod
以及生成类的构造函数与字段等
这边通过在main函数中加入System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
这句代码能够生成相关代理的类,生成的文件目录在你的项目的如下路径下
通过idea打开可以看到它生成的代理类的的代码如下:
package com.sun.proxy; import com.mcj.music.proxytest.MyProxyInterface; 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 MyProxyInterface { private static Method m1; private static Method m3; private static Method m2; 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 void printDate() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } 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 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")); m3 = Class.forName("com.mcj.music.proxytest.MyProxyInterface").getMethod("printDate"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
可以看出其继承了Proxy,因为Java中只支持单继承,所以JDK动态代理只能去实现接口。
代理方法都会去调用InvocationHandler的invoke()方法,因此我们需要重写InvocationHandler的invoke()方法。
-
- 3.2.2 cglib动态代理
cglib动态代理的原理是代理类去继承目标类,然后重写目标类的方法来实现的,所以其对final修饰的方法是无法进行代理的。cglib动态代理最重要的是要实现MethodInterceptor,其只有一个方法intercept,源码如下:public interface MethodInterceptor extends Callback { Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable; }
其中四个参数,var1:表示生成的代理对象,var2:表示目标方法,var3:表示参数,var4:表示代理方法。
有了自己的方法拦截器之后就是设置enhancer的回调方法为这个自己的方法拦截器了,随后利用enhancer生成代理方法即可。具体的样例如下:
目标类:import java.text.SimpleDateFormat; import java.util.Date; /** * @author mcj * @date 2022/11/5 19:56 * @description cglib测试的目标类 */ public class Time { public final void now2() { System.out.println("这是final修饰的方法"); } public void now() { System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } }
这个目标类中有两个方法,其中一个是final修饰的方法,主要是为了展示一下final修饰的方法是无法被cglib代理的。
自己的方法拦截器:package com.mcj.music.cglibproxytest; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @author mcj * @date 2022/11/5 19:58 * @description 方法拦截器,对目标类进行增强 */ public class MyMethodInterceptor implements MethodInterceptor { /** * * @param obj 生成的代理对象 * @param method 目标方法 * @param args 参数 * @param methodProxy 代理方法 * @return * @throws Throwable */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("这是输出时间之前"); // 调用代理对象的方法 Object o = methodProxy.invokeSuper(obj, args); System.out.println("这是输出时间之后"); return o; } }
测试类:
package com.mcj.music.cglibproxytest; import org.springframework.cglib.core.DebuggingClassWriter; import org.springframework.cglib.proxy.Enhancer; /** * @author mcj * @date 2022/11/5 20:01 * @description */ public class CglibProxy { public static void main(String[] args) { // 将生成的代理类保存到指定的目录下 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\Software\\work\\IDEA\\IdeaProject\\WebDemo\\music\\com\\sun\\proxy"); // 创建enhancer对象,类似于jdk动态代理的Proxy Enhancer enhancer = new Enhancer(); // 设置enhancer对象的父类,也就是目标类 enhancer.setSuperclass(Time.class); // 设置回调函数 enhancer.setCallback(new MyMethodInterceptor()); // 创建代理类 Time time = (Time) enhancer.create(); // 通过代理类调用方法 time.now(); time.now2(); } }
运行结果:
通过运行结果可以看出,被修饰的now2方法,是没有在执行前后输出字符串的,因此可以看出其没有被代理,同时也可以在生成的代理文件中进行查看,可以发现其中是没有now2这个方法的。
分析:
这边具体是怎么通过enhancer.create()
这个生成cglib的代理类,就不具体的看了,可以通过看jdk动态代理的方式一步一步的往下点来查看是怎么进行生成cglib的代理类的。这边直接看其生成的代理类。
其生成的代理类总共有三个,我们这边看不带FastClass的那个,其余两个带FastClass的则是FastClass机制,其中FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法,我们知道其中jdk是通过反射来获取方法的,效率会比较低,FastClass机制能够提高调用相应方法的效率。通过看其生成的动态代理类Time$$EnhancerByCGLIB$$33071dd7
可以看到其是继承了Time类public class Time$$EnhancerByCGLIB$$33071dd7 extends Time implements Factory {...}
在代理类中找到我们需要的now方法,源码如下:public final void now() { // 给方法拦截器进行赋值,这里是我们利用enhancer.setCallback()设置的自定义方法拦截器 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$now$0$Method, CGLIB$emptyArgs, CGLIB$now$0$Proxy); } else { super.now(); } }
可以看出这边是使用我们自定义的拦截器进行调用目标方法,如果没有的话则是直接使用拦截器的intercept()方法。另外我们在这个代理类中也没有找到now2()方法,这同样说明cglib不能代理final修饰的方法。
- 3.2.2 cglib动态代理
4.总结
代理主要分为静态代理和动态代理两大类,静态代理的灵活性不够,但是其逻辑比较清晰容易理解,动态代理灵活性很高,但是其逻辑理解起来比较困难。其中动态代理又分为jdk动态代理和cglib动态代理,jdk动态代理生成的代理类是继承了Proxy类,所以其只能代理实现了接口的类,而cglib动态代理是通过创建一个目标类的子类来进行实现的,所以其不能够代理final修饰的方法。
转: https://www.cnblogs.com/mcj123/p/16860633.html