星辰计划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,一经查实,立即删除!

相关文章

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、选择…

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

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

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转换软件 打…

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

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

C语言 | Leetcode C语言题解之第227题基本计算题II

题目&#xff1a; 题解&#xff1a; int calculate(char* s) {int n strlen(s);int stk[n], top 0;char preSign ;int num 0;for (int i 0; i < n; i) {if (isdigit(s[i])) {num num * 10 (int)(s[i] - 0);}if (!isdigit(s[i]) && s[i] ! || i n - 1) {s…

禁止使用存储过程

优质博文&#xff1a;IT-BLOG-CN 灵感来源 什么是存储过程 存储过程Stored Procedure是指为了完成特定功能的SQL语句集&#xff0c;经编译后存储在数据库中&#xff0c;用户可通过指定存储过程的名字并给定参数&#xff08;如果该存储过程带有参数&#xff09;来调用执行。 …

pnpm workspace使用教程【Monorepo项目】

目录 前言一、pnpm简介特点&#xff1a;对比 二、 创建项目添加文件 pnpm-workspace.yaml目录结构pnpm workspace: 协议修改配置文件执行 安装 三、命令解析执行包命令所有包操作命令 四、实例代码 前言 前面两篇&#xff0c;我们讲了 yarn workspace 和 lerna &#xff0c; …

防火墙(ensp USG6000v)---安全策略 + 用户认证综合实验

一. 题目 1&#xff09; 拓扑 2&#xff09;要求 1. DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00 -- 18: 00)可以访问&#xff0c;生产区的设备全天可以访问 2.生产区不允许访问互联网&#xff0c;办公区和游客区允许访问互联网 3.办公区设备10.0.2.10不充许…

桥联亲和素-标记生物素法(BRAB)

桥联亲和素-标记生物素法&#xff08;BRAB&#xff09;是一种基于生物素与亲和素&#xff08;或链霉亲和素&#xff09;之间高亲和力特性的生物医学检测方法。以下是关于BRAB法的详细解释&#xff1a; 一、定义与原理 BRAB法&#xff0c;也称为桥联亲和素-标记生物素法&#xf…

【测试开发】--安全渗透测试

1. 安全渗透 1.1 分类 web数据库安全web应用服务器安全&#xff08;文件上传漏洞、文件包含漏洞&#xff09;web客户端安全&#xff08;XSS跨站攻击&#xff09; 2. sql注入 2.1 sql注入介绍 sql注入在安全问题中排行榜首sql注入攻击是输入参数未经过滤&#xff0c;然后直…

vscode c++可以找到声明却无法自动补全

这个问题折磨了我将近一个月&#xff0c;今天终于被解决了&#xff0c;特此记录 情景再现 事情的起因是我在学习华为的Ascend C算子&#xff0c;需要编写C代码。关于怎么下载库文件怎么编译之类的不是本文的重点&#xff0c;重点是自动补全。 我已经拿到库文件了&#xff0c…

机器学习笔记之监督学习

基本概念 用于训练模型的数据集称为&#xff1a;训练集 成本函数/代价函数&#xff1a;指示模型的运行情况&#xff0c;用于衡量训练数据与该直线的拟合程度。将预测值和目标值做差&#xff0c;该差值也被称为“损失值”。 例如我们需要计算平均的平方误差来衡量 成本函数/…

uniapp适配解决方法

uni-app的专属强大自适应单位upx&#xff0c;rpx&#xff0c;不能动态赋值解决办法… uni-app 使用 upx 作为默认尺寸单位&#xff0c; upx 是相对于基准宽度的单位&#xff0c;可以根据屏幕宽度进行自适应。uni-app 规定屏幕基准宽度750upx。 开发者可以通过设计稿基准宽度计…

无人机之飞行规划与管理篇

无人机飞行规划与管理是确保无人机安全、高效且符合法规的运行的关键步骤。这一过程包括了对飞行任务的详细安排、航线的设定以及风险的评估和管理。下面简述这一过程的主要环节&#xff1a; 一、飞行目的和任务确定 在规划之初&#xff0c;必须明确无人机的飞行目的&#xf…

微软Win11 24H2七月更新补丁KB5040435发布!附下载

系统之家于7月10日发出最新报道&#xff0c;微软为Win11用户发布了24H2版本七月的最新更新补丁KB5040435。用户升级系统后&#xff0c;会发现版本号升至 26100.1150。此次更新针对远程身份验证拨入用户服务(RADIUS)协议与 MD5冲突等问题进行修复。接下来跟随小编看看此次更新的…