星辰计划02-独特视角的spring动态代理

承接上一文 动态代理 ,这里探究spring 动态代理

会话1:spring动态代理 quick start

  • 👧哥哥,哥哥,spring 怎么去搞动态代理的呢
  • 👨 来来来,听我细细来说

quick start
通过Spring的 ProxyFactory去构建我们的代理对象,我对代理对象添加一个日志的动态扩展,打印日志LogAdvice

package com.example.demo;import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.ProxyConfig;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.cglib.core.DebuggingClassWriter;/*** @author bigbird-0101* @date 2024-07-03 22:20*/
public class SpringProxyTest {public static void main(String[] args) {System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\development\\idea workspace\\Study-Project\\com\\sun\\proxy");MaiPiao proxy = (MaiPiao) getProxy(new TargetSource() {@Overridepublic Class<?> getTargetClass() {return ZhouJieLun.class;}@Overridepublic Object getTarget() {return new ZhouJieLun();}}, SpringProxyTest.class.getClassLoader());proxy.maiPiao();}public static class ZhouJieLun implements MaiPiao{@Overridepublic void maiPiao() {System.out.println("卖周杰伦的票咯");}}public interface MaiPiao{void maiPiao();}private static Object getProxy(TargetSource targetSource, ClassLoader classLoader) {ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(new ProxyConfig());Advisor advisor = new Advisor() {@Overridepublic Advice getAdvice() {return new LogAdvice();}@Overridepublic boolean isPerInstance() {return false;}};Advisor[] advisors = new Advisor[]{advisor};proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);proxyFactory.setFrozen(false);return proxyFactory.getProxy(classLoader);}public static class LogAdvice implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("我开始执行");Object proceed = invocation.proceed();System.out.println("我执行完了");return proceed;}}
}

来来来 run一下,可以看到已经 成功打印了。
image.png

会话2:深入分析ProxyFactory

  • 👧哇,哥哥好厉害,这个ProxyFactory是个什么啊?你可以帮我分析一下吗
  • 👨 来来来,听我细细来说
首先我们看下ProxyFactory的类图


可以看到ProxyFactory实现了Advised接口,Advised是spring 代理非常核心的一个接口。
官方注释如下

Interface to be implemented by classes that hold the configuration of a factory of AOP proxies. This configuration includes the Interceptors and other advice, Advisors, and the proxied interfaces.
Any AOP proxy obtained from Spring can be cast to this interface to allow manipulation of its AOP advice.

他的意思是说 接口的实现类是一个aop 代理配置工厂,这个配置包含接口,advice,advisor和代理接口,任何的代理对象都能强转成这个接口,啥意思呢?意思是spring所有的代理类都实现了这个接口(这个我们稍后分析spring 代理工厂生成的代码是啥样的)。
总结概括:
1.spring所有的代理类都实现了这个接口
2.可以通过接口动态配置代理对象,包括动态添加切面和触发的事件,以及接口等。
image.png

继续分析 ProxyCreatorSupport

可以看到真正的构建代理工厂是 AopProxyFactory这个类,我们看他的代码
image.png
可以看到 通过代理配置 来决定使用 jdk生成代理 还是 cglib生成代理
可以看到** 满足Cglib的条件还是比较苛刻的**。spring还是希望尽量使用 jdk动态代理。除非你强制设置优化例如isOptimize 或者 ProxyTargetClass属性,可以看到就算你设置了 proxyTargetClass=true,但是你只要是接口都会使用jdk动态代理。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {/*** Singleton instance of this class.* @since 6.0.10*/public static final DefaultAopProxyFactory INSTANCE = new DefaultAopProxyFactory();private static final long serialVersionUID = 7930414337282325166L;@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {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.");}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])));}}

我们继续分析 ObjenesisCglibAopProxy

看到下面这个方法
org.springframework.aop.framework.CglibAopProxy#buildProxy

private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());}try {//获取代理配置当中的需要被代理的对象的classClass<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");Class<?> proxySuperClass = rootClass;//如果被代理的对象已经是cglib代理 //则我们生成的代理 需要继承和实现 已是cglib类的父类和其实现的接口if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {proxySuperClass = rootClass.getSuperclass();Class<?>[] additionalInterfaces = rootClass.getInterfaces();for (Class<?> additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface);}}// Validate the class, writing log messages as necessary.validateClassIfNecessary(proxySuperClass, classLoader);//拼接cglib代理// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader smartClassLoader &&smartClassLoader.isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}//设置继承的父类enhancer.setSuperclass(proxySuperClass);//设置代理对象所有的接口enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setAttemptLoad(true);//我们代码内部的生成策略 例如方法 字段 和参数等 我们详细分析enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));//获取回调函数 这个非常重要  这个是实现aop的关键 由他来驱动 Advice 的调用 第一个回调函数是DynamicAdvisedInterceptor Callback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveProxyCallbackFilter filter = new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset);enhancer.setCallbackFilter(filter);enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.// ProxyCallbackFilter has method introspection capability with Advisor access.try {return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));}finally {// Reduce ProxyCallbackFilter to key-only state for its class cache role// in the CGLIB$CALLBACK_FILTER field, not leaking any Advisor state...filter.advised.reduceToAdvisorKey();}}catch (CodeGenerationException | IllegalArgumentException ex) {throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +": Common causes of this problem include using a final class or a non-visible class",ex);}catch (Throwable ex) {// TargetSource.getTarget() failedthrow new AopConfigException("Unexpected AOP exception", ex);}}

第一个回调函数 DynamicAdvisedInterceptor 这个非常重要 这个是实现aop的关键 由他来驱动 Advice 的调用
来我们仔细分析一下 DynamicAdvisedInterceptor
image.png

继续分析 DynamicAdvisedInterceptor#intercept

第17行 通过代理配置类 advised 拿到所有的Advice 然后通过 CglibMethodInvocation 遍历调用 new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()

@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = null;TargetSource targetSource = this.advised.getTargetSource();try {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);List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;// Check whether we only have one InvokerInterceptor: that is,// no real advice, but just reflective invocation of the target.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...retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}return processReturnType(proxy, target, method, args, retVal);}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}
}

我们来看下 CglibMethodInvocation的类图。再看代码 发现他的核心逻辑在 其父类当中。
image.png
第27行 可以看到 开始调用了 Advice,那他是怎么实现循环的呢?当27行 执行时,会去 调用 quick start 当中的 LogAdvice的 invoke 方法 ,LogAdvice又会调用 MethodInvocation.proceed 所以又会回到这里 继续执行,然后发现 Advice已经遍历完了,然后就会去执行 第 6行 ,所以会去调用真实的 代理类的方法 。形成了一个循环调用。

@Override@Nullablepublic Object proceed() throws Throwable {// We start with an index of -1 and increment early.if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher dm) {// Evaluate dynamic method matcher here: static part will already have// been evaluated and found to match.Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());if (dm.matcher().matches(this.method, targetClass, this.arguments)) {return dm.interceptor().invoke(this);}else {// Dynamic matching failed.// Skip this interceptor and invoke the next in the chain.return proceed();}}else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}

时序图如下 ** 第3步 和 第4步 如果有多个Advice会循环多次。**
请添加图片描述

继续分析 ClassLoaderAwareGeneratorStrategy

这个是其生成字节码的具体逻辑 可以看到这个generateClass 方法

@Override
public byte[] generate(ClassGenerator cg) throws Exception {DebuggingClassWriter cw = getClassVisitor();transform(cg).generateClass(cw);return transform(cw.toByteArray());
}

他的实现是 org.springframework.cglib.proxy.Enhancer#generateClass 这个实现的 我们可以看到其代码
完整的展示了 这个class 是怎么生成的

public void generateClass(ClassVisitor v) throws Exception {Class sc = (superclass == null) ? Object.class : superclass;if (TypeUtils.isFinal(sc.getModifiers())) {throw new IllegalArgumentException("Cannot subclass final class " + sc.getName());}List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));filterConstructors(sc, constructors);// Order is very important: must add superclass, then// its superclass chain, then each interface and// its superinterfaces.List actualMethods = new ArrayList();List interfaceMethods = new ArrayList();final Set forcePublic = new HashSet();getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);List methods = CollectionUtils.transform(actualMethods, value -> {Method method = (Method) value;int modifiers = Constants.ACC_FINAL| (method.getModifiers()& ~Constants.ACC_ABSTRACT& ~Constants.ACC_NATIVE& ~Constants.ACC_SYNCHRONIZED);if (forcePublic.contains(MethodWrapper.create(method))) {modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;}return ReflectUtils.getMethodInfo(method, modifiers);});ClassEmitter e = new ClassEmitter(v);if (currentData == null) {e.begin_class(Constants.V1_8,Constants.ACC_PUBLIC,getClassName(),Type.getType(sc),(useFactory ?TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :TypeUtils.getTypes(interfaces)),Constants.SOURCE_FILE);}else {e.begin_class(Constants.V1_8,Constants.ACC_PUBLIC,getClassName(),null,new Type[]{FACTORY},Constants.SOURCE_FILE);}List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null);e.declare_field(Constants.ACC_PUBLIC | Constants.ACC_STATIC, FACTORY_DATA_FIELD, OBJECT_TYPE, null);if (!interceptDuringConstruction) {e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null);}e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);if (serialVersionUID != null) {e.declare_field(Constants.PRIVATE_FINAL_STATIC, Constants.SUID_FIELD_NAME, Type.LONG_TYPE, serialVersionUID);}for (int i = 0; i < callbackTypes.length; i++) {e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null);}// This is declared private to avoid "public field" pollutione.declare_field(Constants.ACC_PRIVATE | Constants.ACC_STATIC, CALLBACK_FILTER_FIELD, OBJECT_TYPE, null);if (currentData == null) {emitMethods(e, methods, actualMethods);emitConstructors(e, constructorInfo);}else {emitDefaultConstructor(e);}emitSetThreadCallbacks(e);emitSetStaticCallbacks(e);emitBindCallbacks(e);if (useFactory || currentData != null) {int[] keys = getCallbackKeys();emitNewInstanceCallbacks(e);emitNewInstanceCallback(e);emitNewInstanceMultiarg(e, constructorInfo);emitGetCallback(e, keys);emitSetCallback(e, keys);emitGetCallbacks(e);emitSetCallbacks(e);}e.end_class();
}

会话3:深入分析生成代理类

  • 👧哇,哥哥真牛皮啊,哥哥,哥哥 我想看下 spring 到底生成了什么样的代理对象,他的代码到底是啥?
  • 👨 好的 好的,看我一一给你介绍

以我们上面的quick start的为例,我们查看他生成的 代理类代码是啥样的。
我们设置下面这样的系统参数就可以看到spring生成的类了。

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\development\\idea workspace\\Study-Project\\com\\sun\\proxy");

这个就是spring 生成的代理类 哇,非常复杂非常多啊。他固定实现了 SpringProxy, Advised, Factory 这几个接口,所以每一个代理类都可以转成 Advised

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.example.demo;import java.lang.reflect.Method;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetClassAware;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Dispatcher;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;public class SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0 extends SpringProxyTest.ZhouJieLun implements SpringProxy, Advised, 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 MethodInterceptor CGLIB$CALLBACK_1;private NoOp CGLIB$CALLBACK_2;private NoOp CGLIB$CALLBACK_3;private Dispatcher CGLIB$CALLBACK_4;private MethodInterceptor CGLIB$CALLBACK_5;private MethodInterceptor CGLIB$CALLBACK_6;private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$maiPiao$0$Method;private static final MethodProxy CGLIB$maiPiao$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.example.demo.SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0");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$maiPiao$0$Method = ReflectUtils.findMethods(new String[]{"maiPiao", "()V"}, (var1 = Class.forName("com.example.demo.SpringProxyTest$ZhouJieLun")).getDeclaredMethods())[0];CGLIB$maiPiao$0$Proxy = MethodProxy.create(var1, var0, "()V", "maiPiao", "CGLIB$maiPiao$0");}final void CGLIB$maiPiao$0() {super.maiPiao();}public final void maiPiao() {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$maiPiao$0$Method, CGLIB$emptyArgs, CGLIB$maiPiao$0$Proxy);} else {super.maiPiao();}}final boolean CGLIB$equals$1(Object var1) {return super.equals(var1);}public final boolean equals(Object var1) {MethodInterceptor var10000 = this.CGLIB$CALLBACK_5;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_5;}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 (var10000 == 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_6;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_6;}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 (var10000 == 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 -508378822:if (var10000.equals("clone()Ljava/lang/Object;")) {return CGLIB$clone$4$Proxy;}break;case 221771545:if (var10000.equals("maiPiao()V")) {return CGLIB$maiPiao$0$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 final void setTargetSource(TargetSource var1) {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}((Advised)var10000.loadObject()).setTargetSource(var1);}public final Class[] getProxiedInterfaces() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).getProxiedInterfaces();}public final String toProxyConfigString() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).toProxyConfigString();}public final void addAdvice(Advice var1) throws AopConfigException {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}((Advised)var10000.loadObject()).addAdvice(var1);}public final void addAdvice(int var1, Advice var2) throws AopConfigException {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}((Advised)var10000.loadObject()).addAdvice(var1, var2);}public final int getAdvisorCount() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).getAdvisorCount();}public final void setExposeProxy(boolean var1) {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}((Advised)var10000.loadObject()).setExposeProxy(var1);}public final boolean replaceAdvisor(Advisor var1, Advisor var2) throws AopConfigException {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).replaceAdvisor(var1, var2);}public final boolean removeAdvisor(Advisor var1) {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).removeAdvisor(var1);}public final void removeAdvisor(int var1) throws AopConfigException {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}((Advised)var10000.loadObject()).removeAdvisor(var1);}public final boolean isPreFiltered() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).isPreFiltered();}public final boolean isInterfaceProxied(Class var1) {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).isInterfaceProxied(var1);}public final boolean isExposeProxy() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).isExposeProxy();}public final Advisor[] getAdvisors() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).getAdvisors();}public final boolean removeAdvice(Advice var1) {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).removeAdvice(var1);}public final TargetSource getTargetSource() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).getTargetSource();}public final boolean isProxyTargetClass() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).isProxyTargetClass();}public final void setPreFiltered(boolean var1) {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}((Advised)var10000.loadObject()).setPreFiltered(var1);}public final void addAdvisor(Advisor var1) throws AopConfigException {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}((Advised)var10000.loadObject()).addAdvisor(var1);}public final void addAdvisor(int var1, Advisor var2) throws AopConfigException {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}((Advised)var10000.loadObject()).addAdvisor(var1, var2);}public final int indexOf(Advice var1) {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).indexOf(var1);}public final int indexOf(Advisor var1) {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).indexOf(var1);}public final boolean isFrozen() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((Advised)var10000.loadObject()).isFrozen();}public final Class getTargetClass() {Dispatcher var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}return ((TargetClassAware)var10000.loadObject()).getTargetClass();}public SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0() {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) {SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0 var1 = (SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0)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;}}Callback[] var10001 = (Callback[])var10000;var1.CGLIB$CALLBACK_6 = (MethodInterceptor)((Callback[])var10000)[6];var1.CGLIB$CALLBACK_5 = (MethodInterceptor)var10001[5];var1.CGLIB$CALLBACK_4 = (Dispatcher)var10001[4];var1.CGLIB$CALLBACK_3 = (NoOp)var10001[3];var1.CGLIB$CALLBACK_2 = (NoOp)var10001[2];var1.CGLIB$CALLBACK_1 = (MethodInterceptor)var10001[1];var1.CGLIB$CALLBACK_0 = (MethodInterceptor)var10001[0];}}public Object newInstance(Callback[] var1) {CGLIB$SET_THREAD_CALLBACKS(var1);SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0 var10000 = new SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0();CGLIB$SET_THREAD_CALLBACKS((Callback[])null);return var10000;}public Object newInstance(Callback var1) {throw new IllegalStateException("More than one callback object required");}public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {CGLIB$SET_THREAD_CALLBACKS(var3);SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0 var10000 = new SpringProxyTest$ZhouJieLun$$SpringCGLIB$$0;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);Object var10000;switch (var1) {case 0:var10000 = this.CGLIB$CALLBACK_0;break;case 1:var10000 = this.CGLIB$CALLBACK_1;break;case 2:var10000 = this.CGLIB$CALLBACK_2;break;case 3:var10000 = this.CGLIB$CALLBACK_3;break;case 4:var10000 = this.CGLIB$CALLBACK_4;break;case 5:var10000 = this.CGLIB$CALLBACK_5;break;case 6:var10000 = this.CGLIB$CALLBACK_6;break;default:var10000 = null;}return (Callback)var10000;}public void setCallback(int var1, Callback var2) {switch (var1) {case 0:this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;break;case 1:this.CGLIB$CALLBACK_1 = (MethodInterceptor)var2;break;case 2:this.CGLIB$CALLBACK_2 = (NoOp)var2;break;case 3:this.CGLIB$CALLBACK_3 = (NoOp)var2;break;case 4:this.CGLIB$CALLBACK_4 = (Dispatcher)var2;break;case 5:this.CGLIB$CALLBACK_5 = (MethodInterceptor)var2;break;case 6:this.CGLIB$CALLBACK_6 = (MethodInterceptor)var2;}}public Callback[] getCallbacks() {CGLIB$BIND_CALLBACKS(this);return new Callback[]{this.CGLIB$CALLBACK_0, this.CGLIB$CALLBACK_1, this.CGLIB$CALLBACK_2, this.CGLIB$CALLBACK_3, this.CGLIB$CALLBACK_4, this.CGLIB$CALLBACK_5, this.CGLIB$CALLBACK_6};}public void setCallbacks(Callback[] var1) {this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];this.CGLIB$CALLBACK_1 = (MethodInterceptor)var1[1];this.CGLIB$CALLBACK_2 = (NoOp)var1[2];this.CGLIB$CALLBACK_3 = (NoOp)var1[3];this.CGLIB$CALLBACK_4 = (Dispatcher)var1[4];this.CGLIB$CALLBACK_5 = (MethodInterceptor)var1[5];this.CGLIB$CALLBACK_6 = (MethodInterceptor)var1[6];}static {CGLIB$STATICHOOK1();}
}

可以看到 这个 MethodInterceptor var10000 就是我们上面的 DynamicAdvisedInterceptor

 public final void maiPiao() {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$maiPiao$0$Method, CGLIB$emptyArgs, CGLIB$maiPiao$0$Proxy);} else {super.maiPiao();}}

会话4: spring是如何使用这个 ProxyFactory的呢?

  • 👧哇,哥哥讲的非常透彻啊,我还想知道spring怎么使用这个的呢?
  • 👨 哈哈哈哈哈哈,好哇听我细细将来。

spring主要是通过 InfrastructureAdvisorAutoProxyCreator来构建基础设施类的代理,当然还有其他类例如 AspectJAwareAdvisorAutoProxyCreator(主要是Aspectj),这里我们不细说Aspectj,我们继续说InfrastructureAdvisorAutoProxyCreator,他是通过BeanFactoryPostProcess来注入这个基础设施bean ,我们可以通过AopAutoConfiguration来看到这样一段代码。通过下面这行代码进行将这个bean 声明的。image.png
继续我们来看 InfrastructureAdvisorAutoProxyCreator 这个类,这个类主要是来自动创建类的代理,我们可以看下他的类图,看看它到底是啥?
image.png
果然源码之下没有任何秘密,他是BeanPostProcessor的实现类,当创建bean时 会经过这个类的处理,我们来细看一下他的具体实现org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation, 可以看到第24行 开始构建代理对象。

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {Object cacheKey = getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}// Create proxy here if we have a custom TargetSource.// Suppresses unnecessary default instantiation of the target bean:// The TargetSource will handle target instances in a custom fashion.TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}//获取这个bean上面符合的切面。Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}return null;}

他是怎么获取 符合的切面对象呢?
通过一些过滤手段来过滤 切面,例如在切面当中的Pointcut切点,来判断这个代理对象是否需要这个切面。
我们细看这两个方法,在这个AbstractAdvisorAutoProxyCreator类中

@Override@Nullableprotected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {return DO_NOT_PROXY;}return advisors.toArray();}/*** Find all eligible Advisors for auto-proxying this class.* @param beanClass the clazz to find advisors for* @param beanName the name of the currently proxied bean* @return the empty List, not {@code null},* if there are no pointcuts or interceptors* @see #findCandidateAdvisors* @see #sortAdvisors* @see #extendAdvisors*/protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {//找到候选的切面对象List<Advisor> candidateAdvisors = findCandidateAdvisors();//通过一些过滤手段来过滤 切面,例如在切面当中的切点,来判断这个代理对象是否需要这个切面。List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {try {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}catch (BeanCreationException ex) {throw new AopConfigException("Advisor sorting failed with unexpected bean creation, probably due " +"to custom use of the Ordered interface. Consider using the @Order annotation instead.", ex);}}return eligibleAdvisors;}

这个看到这个代码的调用时序图如下,在AopUtils当中真正调用了Pointcut,来来来我们继续追,为我们再细看AopUtils的 findAdvisorsThatCanApply方法
image.png
canApply就是拿到了Pointcut 的 ClassFilter 来过滤,当前这个bean的class是否符合 这个切面,到这里我们已经成功的分析了几乎所有链路,打完收工

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {if (candidateAdvisors.isEmpty()) {return candidateAdvisors;}List<Advisor> eligibleAdvisors = new ArrayList<>();for (Advisor candidate : candidateAdvisors) {if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {eligibleAdvisors.add(candidate);}}boolean hasIntroductions = !eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {if (candidate instanceof IntroductionAdvisor) {// already processedcontinue;}if (canApply(candidate, clazz, hasIntroductions)) {eligibleAdvisors.add(candidate);}}return eligibleAdvisors;}
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {if (advisor instanceof IntroductionAdvisor) {return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}}

注意点:

InfrastructureAdvisorAutoProxyCreator 当中 有一个这样的判断 判断切面是否需要被过滤。这个切面的角色需要时基础设施层的切面。

protected boolean isEligibleAdvisorBean(String beanName) {
return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
}

会话5: spring aop用例

  • 👧哇,哥哥哥哥你咋这么厉害。我还想看 你举个完整的例子,自定义日志切面,来实现日志打印的需求
  • 👨 哈哈哈哈哈哈,好好好。

看我表演嘿嘿

package com.example.demo;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author bigbird-0101* @date 2024-06-27 22:55*/
public class Test {public static void main(String[] args) {AnnotationConfigApplicationContext annotationConfigApplicationContext=new AnnotationConfigApplicationContext(Config.class,LogAdvisor.class,A.class,B.class);MaiPiao bean = annotationConfigApplicationContext.getBean(MaiPiao.class);bean.maiPiao();annotationConfigApplicationContext.close();}@Configurationpublic static class Config {@Beanpublic BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {return (beanFactory) -> {if (beanFactory instanceof BeanDefinitionRegistry registry) {AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}};}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public Test.LogAdvisor logAdvisor(){return new Test.LogAdvisor();}}@Componentpublic static class LogAdvisor extends AbstractPointcutAdvisor {public LogAdvisor() {System.out.println("LogAdvisor");}@Overridepublic Pointcut getPointcut() {return new AnnotationMatchingPointcut(null,Log.class,false);}@Overridepublic Advice getAdvice() {return new LogMethodInterceptor();}private static class LogMethodInterceptor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("before");Object result = invocation.proceed();System.out.println("after");return result;}}}public interface MaiPiao {void maiPiao();}/*** @author bigbird-0101* @date 2024-06-27 23:07*/@Documented@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface Log {String value() default "";}@Servicepublic static class B {@Log("123")public void maiPiao(){System.out.println("B mai piao");}}@Servicepublic static class A implements MaiPiao{@Autowiredprivate B b;@Overridepublic void maiPiao() {b.maiPiao();}}
}

请注意我们的切面一定要声明这个 @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 不然会跳过 前面已经讲过为啥会跳过
运行一下 可以看到已经成功运行,但是我们发现 这个日志切面对象LogAdvisor被构建了两次 这个咋回事呢?为啥?不是单例吗?我们debug一下,找下根本的问题。
image.png
这个是第一次被调用的时候的截图。
image.png
这个是第二次被调用时候的截图,卧槽我发现了 原来我的 LogAdvisor 被 Component注解修饰了,然后我又去声明导致创建了两次,去除Component这个注解重新运行一下。
image.png
这个是结果 卧槽 咋还是两次 哪里还有声明,我仔细检查一看 卧槽前面 还声明了一次
image.png

AnnotationConfigApplicationContext annotationConfigApplicationContext=new AnnotationConfigApplicationContext(Config.class,LogAdvisor.class,A.class,B.class);

去除其中的LogAdvisor,再试一下。
可以可以终于正常了,不容易啊。之前debug 没注意 写上去了。打完收工
image.png

会话6:结束

  • 👧哇,哥哥好厉害好厉害。
  • 👨 哈哈哈哈哈哈,马马虎虎了。

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

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

相关文章

云视频监控中的高效视频转码策略:视频汇聚EasyCVR平台H.265自动转码H.264能力解析

随着科技的快速发展&#xff0c;视频监控技术已经广泛应用于各个领域&#xff0c;如公共安全、商业管理、教育医疗等。与此同时&#xff0c;视频转码技术作为视频处理的关键环节&#xff0c;也在不断提高视频的质量和传输效率。 一、视频监控技术的演进 视频监控技术的发展历…

前后端如何实现非对称加解密-使用RSA为例讲解!

写在最前面&#xff0c;RSA是一种非对称加密算法&#xff0c;使用不同的公钥和私钥进行加密和解密。 下面是使用RSA进行加密和解密的代码示例&#xff1a; 前端&#xff1a;使用CryptoJS进行RSA加密 在前端JavaScript中&#xff0c;使用jsencrypt库来进行RSA加密&#xff1a…

【React】Ant Design -- Table分页功能实现

实现步骤 为Table组件指定pagination属性来展示分页效果在分页切换事件中获取到筛选表单中选中的数据使用当前页数据修改params参数依赖引起接口重新调用获取最新数据 const pageChange (page) > {// 拿到当前页参数 修改params 引起接口更新setParams({...params,page})…

前端web在线PPT编辑器-PPTLIST

哈喽&#xff0c;大家好&#xff0c;今天给大家介绍一款的在线的PPT编辑器开源框架-PPTLIST&#xff1b;他是一个基于 Vue3.x TypeScript 的在线演示文稿&#xff08;幻灯片&#xff09;应用&#xff0c;还原了大部分 Office PowerPoint 常用功能&#xff0c;支持 文字、图片、…

JMH325【剑侠情缘3】第2版80级橙武网游单机更稳定亲测视频安装教学更新整合收集各类修改教学补丁兴趣可以慢慢探索

资源介绍&#xff1a; 是否需要虚拟机&#xff1a;是 文件大小&#xff1a;压缩包约14G 支持系统&#xff1a;win10、win11 硬件需求&#xff1a;运行内存8G 4核及以上CPU独立显卡 下载方式&#xff1a;百度网盘 任务修复&#xff1a; 1&#xff0c;掌门任务&#xff08…

小程序内容管理系统设计

设计一个小程序内容管理系统&#xff08;CMS&#xff09;时&#xff0c;需要考虑以下几个关键方面来确保其功能完善、用户友好且高效&#xff1a; 1. 需求分析 目标用户&#xff1a;明确你的目标用户群体&#xff0c;比如企业、媒体、个人博主等&#xff0c;这将决定系统的功…

HDFS 块重构和RedundancyMonitor详解

文章目录 1. 前言2 故障块的重构(Reconstruct)2.1 故障块的状态定义和各个状态的统计信息2.2 故障文件块的查找收集2.5.2.1 misReplica的检测2.5.2.2 延迟队列(postponedMisreplicatedBlocks)的构造和实现postponedMisreplicatedBlocks中Block的添加postponedMisreplicatedBloc…

科普文:HTTPS协议

概叙 HTTPS&#xff08;Secure Hypertext Transfer Protocol&#xff09;即安全超文本传输协议&#xff0c;是一个安全通信通道。用于计算机网络的安全通信&#xff0c;已经在互联网得到广泛应用。 HTTPS 是基于 HTTP 的扩展&#xff0c;其相当于 HTTP协议SSL&#xff08;安全套…

Python编程学习笔记(2)--- 列表简介

1、列表是什么 列表由一系列按特定顺序排列的元素组成。可以创建包含字母表中所有字母、数字、0~9或所有家庭成员姓名的列表&#xff1b;也可以将任何东西加入列表中&#xff0c;其中的元素之间可以没有任何关系。列表通常包含多个元素&#xff0c;因此给列表指定一个表示复数…

怎么做好菲律宾TikTok直播带货?

TikTok目前是全球最受欢迎的APP之一&#xff0c;菲律宾TikTok直播已成为品牌出海的新趋势。作为一种新兴的引流渠道&#xff0c;出海电商卖家正通过直播带货模式实现流量变现。 在进行菲律宾TikTok直播时&#xff0c;关键在于能否吸引和留住消费者并促成购买。因此&#xff0c;…

怎么将3张照片合并成一张?这几种拼接方法很实用!

怎么将3张照片合并成一张&#xff1f;在我们丰富多彩的日常生活里&#xff0c;是否总爱捕捉那些稍纵即逝的美好瞬间&#xff0c;将它们定格为一张张珍贵的图片&#xff1f;然而&#xff0c;随着时间的推移&#xff0c;这些满载回忆的宝藏却可能逐渐演变成一项管理挑战&#xff…

PP网/尼龙网检测方案居然如此高效?

硅胶套是一种由硅胶材料制成的套管&#xff0c;通常用于保护、密封或绝缘电子元件、电线、电缆等。硅胶具有优异的耐高温、耐低温、耐化学腐蚀和绝缘性能&#xff0c;因此硅胶套常被用于需要抗高温、耐磨、耐腐蚀的环境中。硅胶套的柔软性和良好的弹性使其适合于包裹各种形状的…

(总结)编译ORB_SLAM2遇到的错误

目录 第一个错误error: ‘CV_BGR2GRAY’ was not declared in this scope 第二个错误error: ‘CV_GRAY2BGR’ was not declared in this scope 第三个错误是没有那个文件或目录 26 | #include 第四个错误是‘CV_LOAD_IMAGE_UNCHANGED’ was not declared in this scope 第…

FPGA_GTX:简要版

1. GTX介绍 Xilinx FPGA的GT意思是Gigabyte Transceiver。通常称呼为Serdes、高速收发器。GT在xilinx不同系列有着不同的产品&#xff0c;从7系列到UltraScale系列分别有GTP、GTX、GTZ、GTH、GTY和GTM。不同GT整体结构上类似&#xff0c;为了支持越来越高的line rate&#xff…

09.C2W4.Word Embeddings with Neural Networks

往期文章请点这里 目录 OverviewBasic Word RepresentationsIntegersOne-hot vectors Word EmbeddingsMeaning as vectorsWord embedding vectors Word embedding processWord Embedding MethodsBasic word embedding methodsAdvanced word embedding methods Continuous Bag-…

针对tcp不出网打——HTTP封装隧道代理(以CFS演示)

目录 上传工具到攻击机 使用说明 生成后门文件 由于电脑短路无法拖动文件&#xff0c;我就wget发送到目标主机tunnel.php文件​ 成功上传​ 可以访问上传的文件 启动代理监听 成功带出 后台私信获取弹药库工具reGeorg 上传工具到攻击机 使用说明 生成后门文件 pyt…

【ARMv8/v9 GIC 系列 5 -- GIC GICD_CTRL 使用详细介绍】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 GICD_CTRLGICD_CTLR 寄存器结构RWP(Register Write Pending)E1NWF(Enable 1 of N Wakeup Functionality)DS(Disable Security)亲和性路由(Affinity Routing)ARE_NSARE_SGIC 中断组使能EnableGrp1SEnableGrp1NS)EnableGrp0G…

【C++初阶】C++入门(下)

【C初阶】C入门&#xff08;下&#xff09; &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f955;所属专栏&#xff1a;C&#x1f96d; &#x1f33c;文章目录&#x1f33c; 6. 引用 6.1 引用的概念 6.2 引用特性 6.3 常引用 6.4 使用场景 6.5 传值、传引用效率…

maven 依赖冲突

依赖冲突 1、对于 Maven 而言&#xff0c;同一个 groupId 同一个 artifactId 下&#xff0c;只能使用一个 version。 <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-math3 --><dependency><groupId>org.apache.commons</groupId&…

【栈和队列】

目录 一、栈1.1、栈的基本概念1.2、栈的基本操作1.3、栈的顺序存储实现1.3.1、顺序栈的定义1.3.2、顺序栈的初始化1.3.3、顺序栈的入栈和出栈1.3.4、读取栈顶元素1.3.5、共享栈&#xff08;即两个栈共享同一片空间&#xff09; 1.4、栈的链式存储实现1.4.1、链栈的定义1.4.2、链…