星辰计划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.mzph.cn/bicheng/44644.shtml

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

相关文章

nvm安装node一直没有npm

报错问题&#xff1a;使用nvm安装Node.js后没有npm。 解释&#xff1a; NVM (Node Version Manager) 是一个用于管理Node.js版本的工具&#xff0c;它允许你在同一台机器上安装和使用不同版本的Node.js。通常情况下&#xff0c;安装Node.js的过程中会自动包含npm&#xff08;…

LoRaWAN网络协议Class A/Class B/Class C三种工作模式说明

LoRaWAN是一种专为广域物联网设计的低功耗广域网络协议。它特别适用于物联网&#xff08;IoT&#xff09;设备&#xff0c;可以在低数据速率下进行长距离通信。LoRaWAN 网络由多个组成部分构成&#xff0c;其中包括节点&#xff08;终端设备&#xff09;、网关和网络服务器。Lo…

Unity到底有无collider可视化,方便调试子弹,ACT,做Demo等

参照日本程序员的代码,改了一些,算是支持MeshCollider 好像确实就是日本《博客》比较多这类,可视化的调试资料 UnityでデバッグをするときColliderを可視化したいことってありますよね。 コライダーを見える化するには Physics Debuggerを使う可視化スクリプトを使うの2通り…

AWS 云安全性:检测 SSH 暴力攻击

由于开源、低成本、可靠性和灵活性等优势&#xff0c;云基础设施主要由基于linux的机器主导&#xff0c;然而&#xff0c;它们也不能幸免于黑客的攻击&#xff0c;从而影响云的安全性。攻击Linux机器最流行的方法之一是通过SSH通道。 什么是 SSH 安全外壳协议&#xff08;Sec…

使用来此加密申请多域名SSL证书

在数字化时代的浪潮中&#xff0c;网站的安全性已成为企业和个人不可或缺的一部分。特别是在数据传输和用户隐私保护方面&#xff0c;SSL证书的作用愈发显著。 申请多域名SSL证书步骤 1、登录来此加密网站&#xff0c;输入域名&#xff0c;可以勾选泛域名和包含根域。 2、选择…

Unity3D中UI层级改变详解

在Unity3D开发中&#xff0c;UI层级的调整是常见的需求&#xff0c;它直接关系到用户界面(UI)元素的显示顺序。在Unity的UI系统中&#xff0c;主要使用UGUI&#xff08;Unitys Graphical User Interface&#xff09;来实现界面布局和元素展示。本文将详细讲解Unity3D中如何改变…

华为机考真题 -- 连续自然数之和来表达整数

题目描述&#xff1a; 一个整数可以由连续的自然数之和来表示给定一个整数&#xff0c;计算该整数有几种连续自然数之和的表达式&#xff0c;且打印出每种表达式 示例&#xff1a; 输入 9 输出 99 945 9234 Result:3 C源码&#xff1a; #include <iostream> #includ…

代码随想录-DAY⑦-字符串——leetcode 344 | 541 | 151

344 思路 没啥好说的&#xff0c; 双指针头尾交换&#xff0c; 相遇结束。 时间复杂度&#xff1a;O(n) 空间复杂度&#xff1a;O(1) 代码 class Solution { public:void reverseString(vector<char>& s) {int left0, rights.size()-1;while(left<right){swa…

低代码平台赋能企业全面数字化转型

引言&#xff1a;在当今这个日新月异的数字化时代&#xff0c;企业正面临着前所未有的机遇与挑战。为了保持竞争力并实现可持续发展&#xff0c;企业亟需进行全面的数字化转型。而低代码平台作为数字化转型的重要工具&#xff0c;正以其独特的优势赋能企业&#xff0c;推动其向…

智慧城市的神经网络:Transformer模型在智能城市构建中的应用

智慧城市的神经网络&#xff1a;Transformer模型在智能城市构建中的应用 随着城市化的快速发展&#xff0c;智能城市的概念应运而生&#xff0c;旨在通过先进的信息技术提升城市管理效率和居民生活质量。Transformer模型&#xff0c;作为人工智能领域的一颗新星&#xff0c;其…

C语言自学探究---已知三边求指定的一个角的cos/sin值

一.已知三边求指定的一个角的cos/sin值 上一次我探究了三角形勾股定理实例http://t.csdnimg.cn/eY7zI 写完三角形勾股定理代码后&#xff0c;我自然而然想到了解三角形中&#xff1a; 已知三边求指定的一个角的cos/sin值 于是我开始自己思考如何实现这一功能 part1:思考过程…

Perl编译器架构:前端与后端的精细分工

&#x1f527; Perl编译器架构&#xff1a;前端与后端的精细分工 Perl作为一种高级、通用的编程语言&#xff0c;其编译器的架构设计对于性能和灵活性至关重要。Perl编译器由前端和后端组成&#xff0c;它们各自承担着不同的职责。本文将深入解析Perl编译器前端和后端的区别&a…

Apache Flink 运行时架构

Flink 运行时架构 Flink整个系统由两个主要部分组成JobManager和TaskManager&#xff0c;Flink架构也遵循Master-Slave架构设计原则&#xff0c;JobManager为Master节点&#xff0c;TaskManager为worker&#xff08;Slave&#xff09;节点&#xff0c;所有组件之间通讯都是借助…

IDEA插件更新:配置选项更丰富、支持环境、全局参数的创建和维护

Apipost-Helper-2.0 IDEA插件&#xff0c;因快速扫描代码、基于注解提取信息、支持直接调试及生成API文档等功能受到众多用户的认可&#xff0c;是目前市面上一款真正高效便捷生成接口文档的利器。本月初&#xff0c;Apipost IDEA插件又迎来重大优化&#xff0c;在应用市场搜索…

如何降低pdf内存,如何降低pdf內存大小,如何降低pdf内存占用

在现代办公环境中&#xff0c;pdf文件已经成为了一种不可或缺的文档格式。然而&#xff0c;pdf内存太大文件常常给我们的工作带来困扰&#xff0c;本文将为你揭秘几种简单有效的方法&#xff0c;帮助你轻松降低 pdf 内存&#xff0c;提高工作效率。 方法一、安装pdf转换软件 打…

SVN 解决冲突

SVN 解决冲突 1. 引言 在软件开发过程中,版本控制是一个至关重要的环节。SVN(Subversion)作为一个流行的版本控制系统,被广泛应用于团队协作中。然而,当多个开发者同时对同一部分代码进行修改时,冲突是不可避免的。本文将详细介绍如何在SVN中解决这些冲突,以便团队成员…

Matlab学习笔记(个人向)

Matlab复习笔记(个人向) 学Matlab无非就一个讲究&#xff0c;老老实实把系统的help帮助中心部分给看了&#xff0c;非常的管用 1. 变量 申明变量表达式;写了;则不会显示在命令行窗口中&#xff0c;不写则会显示 查询 who语句查看工作空间中的所有变量whos查看工作空间中变量的…

ES6 Generator函数的语法 (七)

ES6 中的 Generator函数是一种特殊类型的函数&#xff0c;它允许你控制函数的执行流程&#xff0c;使其在执行过程中可以多次暂停和恢复。Generator 函数在定义时使用星号 * 作为后缀。 主要特性&#xff1a; 惰性计算&#xff1a;Generator 函数执行到 yield 表达式时会暂停&…

(Windows环境)FFMPEG编译,包含编译x264以及x265

本文使用 MSYS2 来编译 ffmpeg 一、安装MSYS2 MSYS2 是 Windows 下的一组编译套件&#xff0c;它可以在 Windows 系统中模拟 Linux 下的编译环境&#xff0c;如使用 shell 运行命令、使用 pacman 安装软件包、使用 gcc (MinGW) 编译代码等。 MSYS2 的安装也非常省心&#x…

RAG技术知识笔记

RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术 介绍 Retrieval-Augmented Generation&#xff08;RAG&#xff09;是一种将信息检索&#xff08;IR&#xff09;与自然语言生成&#xff08;NLG&#xff09;相结合的技术&#xff0c;用于构建具有增强回答能力…