Spring 中的 ProxyFactory 创建代理对象

一、jdk 动态代理 和 cglib动态代理 简单介绍 

        1.jdk动态代理

public interface AService {public String serviceA(String param);public String serviceAA(String param);
}
public interface BService {public String serviceB(String param);public String serviceBB(String param);
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class JdkCustomInvocationHandler implements InvocationHandler {@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {if(method.equals(AService.class.getMethod("serviceA" , String.class))){// 在这里可以写 当我们把代理当作 AService 的实现子类,调用 serviceA 方法时// 我们要执行的代码,这里我们姑且就简单打印,并返回一个值System.out.println("JdkCustomInvocationHandler 开始处理 com.fll.start.dynamic_proxy.jdk.AService.serviceA 方法");return "JdkCustomInvocationHandler 处理 com.fll.start.dynamic_proxy.jdk.BService.serviceA 的结果";}else if(method.equals(BService.class.getMethod("serviceB", String.class))){// 在这里可以写 当我们把代理当作 BService 的实现子类,调用 serviceB 方法时// 我们要执行的代码,这里我们姑且就简单打印,并返回一个值System.out.println("JdkCustomInvocationHandler 开始处理 com.fll.start.dynamic_proxy.jdk.BService.serviceB 方法");return "JdkCustomInvocationHandler 处理 com.fll.start.dynamic_proxy.jdk.BService.serviceB 的结果";}else {return "暂时对该方法没有处理逻辑";}}
}

这里总结下jdk动态代理的特点:
        1.可以不需要实现类,直接对接口进行代理
        2.创建代理对象时返回的类型时Object,但是可以将其强转为任何一个它所代理的接口类型
因为生成的代理对象是它所代理的所有接口的实现类
        3.当我们将代理对象强转为它所代理的接口类型进行方法调用时,所有的调用都会回调到InvocationHandler 对象的 invoke 方法,在回调 invoke 方法的参数中有我们调用的方法对象 Method method,和调用时所传递的所有参数Object[] objects,Object o就是代理对象本身
        4.关于这几个参数的注意点
                1.不能在 invoke 方法中将代理对象强转为它所代理的某一个接口,然后调用其方法,这          样会形成递归调用,造成栈内存溢出
                2.如果没有所代理的接口的真正实现类,不可以通过反射的方法调用该方法,因为通过            反射的方式,Method.invoke() 方法进行调用时,需要传递真正实现了该接口的实现类的一个          对象,这里的Object o对象虽然也是接口的实现类的对象,但是不能传递 o ,如果过传递                o,还是相当于调用了代理对象的方法,也会形成递归调用。
                3.要想让代理对象调用不同的方法时,分别执行我们想要的逻辑,只能在 invoke 方法的          回调中通过判断 Method method 对象的不同,执行不同业务逻辑
        5.这样的的代理方式没啥实际意义,只是把对所有接口方法的调用全部回调到了 InvocationHandler.invoke() 方法中,在invoke中进行分别实现,会造成代码冗长杂乱

 接下来常规用法

public class AServiceImpl implements AService{@Overridepublic String serviceA(String param){System.out.println("AServiceImpl 开始执行 serviceA,param=" + param);return "AServiceImpl.serviceA 处理的结果";}@Overridepublic String serviceAA(String param) {System.out.println("AServiceImpl 开始执行 serviceAA,param=" + param);return "AServiceImpl.serviceAA 处理的结果";}
}
public class BServiceImpl implements BService{@Overridepublic String serviceB(String param){System.out.println("BServiceImpl 开始执行 serviceB,param=" + param);return "BServiceImpl.serviceB 处理的结果";}@Overridepublic String serviceBB(String param) {System.out.println("BServiceImpl 开始执行 serviceBB,param=" + param);return "BServiceImpl.serviceBB 处理的结果";}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class JdkCustomInvocationHandler1 implements InvocationHandler {private AService aService;private BService bService;public JdkCustomInvocationHandler1(AService aService , BService bService){this.aService = aService;this.bService = bService;}@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {if(method.equals(AService.class.getMethod("serviceA" , String.class))){System.out.println(String.format("执行%s前置加强逻辑" , aService.getClass() +"."+ method.getName()));Object retVal = method.invoke(this.aService , objects);System.out.println(String.format("执行%s后置加强逻辑" , aService.getClass() +"."+ method.getName()));return retVal + "(也可以被代理对象再加工)";}else if(method.equals(BService.class.getMethod("serviceB", String.class))){System.out.println(String.format("执行%s前置加强逻辑" , bService.getClass() +"."+ method.getName()));Object retVal = method.invoke(this.bService , objects);System.out.println(String.format("执行%s后置加强逻辑" , bService.getClass() +"."+ method.getName()));return retVal + "(也可以被代理对象再加工)";}else {return "暂时对该方法没有处理逻辑";}}
}
public class JdkDynamicProxyTest1 {public static void main(String[] args) throws IOException {ClassLoader classLoader = JdkDynamicProxyTest1.class.getClassLoader();Class[] interfaces = {AService.class, BService.class};AService aService = new AServiceImpl();BService bService = new BServiceImpl();JdkCustomInvocationHandler1 jdkCustomInvocationHandler= new JdkCustomInvocationHandler1(aService , bService);AService aServiceProxy = (AService) Proxy.newProxyInstance(classLoader, interfaces, jdkCustomInvocationHandler);String test = aServiceProxy.serviceA("AService");System.out.println(test);System.out.println();BService bServiceProxy = (BService) Proxy.newProxyInstance(classLoader , interfaces , jdkCustomInvocationHandler);String test1 = bServiceProxy.serviceB("BService");System.out.println(test1);}}运行结果:
执行class com.fll.start.dynamic_proxy.jdk.AServiceImpl.serviceA前置加强逻辑
AServiceImpl 开始执行 serviceA,param=AService
执行class com.fll.start.dynamic_proxy.jdk.AServiceImpl.serviceA后置加强逻辑
AServiceImpl.serviceA 处理的结果(也可以被代理对象再加工)执行class com.fll.start.dynamic_proxy.jdk.BServiceImpl.serviceB前置加强逻辑
BServiceImpl 开始执行 serviceB,param=BService
执行class com.fll.start.dynamic_proxy.jdk.BServiceImpl.serviceB后置加强逻辑
BServiceImpl.serviceB 处理的结果(也可以被代理对象再加工)

          这次的特点:
                1.所代理的接口本来就有自己的实现类
                2.调用所代理的接口的方法时,最终都会在 InvocationHandler.invoke() 方法中通过反射           的方式调用到接口实现类对象的对应方法上,只不过我们可以在调用实现类方法之前或者之           后执行额外的逻辑,进行加强,也可以对实现类返回的结果进行再加工

        其实这种方式才是代理模式要达到的真正目的,本来就有实现好的功能,而且代码运行稳定,或者说一个黑盒子,我们只知道其功能和参数,这些情况下,我们想要对原本的功能或者黑盒子进行加强,但是又不想修改原来代码逻辑,所以就可以通过代理,在原来的功能之上 ,进行额外的加强处理

 通过jdk源码,看看代理对象是如何创建的
java.lang.reflect.Proxy#newProxyInstance

/*** Returns an instance of a proxy class for the specified interfaces* that dispatches method invocations to the specified invocation* handler.** <p>{@code Proxy.newProxyInstance} throws* {@code IllegalArgumentException} for the same reasons that* {@code Proxy.getProxyClass} does.** @param   loader the class loader to define the proxy class* @param   interfaces the list of interfaces for the proxy class*          to implement* @param   h the invocation handler to dispatch method invocations to* @return  a proxy instance with the specified invocation handler of a*          proxy class that is defined by the specified class loader*          and that implements the specified interfaces* @throws  IllegalArgumentException if any of the restrictions on the*          parameters that may be passed to {@code getProxyClass}*          are violated* @throws  SecurityException if a security manager, <em>s</em>, is present*          and any of the following conditions is met:*          <ul>*          <li> the given {@code loader} is {@code null} and*               the caller's class loader is not {@code null} and the*               invocation of {@link SecurityManager#checkPermission*               s.checkPermission} with*               {@code RuntimePermission("getClassLoader")} permission*               denies access;</li>*          <li> for each proxy interface, {@code intf},*               the caller's class loader is not the same as or an*               ancestor of the class loader for {@code intf} and*               invocation of {@link SecurityManager#checkPackageAccess*               s.checkPackageAccess()} denies access to {@code intf};</li>*          <li> any of the given proxy interfaces is non-public and the*               caller class is not in the same {@linkplain Package runtime package}*               as the non-public interface and the invocation of*               {@link SecurityManager#checkPermission s.checkPermission} with*               {@code ReflectPermission("newProxyInPackage.{package name}")}*               permission denies access.</li>*          </ul>* @throws  NullPointerException if the {@code interfaces} array*          argument or any of its elements are {@code null}, or*          if the invocation handler, {@code h}, is*          {@code null}*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/* * 生成代理类的 Class 对象* 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);}// 这里的private static final Class<?>[] constructorParams =// {InvocationHandler.class };// 通过代理类的 Class 对象获取参数为 InvocationHandler 对象的构造器对象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);}
}

java.lang.reflect.Proxy#getProxyClass0 

/*** Generate a proxy class.  Must call the checkProxyAccess method* to perform permission checks before calling this.*/
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {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// 如果被指定的类加载器定义好的,实现了指定接口的代理类 Class 对象已经存在了// 那就简单的返回缓存中的备份,相反如果不存在,就得通过ProxyClassFactory创建// 代理类的 Class 对象,并放入缓存中return proxyClassCache.get(loader, interfaces);
}

 java.lang.reflect.WeakCache#get

/*** Look-up the value through the cache. This always evaluates the* {@code subKeyFactory} function and optionally evaluates* {@code valueFactory} function if there is no entry in the cache for given* pair of (key, subKey) or the entry has already been cleared.** @param key       possibly null key* @param parameter parameter used together with key to create sub-key and*                  value (should not be null)* @return the cached value (never null)* @throws NullPointerException if {@code parameter} passed in or*                              {@code sub-key} calculated by*                              {@code subKeyFactory} or {@code value}*                              calculated by {@code valueFactory} is null.*/
public V get(K key, P parameter) {Objects.requireNonNull(parameter);expungeStaleEntries();Object cacheKey = CacheKey.valueOf(key, refQueue);// lazily install the 2nd level valuesMap for the particular cacheKeyConcurrentMap<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// 生成代理类Class对象的主要逻辑在 subKeyFactory.apply(key, parameter)Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));Supplier<V> supplier = valuesMap.get(subKey);Factory factory = null;while (true) {if (supplier != null) {// supplier might be a Factory or a CacheValue<V> instanceV 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 Factoryif (factory == null) {factory = new Factory(key, parameter, subKey, valuesMap);}if (supplier == null) {supplier = valuesMap.putIfAbsent(subKey, factory);if (supplier == null) {// successfully installed Factorysupplier = factory;}// else retry with winning supplier} else {if (valuesMap.replace(subKey, supplier, factory)) {// successfully replaced// cleared CacheEntry / unsuccessful Factory// with our Factorysupplier = factory;} else {// retry with current suppliersupplier = valuesMap.get(subKey);}}}
}

 java.lang.reflect.Proxy.ProxyClassFactory#apply

/*** A factory function that generates, defines and returns the proxy class given* the ClassLoader and array of interfaces.*/
private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>>
{// prefix for all proxy class namesprivate static final String proxyClassNamePrefix = "$Proxy";// next number to use for generation of unique proxy class namesprivate static final AtomicLong nextUniqueNumber = new AtomicLong();@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);for (Class<?> intf : interfaces) {/** Verify that the class loader resolves the name of this* interface to the same Class object.*/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");}/** Verify that the Class object actually represents an* interface.*/if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}/** Verify that this interface is not a duplicate.*/if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}String proxyPkg = null;     // package to define proxy class inint accessFlags = Modifier.PUBLIC | Modifier.FINAL;/** Record the package of a non-public proxy interface so that the* proxy class will be defined in the same package.  Verify that* all non-public proxy interfaces are in the same package.*/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) {// if no non-public proxy interfaces, use com.sun.proxy packageproxyPkg = ReflectUtil.PROXY_PACKAGE + ".";}/** Choose a name for the proxy class to generate.*/long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num;/** Generate the specified proxy class.* 生成代理类,我们看到生成代理类Class对象需要两部* 1.生成一个字节数组,其实等价于我们通过.class文件创建Class对象的时候,把.class文件            * 加载进内存,放到一个字节数组中* 2.通过 defineClass0() 方法把字节数组解析为一个Class对象*/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());}}
}

 我们看到可以通过以下代码生成一个等价于.class文件的字节数组,那我们把生成的这个字节数组输出到一个文件,是不是就相当于得到了这个代理类的 .class 文件了

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
import sun.misc.ProxyGenerator;import java.io.IOException;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;public class ProxyGeneratorTest {public static void main(String[] args) throws IOException {ClassLoader classLoader = JdkDynamicProxyTest1.class.getClassLoader();Class[] interfaces = {AService.class, BService.class};AService aService = new AServiceImpl();BService bService = new BServiceImpl();JdkCustomInvocationHandler1 jdkCustomInvocationHandler= new JdkCustomInvocationHandler1(aService , bService);Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, jdkCustomInvocationHandler);byte[] bytes = ProxyGenerator.generateProxyClass(proxyInstance.getClass().getSimpleName(), interfaces);Files.write(Paths.get("D:/springboot-demo/study-spring/src/main/java/com/fll/start/dynamic_proxy/jdk/" + proxyInstance.getClass().getSimpleName() + ".class") , bytes);}}

 以下是使用idea将生成的.class文件反编译之后得到的源码

import com.fll.start.dynamic_proxy.jdk.AService;
import com.fll.start.dynamic_proxy.jdk.BService;
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 AService, BService {private static Method m1;private static Method m4;private static Method m2;private static Method m6;private static Method m3;private static Method m0;private static Method m5;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 serviceA(String var1) throws  {try {return (String)super.h.invoke(this, m4, 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 String serviceBB(String var1) throws  {try {return (String)super.h.invoke(this, m6, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final String serviceAA(String var1) throws  {try {return (String)super.h.invoke(this, m3, 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);}}public final String serviceB(String var1) throws  {try {return (String)super.h.invoke(this, m5, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m4 = Class.forName("com.fll.start.dynamic_proxy.jdk.AService").getMethod("serviceA", Class.forName("java.lang.String"));m2 = Class.forName("java.lang.Object").getMethod("toString");m6 = Class.forName("com.fll.start.dynamic_proxy.jdk.BService").getMethod("serviceBB", Class.forName("java.lang.String"));m3 = Class.forName("com.fll.start.dynamic_proxy.jdk.AService").getMethod("serviceAA", Class.forName("java.lang.String"));m0 = Class.forName("java.lang.Object").getMethod("hashCode");m5 = Class.forName("com.fll.start.dynamic_proxy.jdk.BService").getMethod("serviceB", Class.forName("java.lang.String"));} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

这里注意最好用 jdk1.8,在jdk11 中,这个方法就不是public了,测试调用的话比较麻烦 

可以看到这里的代理类 除了实现了 我们要代理的接口 AService, BService之外还结成了java.lang.reflect.Proxy,我们使用代理类的构造器创建代理对象时,所传的 构造器参数InvocationHandler 就是通过 super(InvocationHandler) 传到了java.lang.reflect.Proxy

        2.cglib动态代理

public class CustomCglibMethodInterceptor<T> implements MethodInterceptor {private T target;public CustomCglibMethodInterceptor(T target){this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println(String.format("执行%s前置加强逻辑" , target.getClass() +"."+ method.getName()));Object invoke = method.invoke(target, objects);methodProxy.invoke(target , objects);System.out.println(String.format("执行%s后置加强逻辑" , target.getClass() +"."+ method.getName()));return invoke + "(也可以被代理对象再加工)";}}
public class CglibDynamicProxyTest {public static void main(String[] args) {AService aService = new AServiceImpl();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(aService.getClass());
//        enhancer.setSuperclass(AService.class);enhancer.setCallback(new CustomCglibMethodInterceptor(aService));AService aServiceProxy = (AService) enhancer.create();String s = aServiceProxy.serviceA("test-serviceA");System.out.println(s);}}运行结果:
执行class com.fll.start.dynamic_proxy.AServiceImpl.serviceA前置加强逻辑
AServiceImpl 开始执行 serviceA,param=test-serviceA
AServiceImpl 开始执行 serviceA,param=test-serviceA
执行class com.fll.start.dynamic_proxy.AServiceImpl.serviceA后置加强逻辑
AServiceImpl.serviceA 处理的结果(也可以被代理对象再加工)

        总结一下 cglib 动态代理的特点:

                1. cglib既能代理接口,又能代理实现类
                2. 如果代理的时接口,那么生成的代理类就会实现该接口,如果代理的时实现类,那么         生成代理类就会继承该类,由于java的类之间只支持单继承,所以一个 cglib 代理类只能代理         一个接口,或者代理一个实现类
                3. 在 cglib 的MethodInterceptor 实现类中,调用目标对象的有两种方法                                            method.invoke(target, objects);
                methodProxy.invoke(target , objects);

那么 cglib 的原理是什么呢,我们能不能也想上面反编译jdk动态代理类字节码一样,反编译一下cglib的代理类的字节码呢,我自己想了个办法还是成功反编译了
org.springframework.cglib.core.AbstractClassGenerator#generate

protected Class generate(ClassLoaderData data) {Class gen;Object save = CURRENT.get();CURRENT.set(this);try {ClassLoader classLoader = data.getClassLoader();if (classLoader == null) {throw new IllegalStateException("ClassLoader is null while trying to define class " +getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +"Please file an issue at cglib's issue tracker.");}synchronized (classLoader) {String name = generateClassName(data.getUniqueNamePredicate());data.reserveName(name);this.setClassName(name);}if (attemptLoad) {try {gen = classLoader.loadClass(getClassName());return gen;}catch (ClassNotFoundException e) {// ignore}}byte[] b = strategy.generate(this);String className = ClassNameReader.getClassName(new ClassReader(b));ProtectionDomain protectionDomain = getProtectionDomain();synchronized (classLoader) { // just in case// SPRING PATCH BEGINgen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain, contextClass);// SPRING PATCH END}return gen;}catch (RuntimeException | Error ex) {throw ex;}catch (Exception ex) {throw new CodeGenerationException(ex);}finally {CURRENT.set(save);}}

Files.write(Paths.get("D:/springboot-demo/study-spring/src/main/java/com/fll/start/dynamic_proxy/cglib/" + "AServiceImpl$$EnhancerByCGLIB$$bfa5b612.class") , b) 

我们可以看到Files 和 Paths 都爆红,可以想正常写代码那样导入包就行了

解决了报错之后,直接点击右下角的 Evaluate 就可以了,然后刷新一下你输出的包,就会出现你输出的 .class 文件

 使用 idea 打开就可以看到反编译后的代理类源码

package com.fll.start.dynamic_proxy;import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;public class AServiceImpl$$EnhancerByCGLIB$$bfa5b612 extends AServiceImpl 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$serviceA$0$Method;private static final MethodProxy CGLIB$serviceA$0$Proxy;private static final Object[] CGLIB$emptyArgs;private static final Method CGLIB$serviceAA$1$Method;private static final MethodProxy CGLIB$serviceAA$1$Proxy;private static final Method CGLIB$equals$2$Method;private static final MethodProxy CGLIB$equals$2$Proxy;private static final Method CGLIB$toString$3$Method;private static final MethodProxy CGLIB$toString$3$Proxy;private static final Method CGLIB$hashCode$4$Method;private static final MethodProxy CGLIB$hashCode$4$Proxy;private static final Method CGLIB$clone$5$Method;private static final MethodProxy CGLIB$clone$5$Proxy;static void CGLIB$STATICHOOK1() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class var0 = Class.forName("com.fll.start.dynamic_proxy.AServiceImpl$$EnhancerByCGLIB$$bfa5b612");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$2$Method = var10000[0];CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");CGLIB$toString$3$Method = var10000[1];CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");CGLIB$hashCode$4$Method = var10000[2];CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");CGLIB$clone$5$Method = var10000[3];CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");var10000 = ReflectUtils.findMethods(new String[]{"serviceA", "(Ljava/lang/String;)Ljava/lang/String;", "serviceAA", "(Ljava/lang/String;)Ljava/lang/String;"}, (var1 = Class.forName("com.fll.start.dynamic_proxy.AServiceImpl")).getDeclaredMethods());CGLIB$serviceA$0$Method = var10000[0];CGLIB$serviceA$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "serviceA", "CGLIB$serviceA$0");CGLIB$serviceAA$1$Method = var10000[1];CGLIB$serviceAA$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "serviceAA", "CGLIB$serviceAA$1");}final String CGLIB$serviceA$0(String var1) {return super.serviceA(var1);}public final String serviceA(String var1) {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}return var10000 != null ? (String)var10000.intercept(this, CGLIB$serviceA$0$Method, new Object[]{var1}, CGLIB$serviceA$0$Proxy) : super.serviceA(var1);}final String CGLIB$serviceAA$1(String var1) {return super.serviceAA(var1);}public final String serviceAA(String var1) {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}return var10000 != null ? (String)var10000.intercept(this, CGLIB$serviceAA$1$Method, new Object[]{var1}, CGLIB$serviceAA$1$Proxy) : super.serviceAA(var1);}final boolean CGLIB$equals$2(Object var1) {return super.equals(var1);}public final boolean equals(Object var1) {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if (var10000 != null) {Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy);return var2 == null ? false : (Boolean)var2;} else {return super.equals(var1);}}final String CGLIB$toString$3() {return super.toString();}public final String toString() {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString();}final int CGLIB$hashCode$4() {return super.hashCode();}public final int hashCode() {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if (var10000 != null) {Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);return var1 == null ? 0 : ((Number)var1).intValue();} else {return super.hashCode();}}final Object CGLIB$clone$5() throws CloneNotSupportedException {return super.clone();}protected final Object clone() throws CloneNotSupportedException {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}return var10000 != null ? var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone();}public static MethodProxy CGLIB$findMethodProxy(Signature var0) {String var10000 = var0.toString();switch(var10000.hashCode()) {case -2021511251:if (var10000.equals("serviceA(Ljava/lang/String;)Ljava/lang/String;")) {return CGLIB$serviceA$0$Proxy;}break;case -508378822:if (var10000.equals("clone()Ljava/lang/Object;")) {return CGLIB$clone$5$Proxy;}break;case 386645590:if (var10000.equals("serviceAA(Ljava/lang/String;)Ljava/lang/String;")) {return CGLIB$serviceAA$1$Proxy;}break;case 1826985398:if (var10000.equals("equals(Ljava/lang/Object;)Z")) {return CGLIB$equals$2$Proxy;}break;case 1913648695:if (var10000.equals("toString()Ljava/lang/String;")) {return CGLIB$toString$3$Proxy;}break;case 1984935277:if (var10000.equals("hashCode()I")) {return CGLIB$hashCode$4$Proxy;}}return null;}public AServiceImpl$$EnhancerByCGLIB$$bfa5b612() {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) {AServiceImpl$$EnhancerByCGLIB$$bfa5b612 var1 = (AServiceImpl$$EnhancerByCGLIB$$bfa5b612)var0;if (!var1.CGLIB$BOUND) {var1.CGLIB$BOUND = true;Object var10000 = CGLIB$THREAD_CALLBACKS.get();if (var10000 == null) {var10000 = CGLIB$STATIC_CALLBACKS;if (var10000 == null) {return;}}var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];}}public Object newInstance(Callback[] var1) {CGLIB$SET_THREAD_CALLBACKS(var1);AServiceImpl$$EnhancerByCGLIB$$bfa5b612 var10000 = new AServiceImpl$$EnhancerByCGLIB$$bfa5b612();CGLIB$SET_THREAD_CALLBACKS((Callback[])null);return var10000;}public Object newInstance(Callback var1) {CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});AServiceImpl$$EnhancerByCGLIB$$bfa5b612 var10000 = new AServiceImpl$$EnhancerByCGLIB$$bfa5b612();CGLIB$SET_THREAD_CALLBACKS((Callback[])null);return var10000;}public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {CGLIB$SET_THREAD_CALLBACKS(var3);AServiceImpl$$EnhancerByCGLIB$$bfa5b612 var10000 = new AServiceImpl$$EnhancerByCGLIB$$bfa5b612;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 的代理类源码 和 jdk 动态代理的 代理类源码 实现原理都差不多,都是先把被代理的类或者被代理接口所有的方法的 Method 对象取出来作为代理类的成员变量,当调用代理类的对应方法时,就在成员变量中找到对应的 Method 对象 和 和传进来的参数,一起调用一个 回调方法,这个回调方法是我们自己实现的,所以我们可以在回调方法中按照自己的想法来实现

二、Spring中的 三个重要的概念 Advice  Advisor  Advised

        1.Advice

         Advice 是AOP中的核心概念之一,它定义了在特定的连接点(Join point)上执行的行为。简单来说,Advice 就是你希望在方法调用前、后、异常抛出时等不同时机下执行的代码。Spring AOP 支持多种类型的 Advice,包括但不限于:

Before advice:在方法调用之前执行的通知。
After returning advice:在方法成功返回结果之后执行的通知。
After throwing advice:在方法抛出异常之后执行的通知。
After (finally) advice:无论方法是否正常完成,都会执行的通知。
Around advice:包围方法调用的通知,可以控制方法是否执行以及何时执行

        2.Pointcut

        Pointcut定义了在何处应用通知(Advice)。它指定了程序执行过程中的一个或多个连接点(JoinPoint),在这些连接点上将执行特定的通知。

        Pointcut用于定义切入点集合,即哪些连接点会被拦截。
        它可以是一个具体的方法、一组方法、类中的所有方法、包中的所有方法等。
        Pointcut可以使用表达式来定义,例如使用AspectJ的切点表达式。

        Pointcut可以理解为一个连接点的过滤规则,我们定义的每个方法都有做为连接点的可能,但          是最后哪些方法会成为我们想要添加额外的 Advice 的方法呢,这就需要具体的 Pointcut 实            现进行过滤,针对包名过滤,针对类名过滤,针对方法名过滤。这种过滤不一定是相等比较
        ,也可以是通过一个正则表达式进行匹配

        3.JoinPoint

        JoinPoint可以理解为Pointcut筛选出来的一个具体的执行点(也就是要被加强的那些方法),在这个点上要执行额外的Advice逻辑,只要满足Pointcut规则的方法都可以成为JoinPoint,在执行Advice逻辑的时候,JoinPoint会作为参数被传到Advice方法中,作为执行Advice的上下文,我们在Advice方法中通过JoinPoint获取到要被加强的方法的所有信息,包括 Method对象,参数,返回值

通过 Pointcut,你可以精确地控制 Advice 的应用范围,而 JoinPoint 则提供了在 Advice 中访问当前执行点信息的能力。两者共同作用,使得Spring AOP能够灵活地实现横切关注点的管理。

        4.Advisor

Advisor 可以看作是 Advice 的封装,它不仅包含了 Advice,还可能包含一个切入点(Pointcut),用于指定 Advice 应该应用到哪些连接点。如果一个 Advisor 没有指定切入点,那么它的 Advice 将被应用到所有连接点。常见的 Advisor 类型有:

DefaultPointcutAdvisor:最常用的 Advisor,允许同时指定一个切入点和一个通知。
RegexpMethodPointcutAdvisor:基于正则表达式匹配方法名来确定切入点,默认是 JdkRegexpMethodPointcut。
NameMatchMethodPointcutAdvisor:根据方法名称匹配来确定切入点。
AspectJExpressionPointcutAdvisor: 根据AspectJ表达式匹配包名,类名,方法名

        5.Advised

这个接口稍微复杂点,这里举个例子说明一下,便于自己以后看的时候好理解,好回忆。
就以jdk动态代理为例,在创建 jdk 动态代理的时候,需要传递一个InvocationHandler的实现类对象,和被代理的接口数组。当我们将代理对象转换为被代理的某一个接口类型 UserService,调用其方法时 addUser(User user),就会回调我们创建代理对象时传递的那个 InvocationHandler 对象的 invoke 方法。回调 invoke(Object proxy, Method method, Object[] args)时,代理对象会传递要调用的目标方法Method method,和实际调用代理对象时传的参数。我们可以在 invoke() 方法对被代理接口的一个实现类 UserServiceImpl 的对象进行加强。要对 UserServiceImpl 的对象进行加强我们有几个需求要满足:

        1.需要被加强的 UserServiceImpl 实例
        2.需要除了 UserServiceImpl.addUser(User user) 自己的业务以外,额外添加的加强行为Advice,这里可以添加多个Advice
        3.需要一个组件能够根据配置,判断有哪些Advice需要被应用到当前方法Method来对其进行加强,其实Advisor可以解决2和3这两个问题,因为Advisor同时封装可 Pointcut 和 Advice

所以在创建 InvocationHandler 实例的时候,就需要把这些信息传给这个 InvocationHandler 的实例,这样在回调到它的 invoke 方法的时候,他就能获取到这些信息了
Spring Aop 中创造代理时传的 InvocationHandler 实现类是 JdkDynamicAopProxy 构造函数

/*** Construct a new JdkDynamicAopProxy for the given AOP configuration.* @param config the AOP configuration as AdvisedSupport object* @throws AopConfigException if the config is invalid. We try to throw an informative* exception in this case, rather than let a mysterious failure happen later.*/
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {Assert.notNull(config, "AdvisedSupport must not be null");if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {throw new AopConfigException("No advisors and no TargetSource specified");}this.advised = config;this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
}

 构造参数传的是一个 AdvisedSupport 的对象,说明这个对象能解决我上面提出的三个需求

/*** Implementation of {@code InvocationHandler.invoke}.* <p>Callers will see exactly the exception thrown by the target,* unless a hook method throws an exception.*/
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;TargetSource targetSource = this.advised.targetSource;Object target = null;try {if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {// The target does not implement the equals(Object) method itself.return equals(args[0]);}else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {// The target does not implement the hashCode() method itself.return hashCode();}else if (method.getDeclaringClass() == DecoratingProxy.class) {// There is only getDecoratedClass() declared -> dispatch to proxy config.return AopProxyUtils.ultimateTargetClass(this.advised);}else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {// Service invocations on ProxyConfig with the proxy config...return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}Object retVal;if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// Get as late as possible to minimize the time we "own" the target,// in case it comes from a pool.target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);// Get the interception chain for this method.List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// Check whether we have any advice. If we don't, we can fallback on direct// reflective invocation of the target, and avoid creating a MethodInvocation.if (chain.isEmpty()) {// We can skip creating a MethodInvocation: just invoke the target directly// Note that the final invoker must be an InvokerInterceptor so we know it does// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {// We need to create a method invocation...MethodInvocation invocation =new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// Proceed to the joinpoint through the interceptor chain.retVal = invocation.proceed();}// Massage return value if necessary.Class<?> returnType = method.getReturnType();if (retVal != null && retVal == target &&returnType != Object.class && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// Special case: it returned "this" and the return type of the method// is type-compatible. Note that we can't help if the target sets// a reference to itself in another returned object.retVal = proxy;}else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);}return retVal;}finally {if (target != null && !targetSource.isStatic()) {// Must have come from TargetSource.targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}
}

 我们看到在 invoke 方法中,通过 this.advised 这个 AdvisedSupport 对象确实解决了了我们上面提到的三个需求

1.获取被加强的实例
TargetSource targetSource = this.advised.targetSource;
Object target = null;
target = targetSource.getTarget();
2.获取需要被应用到当前Method的 Advice,这里把需要应用到当前Method的Advice封装成了不同的MethodInterceptor 实现类对象。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
这里其实就解决了上面的 2,3 两个需求,说明 advised 也包含获取到要创建的代理对象需要用的所有Advisor
3.在invoke方法中发现,还从advised对象中获取了其他的一些配置信息
4.在下面创建代理对象的时候,传入的需要被代理的接口数组也是构造方法中从advised中解析出来的
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());}return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

 这样看来Advised其实就是一个配置信息管理类,可以配置管理 1.被代理的对象(被加强的对象),2.Advisor(用来获取当前被调的方法需要被额外加强的Advice) 3.被代理的接口数组(创建代理对象时需要) 4.另外在其他地方也有用到 AdvisedSupport 的其他配置,比如,proxyTargetClass,optimize,opaque,exposeProxy 其实这些属性是AdvisedSupport从ProxyConfig继承来的

这么看来AdvisedSupport是综合了 Advised 和 ProxyConfig 的更强大的配置类,Advised提供了target和Advisor配置和管理,ProxyConfig提供了proxyTargetClass,optimize,opaque,exposeProxy等的配置和管理

三、ProxyFactory 类的继承结构,以及每一层中实现的功能

 ProxyFactory 这一层自己没有定义任何属性,可以通过在构造方法中调用父类AdvisedSupport的配置方法,配置代理目标targetSource, 被代理的接口 proxyInterfaces,Advice


/*** Base class for proxy factories.* Provides convenient access to a configurable AopProxyFactory.** @author Juergen Hoeller* @since 2.0.3* @see #createAopProxy()*/
@SuppressWarnings("serial")
public class ProxyCreatorSupport extends AdvisedSupport {private AopProxyFactory aopProxyFactory;private final List<AdvisedSupportListener> listeners = new ArrayList<>();/** Set to true when the first AOP proxy has been created. */private boolean active = false;/*** Create a new ProxyCreatorSupport instance.*/public ProxyCreatorSupport() {this.aopProxyFactory = new DefaultAopProxyFactory();}/*** Create a new ProxyCreatorSupport instance.* @param aopProxyFactory the AopProxyFactory to use*/public ProxyCreatorSupport(AopProxyFactory aopProxyFactory) {Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");this.aopProxyFactory = aopProxyFactory;}/*** Customize the AopProxyFactory, allowing different strategies* to be dropped in without changing the core framework.* <p>Default is {@link DefaultAopProxyFactory}, using dynamic JDK* proxies or CGLIB proxies based on the requirements.*/public void setAopProxyFactory(AopProxyFactory aopProxyFactory) {Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");this.aopProxyFactory = aopProxyFactory;}/*** Return the AopProxyFactory that this ProxyConfig uses.*/public AopProxyFactory getAopProxyFactory() {return this.aopProxyFactory;}/*** Add the given AdvisedSupportListener to this proxy configuration.* @param listener the listener to register*/public void addListener(AdvisedSupportListener listener) {Assert.notNull(listener, "AdvisedSupportListener must not be null");this.listeners.add(listener);}/*** Remove the given AdvisedSupportListener from this proxy configuration.* @param listener the listener to deregister*/public void removeListener(AdvisedSupportListener listener) {Assert.notNull(listener, "AdvisedSupportListener must not be null");this.listeners.remove(listener);}/*** Subclasses should call this to get a new AOP proxy. They should <b>not</b>* create an AOP proxy with {@code this} as an argument.*/protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);}/*** Activate this proxy configuration.* @see AdvisedSupportListener#activated*/private void activate() {this.active = true;for (AdvisedSupportListener listener : this.listeners) {listener.activated(this);}}/*** Propagate advice change event to all AdvisedSupportListeners.* @see AdvisedSupportListener#adviceChanged*/@Overrideprotected void adviceChanged() {super.adviceChanged();synchronized (this) {if (this.active) {for (AdvisedSupportListener listener : this.listeners) {listener.adviceChanged(this);}}}}/*** Subclasses can call this to check whether any AOP proxies have been created yet.*/protected final synchronized boolean isActive() {return this.active;}}
ProxyCreatorSupport 这一层定义了private AopProxyFactory aopProxyFactory; 并提供了方便的配置方法。对子类暴露了
/*** Subclasses should call this to get a new AOP proxy. They should <b>not</b>* create an AOP proxy with {@code this} as an argument.*/
protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);
}

主要就是调用AopProxyFactory.createAopProxy(AdvisedSupport config) 方法创建代理类,可见ProxyCreatorSupport 自己没有实现任何主要功能,只是提供了配置AopProxyFactory的方法,和调用AopProxyFactory的方法创建代理对象。这里调用AopProxyFactory.createAopProxy(AdvisedSupport config)的时候,传了 this ,这是把ProxyCreatorSupport当作一个AdvisedSupport对象来用了,因为ProxyCreatorSupport继承了AdvisedSupport,并且AdvisedSupport可以提供AopProxyFactory创建代理对象所需的所有配置信息

四、AopProxyFactory的默认实现类DefaultAopProxyFactory的介绍

/*** Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy* or a JDK dynamic proxy.** <p>Creates a CGLIB proxy if one the following is true for a given* {@link AdvisedSupport} instance:* <ul>* <li>the {@code optimize} flag is set* <li>the {@code proxyTargetClass} flag is set* <li>no proxy interfaces have been specified* </ul>** <p>In general, specify {@code proxyTargetClass} to enforce a CGLIB proxy,* or specify one or more interfaces to use a JDK dynamic proxy.** @author Rod Johnson* @author Juergen Hoeller* @author Sebastien Deleuze* @author Sam Brannen* @since 12.03.2004* @see AdvisedSupport#setOptimize* @see AdvisedSupport#setProxyTargetClass* @see AdvisedSupport#setInterfaces*/
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {private static final long serialVersionUID = 7930414337282325166L;@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {// proxyTargetClass == true 表示除了可以代理接口,还可以代理类,如果是targetClass是类,就通过cglib创建// 类的代理// hasNoUserSuppliedProxyInterfaces(config) == true 需要通过判断// targetClass 是接口或者类来决定 创建jdk动态代理,还是cglib动态代理Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}// 如果代理的目标类是一个接口 或者 目标类本身也是一个代理类 或者 是Lambda表达式 创建jdk动态代理if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}/*** Determine whether the supplied {@link AdvisedSupport} has only the* {@link org.springframework.aop.SpringProxy} interface specified* (or no proxy interfaces specified at all).*/private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {Class<?>[] ifcs = config.getProxiedInterfaces();return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));}}

五、Spring 中的 InvocationHandler --- JdkDynamicAopProxy

DefaultAopProxyFactory.createAopProxy(AdvisedSupport config)方法返回了一个AopProxy对象,AopProxy对象可能是一个JdkDynamicAopProxy对象也可能是一个ObjenesisCglibAopProxy对象
JdkDynamicAopProxy和CglibAopProxy都对AopProxy.getProxy()方法进行了实现,获取一个代理对象。
JdkDynamicAopProxy和CglibAopProxy都持有AdvisedSupport实例,所以他两都能获取到创建代理对象所需要的所有配置这里重点说一下 JdkDynamicAopProxy,JdkDynamicAopProxy还继承了jdk动态代理中的InvocationHandler,并且在它自己实现的创建jdk动态代理的方法中,把自己作为InvocationHandler实例传了进去,所以当我们调用这个JdkDynamicAopProxy创建的代理对象的方法时,就会回调这个JdkDynamicAopProxy的invoke方法,在invoke方法中 需要用到的 targetSource , Advisor 和其他的配置都可以从它持有的AdvisedSupport实例中获取

六、AdvisedSupport .getInterceptorsAndDynamicInterceptionAdvice()

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {MethodCacheKey cacheKey = new MethodCacheKey(method);List<Object> cached = this.methodCache.get(cacheKey);if (cached == null) {cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);this.methodCache.put(cacheKey, cached);}return cached;
}

 可以看到调用advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this, method, targetClass);的时候,传递了this ,this就是当前的AdvisedSupport对象,可以从这获取到Advisor,还传递了Method对象和目标对象的类型 targetClass。这样就可以通过用Advisor中的Pointcut对象,对 Method 和 targetClass 进行匹配,如果匹配成功了,那么这个Advisor中的Advice就得加入到本次调用的 MethodInterceptor 调用链中

七、AdvisorChainFactory 的默认实现类 DefaultAdvisorChainFactory

/*** A simple but definitive way of working out an advice chain for a Method,* given an {@link Advised} object. Always rebuilds each advice chain;* caching can be provided by subclasses.** @author Juergen Hoeller* @author Rod Johnson* @author Adrian Colyer* @since 2.0.3*/
@SuppressWarnings("serial")
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {@Overridepublic List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {// This is somewhat tricky... We have to process introductions first,// but we need to preserve order in the ultimate list.AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();//从AdvisedSupport实例中获取配置了的所有AdvisorAdvisor[] advisors = config.getAdvisors();//这里创建要返回的 MethodInterceptor 集合,注意这里设定了长度//这样可以节省内存,interceptorList最终的size最大也就是跟advisors.length差不多//就算所有的Advisor中的Pointcut都匹配成功,也就是把所有Advisor中的Advice//封装成 MethodInterceptor 添加到interceptorList中,不过在registry.getInterceptors(advisor)//中,一个Advice有可能被多个AdvisorAdapter支持,被封装到多个 MethodInterceptorList<Object> interceptorList = new ArrayList<>(advisors.length);// 获取要代理的Class,用于 PointcutAdvisor 进行类型匹配Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());Boolean hasIntroductions = null;for (Advisor advisor : advisors) {if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;// config.isPreFiltered()确定代理配置是否已经预先过滤,仅包含适用于该代理目标类的顾问(advisors)// 如果预先过滤了,就不需要匹配代理目标类了,直接匹配方法// pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass) 用PointcutAdvisor中配置的// Pointcut 对代理目标类进行匹配if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {// 获取Pointcut的方法匹配器MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();boolean match;// 这是一种特殊的方法匹配器,匹配的时候还会考虑 introductions// 如果targetClass上没有introductions,方法的匹配效率会更高if (mm instanceof IntroductionAwareMethodMatcher) {if (hasIntroductions == null) {hasIntroductions = hasMatchingIntroductions(advisors, actualClass);}match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);}else {// 非 IntroductionAwareMethodMatcher 匹配器匹配match = mm.matches(method, actualClass);}if (match) {// 如果方法也匹配成功,就需要这个 Advisor 中的Advice封装成 MethodInterceptor// 这里一个Advice有可能被多个AdvisorAdapter支持,被封装到多个 MethodInterceptorMethodInterceptor[] interceptors = registry.getInterceptors(advisor);if (mm.isRuntime()) {// Creating a new object instance in the getInterceptors() method// isn't a problem as we normally cache created chains.for (MethodInterceptor interceptor : interceptors) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));}}else {interceptorList.addAll(Arrays.asList(interceptors));}}}}else if (advisor instanceof IntroductionAdvisor) {// 如果时 IntroductionAdvisor 只匹配代理目标类就可以了IntroductionAdvisor ia = (IntroductionAdvisor) advisor;if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {Interceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}else {Interceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}return interceptorList;}/*** Determine whether the Advisors contain matching introductions.*/private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {for (Advisor advisor : advisors) {if (advisor instanceof IntroductionAdvisor) {IntroductionAdvisor ia = (IntroductionAdvisor) advisor;if (ia.getClassFilter().matches(actualClass)) {return true;}}}return false;}}

八、AdvisorAdapterRegistry 的 默认实现类 DefaultAdvisorAdapterRegistry

/*** Default implementation of the {@link AdvisorAdapterRegistry} interface.* Supports {@link org.aopalliance.intercept.MethodInterceptor},* {@link org.springframework.aop.MethodBeforeAdvice},* {@link org.springframework.aop.AfterReturningAdvice},* {@link org.springframework.aop.ThrowsAdvice}.** @author Rod Johnson* @author Rob Harrop* @author Juergen Hoeller*/
@SuppressWarnings("serial")
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {private final List<AdvisorAdapter> adapters = new ArrayList<>(3);/*** Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.*/public DefaultAdvisorAdapterRegistry() {// 先注册三个 AdviceAdapter ,用于后面判断 advisor 中 Advice// 应该被封装为哪一种类型的 MethodInterceptorregisterAdvisorAdapter(new MethodBeforeAdviceAdapter());registerAdvisorAdapter(new AfterReturningAdviceAdapter());registerAdvisorAdapter(new ThrowsAdviceAdapter());}@Overridepublic Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {if (adviceObject instanceof Advisor) {return (Advisor) adviceObject;}if (!(adviceObject instanceof Advice)) {throw new UnknownAdviceTypeException(adviceObject);}Advice advice = (Advice) adviceObject;if (advice instanceof MethodInterceptor) {// So well-known it doesn't even need an adapter.return new DefaultPointcutAdvisor(advice);}for (AdvisorAdapter adapter : this.adapters) {// Check that it is supported.if (adapter.supportsAdvice(advice)) {return new DefaultPointcutAdvisor(advice);}}throw new UnknownAdviceTypeException(advice);}@Overridepublic MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<>(3);Advice advice = advisor.getAdvice();// 如果添加的 advice 本来就是MethodInterceptor 类型的// 直接添加到调用链中if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}// 遍历三个转换器,判断 advice 应该被封装为哪一种类型的 MethodInterceptor,// 封装完了之后,添加到调用链中for (AdvisorAdapter adapter : this.adapters) {if (adapter.supportsAdvice(advice)) {interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());}return interceptors.toArray(new MethodInterceptor[0]);}@Overridepublic void registerAdvisorAdapter(AdvisorAdapter adapter) {this.adapters.add(adapter);}}/*** Adapter to enable {@link org.springframework.aop.MethodBeforeAdvice}* to be used in the Spring AOP framework.** @author Rod Johnson* @author Juergen Hoeller*/
@SuppressWarnings("serial")
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof MethodBeforeAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();return new MethodBeforeAdviceInterceptor(advice);}}/*** Adapter to enable {@link org.springframework.aop.AfterReturningAdvice}* to be used in the Spring AOP framework.** @author Rod Johnson* @author Juergen Hoeller*/
@SuppressWarnings("serial")
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof AfterReturningAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();return new AfterReturningAdviceInterceptor(advice);}}/*** Adapter to enable {@link org.springframework.aop.ThrowsAdvice} to be used* in the Spring AOP framework.** @author Rod Johnson* @author Juergen Hoeller*/
@SuppressWarnings("serial")
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof ThrowsAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {return new ThrowsAdviceInterceptor(advisor.getAdvice());}}

 我们看到Advice有可能被封装成,MethodBeforeAdviceInterceptor,AfterReturningAdviceInterceptor,ThrowsAdviceInterceptor ,也有可能本来就是MethodInterceptor类型的

public class AspectJAfterReturningAdvice extends AbstractAspectJAdviceimplements AfterReturningAdvice, AfterAdvice, Serializable {public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {public class AspectJAfterThrowingAdvice extends AbstractAspectJAdviceimplements MethodInterceptor, AfterAdvice, Serializable {public class AspectJAfterAdvice extends AbstractAspectJAdviceimplements MethodInterceptor, AfterAdvice, Serializable {
通知类型实现的接口支持的AdviceAdapter被封装的MethodInterceptor
AspectJMethodBeforeAdviceMethodBeforeAdviceMethodBeforeAdviceAdapterMethodBeforeAdviceInterceptor
AspectJAfterReturningAdviceAfterReturningAdviceAfterReturningAdviceAdapterAfterReturningAdviceInterceptor
AspectJAfterAdviceMethodInterceptor直接使用MethodInterceptor本身
AspectJAfterThrowingAdviceMethodInterceptor直接使用MethodInterceptor本身
AspectJAroundAdviceMethodInterceptor直接使用MethodInterceptor本身

我们看到在基于AspectJ的这五种Advice中没有实现ThrowsAdvice的, ThrowsAdviceAdapter 和 ThrowsAdviceInterceptor 没有用到,其实我们可以自己来定义ThrowsAdvice实现

public class CustomThrowsAdvice implements ThrowsAdvice {public Object afterThrowing(ArithmeticException exeption){System.out.println("CustomThrowsAdvice.afterThrowing()");return exeption;}public Object afterThrowing(Method method , Object[] args , Object target , ArithmeticException exeption){System.out.println("CustomThrowsAdvice.afterThrowing()================");return exeption;}}
/*** Interceptor to wrap an after-throwing advice.** <p>The signatures on handler methods on the {@code ThrowsAdvice}* implementation method argument must be of the form:<br>** {@code void afterThrowing([Method, args, target], ThrowableSubclass);}** <p>Only the last argument is required.** <p>Some examples of valid methods would be:** <pre class="code">public void afterThrowing(Exception ex)</pre>* <pre class="code">public void afterThrowing(RemoteException)</pre>* <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>* <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>** <p>This is a framework class that need not be used directly by Spring users.** @author Rod Johnson* @author Juergen Hoeller* @see MethodBeforeAdviceInterceptor* @see AfterReturningAdviceInterceptor*/
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {private static final String AFTER_THROWING = "afterThrowing";private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class);private final Object throwsAdvice;/** Methods on throws advice, keyed by exception class. */private final Map<Class<?>, Method> exceptionHandlerMap = new HashMap<>();/*** Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.* @param throwsAdvice the advice object that defines the exception handler methods* (usually a {@link org.springframework.aop.ThrowsAdvice} implementation)*/public ThrowsAdviceInterceptor(Object throwsAdvice) {Assert.notNull(throwsAdvice, "Advice must not be null");this.throwsAdvice = throwsAdvice;//创建 ThrowsAdviceInterceptor 的时候,先把传进来的ThrowsAdvice实例进行解析//获取实现类的所有方法进行遍历Method[] methods = throwsAdvice.getClass().getMethods();for (Method method : methods) {// 只有method.getName() == afterThrowing 并且 参数为1个或者四个的方法才可以if (method.getName().equals(AFTER_THROWING) &&(method.getParameterCount() == 1 || method.getParameterCount() == 4)) {// 获取方法的最后一个参数的具体类型Class<?> throwableParam = method.getParameterTypes()[method.getParameterCount() - 1];// 如果最后一个参数是 Throwable 的子类,就将这种Throwable的具体类型和这个afterThrowing方法做映射if (Throwable.class.isAssignableFrom(throwableParam)) {// An exception handler to register...this.exceptionHandlerMap.put(throwableParam, method);if (logger.isDebugEnabled()) {logger.debug("Found exception handler method on throws advice: " + method);}}}}// 如果一个方法名为afterThrowing// 如果定义的afterThrowing方法的参数个数不是1个或者4个// 如果定义的afterThrowing方法的最后一个参数不是Throwable的子类// 说明这个 ThrowsAdvice 的方法定义的不合格if (this.exceptionHandlerMap.isEmpty()) {throw new IllegalArgumentException("At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");}}/*** Return the number of handler methods in this advice.*/public int getHandlerMethodCount() {return this.exceptionHandlerMap.size();}@Override@Nullablepublic Object invoke(MethodInvocation mi) throws Throwable {try {return mi.proceed();}catch (Throwable ex) {// 如果在调用调用链后面的MethodInterceptor或者目标对象的方法是抛出了异常// 就用这个异常去找该异常有没有对应的 afterThrowing 方法// 如果有的话,回调该afterThrowing方法Method handlerMethod = getExceptionHandler(ex);if (handlerMethod != null) {invokeHandlerMethod(mi, ex, handlerMethod);}throw ex;}}/*** Determine the exception handle method for the given exception.* @param exception the exception thrown* @return a handler for the given exception type, or {@code null} if none found*/@Nullableprivate Method getExceptionHandler(Throwable exception) {Class<?> exceptionClass = exception.getClass();if (logger.isTraceEnabled()) {logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]");}Method handler = this.exceptionHandlerMap.get(exceptionClass);while (handler == null && exceptionClass != Throwable.class) {exceptionClass = exceptionClass.getSuperclass();handler = this.exceptionHandlerMap.get(exceptionClass);}if (handler != null && logger.isTraceEnabled()) {logger.trace("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler);}return handler;}private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {Object[] handlerArgs;// 如果afterThrowing方法只有一个参数,只需要把异常作为参数就可以了if (method.getParameterCount() == 1) {handlerArgs = new Object[] {ex};}else {// 如果afterThrowing方法有四个参数,就得准备这四个参数handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};}try {// 调用ThrowsAdvice实例的afterThrowing方法method.invoke(this.throwsAdvice, handlerArgs);}catch (InvocationTargetException targetEx) {throw targetEx.getTargetException();}}}

 按照 ThrowsAdviceInterceptor 的实现我们知道,自己定义ThrowsAdvice时必须满足几个条件
1.必须至少有一个方法的方法名为afterThrowing
2.afterThrowing方法的参数个数只能是1个或者4个
3.afterThrowing方法的最后一个参数只能是Throwable的子类

九、详细介绍 MethodInterceptor 

十、如何通过 MethodInterceptor 几个实现类实现 Advice 的顺序调用

十一、AdvisorChainFactory 的默认实现类 DefaultAdvisorChainFactory

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/478518.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FreeRTOS之链表源码分析

文章目录 前言一、结构体1、链表List_t2、链表项xLIST_ITEM3、头节点xMINI_LIST_ITEM4、链表示意图 二、函数分析1、初始化函数vListInitialise2、初始化链表项vListInitialiseItem3、链表尾部添加节点vListInsertEnd4、按序插入节点vListInsert5、删除节点uxListRemove 总结 前…

【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程

【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程前言模型转换--pytorch转rknnpytorch转onnxonnx转rkn…

Matlab 深度学习工具箱 案例学习与测试————求二阶微分方程

clc clear% 定义输入变量 x linspace(0,2,10000);% 定义网络的层参数 inputSize 1; layers [featureInputLayer(inputSize,Normalization"none")fullyConnectedLayer(10)sigmoidLayerfullyConnectedLayer(1)sigmoidLayer]; % 创建网络 net dlnetwork(layers);% 训…

51单片机-独立按键与数码管联动

独立键盘和矩阵键盘检测原理及实现 键盘的分类&#xff1a;编码键盘和非编码键盘 键盘上闭合键的识别由专用的硬件编码器实现&#xff0c;并产生键编码号或键值的称为编码键盘&#xff0c;如&#xff1a;计算机键盘。靠软件编程识别的称为非编码键盘&#xff1b;在单片机组成…

华为无线AC+AP组网实际应用小结

之前公司都是使用的H3C的交换机、防火墙以及无线AC和AP的&#xff0c;最近优化下无线网络&#xff0c;说新的设备用华为的&#xff0c;然后我是直到要部署的当天才知道用华为设备的&#xff0c;就很无语了&#xff0c;一点准备没有&#xff0c;以下为这次的实际操作记录吧&…

浅谈网络 | 传输层之TCP协议

目录 TCP 包头格式TCP 的三次握手TCP 的四次挥手TCP 的可靠性与"靠谱"的哲学TCP流量控制TCP拥塞控制 上一章我们提到&#xff0c;UDP 就像我们小时候一样简单天真&#xff0c;它相信“网之初&#xff0c;性本善&#xff0c;不丢包&#xff0c;不乱序”&#xff0c;因…

MongoDB相关问题

视频教程 【GeekHour】20分钟掌握MongoDB Complete MongoDB Tutorial by Net Ninja MongoDB开机后调用缓慢的原因及解决方法 问题分析&#xff1a; MongoDB开机后调用缓慢&#xff0c;通常是由于以下原因导致&#xff1a; 索引重建&#xff1a; MongoDB在启动时会重建索引…

嵌入式驱动开发详解3(pinctrl和gpio子系统)

文章目录 前言pinctrl子系统pin引脚配置pinctrl驱动详解 gpio子系统gpio属性配置gpio子系统驱动gpio子系统API函数与gpio子系统相关的of函数 pinctrl和gpio子系统的使用设备树配置驱动层部分用户层部分 前言 如果不用pinctrl和gpio子系统的话&#xff0c;我们开发驱动时需要先…

[模版总结] - 树的基本算法4 -最近公共祖先 LCA

什么是最近公共祖先LCA LCA&#xff1a;在一个树中&#xff0c;距离两个节点p,q最近可以是其本身并且同时包含这两个子节点的节点 题目连接 Leetcode 236 - LCA Leetcode 1644 - LCA II Leetcode 1650 - LCAIII Leetcode 1123 - LCA of Deepest leaves 基本思路 Leetcode 23…

永磁同步电机末端振动抑制(输入整形)

文章目录 1、前言2、双惯量系统3、输入整形3.1 ZV整形器3.2 ZVD整形器3.3 EI整形器 4、伺服系统位置环控制模型5、仿真5.1 快速性分析5.2 鲁棒性分析 参考 1、前言 什么是振动抑制&#xff1f;对于一个需要精确定位的系统&#xff0c;比如机械臂、塔吊、码头集装箱等&#xff…

jQuery-Word-Export 使用记录及完整修正文件下载 jquery.wordexport.js

参考资料&#xff1a; jQuery-Word-Export导出word_jquery.wordexport.js下载-CSDN博客 近期又需要自己做个 Html2Doc 的解决方案&#xff0c;因为客户又不想要 Html2pdf 的下载了&#xff0c;当初还给我费尽心思解决Html转pdf时中文输出的问题&#xff08;html转pdf文件下载之…

【Redis_Day6】Hash类型

【Redis_Day6】Hash类型 Hash类型操作hash的命令hset&#xff1a;设置hash中指定的字段&#xff08;field&#xff09;的值&#xff08;value&#xff09;hsetnx&#xff1a;想hash中添加字段并设置值hget&#xff1a;获取hash中指定字段的值hexists&#xff1a;判断hash中是否…

【CSP CCF记录】201809-2第14次认证 买菜

题目 样例输入 4 1 3 5 6 9 13 14 15 2 4 5 7 10 11 13 14 样例输出 3 思路 易错点&#xff1a;仅考虑所给样例&#xff0c;会误以为H和W两人的装车时间是一一对应的&#xff0c;那么提交结果的运行错误就会让你瞬间清醒。 本题关键是认识到H和W的装车时间不一定一一对应&…

Unity清除所有的PlayerPrefs

方法1&#xff1a; 直接在你的代码中加入这句 PlayerPrefs.DeleteAll(); 方法2&#xff1a; 点击编辑窗口的这里

非交换几何与黎曼ζ函数:数学中的一场革命性对话

非交换几何与黎曼ζ函数&#xff1a;数学中的一场革命性对话 非交换几何&#xff08;Noncommutative Geometry, NCG&#xff09;是数学的一个分支领域&#xff0c;它将经典的几何概念扩展到非交换代数的框架中。非交换代数是一种结合代数&#xff0c;其中乘积不是交换性的&…

微信小程序下拉刷新与上拉触底的全面教程

微信小程序下拉刷新与上拉触底的全面教程 引言 在微信小程序的开发中,用户体验至关重要。下拉刷新和上拉触底是提高用户交互体验的重要功能,能够让用户轻松获取最新数据和内容。本文将详细介绍这两个功能的实现方式,结合实际案例、代码示例和图片展示,帮助开发者轻松掌握…

数据库的联合查询

数据库的联合查询 简介为什么要使⽤联合查询多表联合查询时MYSQL内部是如何进⾏计算的构造练习案例数据案例&#xff1a;⼀个完整的联合查询的过程 内连接语法⽰例 外连接语法 ⽰例⾃连接应⽤场景示例表连接练习 ⼦查询语法单⾏⼦查询多⾏⼦查询多列⼦查询在from⼦句中使⽤⼦查…

论文阅读:A fast, scalable and versatile tool for analysis of single-cell omics data

Zhang, K., Zemke, N.R., Armand, E.J. et al. A fast, scalable and versatile tool for analysis of single-cell omics data. Nat Methods 21, 217–227 (2024). 论文地址&#xff1a;https://doi.org/10.1038/s41592-023-02139-9 代码地址&#xff1a;https://github.com…

Hive离线数仓结构分析

Hive离线数仓结构 首先&#xff0c;在数据源部分&#xff0c;包括源业务库、用户日志、爬虫数据和系统日志&#xff0c;这些都是数据的源头。这些数据通过Sqoop、DataX或 Flume 工具进行提取和导入操作。这些工具负责将不同来源的数据传输到基于 Hive 的离线数据仓库中。 在离线…

设计模式之 模板方法模式

模板方法模式是行为型设计模式的一种。它定义了一个算法的骨架&#xff0c;并将某些步骤的实现延迟到子类中。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。 模板方法模式的核心在于&#xff1a; 封装算法的骨架&#xff1a;通过父类中的模板方…