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

news2024/12/25 14:50:03

承接上一文 动态代理 ,这里探究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() {
            @Override
            public Class<?> getTargetClass() {
                return ZhouJieLun.class;
            }
            @Override
            public Object getTarget() {
                return new ZhouJieLun();
            }
        }, SpringProxyTest.class.getClassLoader());
        proxy.maiPiao();
    }

    public static class ZhouJieLun implements MaiPiao{
        @Override
        public 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() {
            @Override
            public Advice getAdvice() {
                return new LogAdvice();
            }
            @Override
            public 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 {
        @Override
        public 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;


	@Override
	public 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 {
            //获取代理配置当中的需要被代理的对象的class
			Class<?> 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 above
			ProxyCallbackFilter 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() failed
			throw 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
	@Nullable
	public 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" pollution
    e.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
	@Nullable
	protected 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 processed
				continue;
			}
			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();
    }
    @Configuration
    public static class Config {
        @Bean
        public 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();
        }
    }
    @Component
    public static class LogAdvisor extends AbstractPointcutAdvisor {
        public LogAdvisor() {
            System.out.println("LogAdvisor");
        }

        @Override
        public Pointcut getPointcut() {
            return new AnnotationMatchingPointcut(null,Log.class,false);
        }

        @Override
        public Advice getAdvice() {
            return new LogMethodInterceptor();
        }

        private static class LogMethodInterceptor implements MethodInterceptor {
            @Override
            public 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 "";
    }

    @Service
    public static class B {
        @Log("123")
        public void maiPiao()
        {
            System.out.println("B mai piao");
        }
    }

    @Service
    public static class A implements MaiPiao{
        @Autowired
        private B b;
        @Override
        public 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.coloradmin.cn/o/1927889.html

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

相关文章

学习小记-Nacos的服务注册与发现原理

服务注册&#xff1a; 当一个服务实例启动时&#xff0c;它会向 Nacos 服务器注册自己的信息&#xff0c;包括 IP 地址、端口号、元数据&#xff08;如服务版本、区域信息等&#xff09;。服务实例使用 Nacos API 发送注册请求&#xff0c;Nacos 服务器接收请求并存储服务实例信…

浅聊授权-spring security和oauth2

文章目录 前言自定义授权spring security授权oauth2授权概述 前言 通常说到授权&#xff0c;就会想到登录授权、token令牌、JWT等概念&#xff0c;授权。顾名思义就是服务器授予了客户端访问资源的权益&#xff0c;那么要实现授权有几种方案呢&#xff0c;三种授权方式在公司项…

Python个性化电影推荐系统的设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

客户端通过服务器进行TCP通信(三)

一. 对TCP的基础讲解 服务端 1. 首先创建一个套接字&#xff0c;TCP是面向字节流的套接字&#xff0c;故需要使用SOCK_STREAM 2. 然后使用bind()函数将套接字与服务器地址关联(如果是在本地测试&#xff0c;直接将地址设置为217.0.0.1或者localhost&#xff0c;端口号为1000…

Mac电脑下运行java命令行出现:错误: 找不到或无法加载主类

mac 电脑 问题复现 随手写了一个main方法&#xff0c;想用命令行操作 进入 BlockDemo.java 所在目录&#xff1a; wnwangnandeMBP wn % cd /Users/wn/IdeaProjects/test/JianZhiOffer/src/main/java/com/io/wn wnwangnandeMBP wn % ls -l total 16 -rw-r--r-- 1 wangnan …

前端框架学习之 搭建vue2的环境 书写案例并分析

目录 搭建vue的环境 Hello小案例 分析案例 搭建vue的环境 官方指南假设你已经了解关于HTML CSS 和JavaScript的中级知识 如果你刚开始学习前端开发 将框架作为你的第一步可能不是最好的主意 掌握好基础知识再来吧 之前有其他框架的使用经验会有帮助 但这不是必需的 最…

【JavaScript 算法】二分查找:快速定位目标元素

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、算法原理二、算法实现三、应用场景四、优化与扩展五、总结 二分查找&#xff08;Binary Search&#xff09;是一种高效的查找算法&#xff0c;适用于在有序数组中快速定位目标元素。相比于线性查找&#xff0c;二分查找…

【java】力扣 买卖股票的最佳时机II

文章目录 题目链接题目描述思路代码 题目链接 122.买卖股票的最佳时机II 题目描述 思路 这道题和121.买卖股票的最佳时机 有所不同&#xff0c;不同点在于&#xff0c;这道题的股票可以多次买卖(但是要在买之前先卖掉) 详细思路请看链接的文章【java】力扣 买卖股票的最佳时…

Milvus核心设计(2)-----TSO机制详解

目录 背景 动机 Timestamp种类及使用场景 Guarantee timestamp Service timestamp Graceful time Timestamp同步机制 主流程 时间戳同步流程 背景 Milvus 在设计上突出了分布式的设计,虽然Chroma 也支持分布式的store 与 query。但是相对Milvus来说,不算非常突出。…

Linux--USB驱动开发(二)插入USB后的内核执行程序

一、USB总线驱动程序的作用 a&#xff09;识别USB设备 1.1 分配地址 1.2 并告诉USB设备(set address) 1.3 发出命令获取描述符 b&#xff09;查找并安装对应的设备驱动程序 c&#xff09;提供USB读写函数 二、USB设备工作流程 由于内核自带了USB驱动,所以我们先插入一个U…

SQL中的谓词与谓词下推

在 SQL 查询中&#xff0c;谓词&#xff08;Predicate&#xff09;是用来对数据进行过滤的条件。它们决定了数据从数据库表中被选择的条件。理解和正确使用 SQL 谓词对于编写高效查询至关重要。 目录 什么是谓词&#xff1f;一个真实的故事SQL 谓词的代码示例比较谓词逻辑谓词…

服务客户,保证质量:腾讯云产品的质量实践

分享主题是“服务客户&#xff0c;保证质量”。自从20年开始&#xff0c;我们把质量提升到了一个前所未有的高度。为什么会如此重视质量呢&#xff1f;在竞争激烈和复杂的市场环境中&#xff0c;产品质量对于企业的重要性不言而喻。一旦出现了质量事故&#xff0c;对客户和企业…

SCI二区|母亲优化算法(MOA)原理及实现【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2023年&#xff0c;I Matoušov受到母亲与孩子之间的人际互动启发&#xff0c;提出了母亲优化算法&#xff08;Mother Optimization Algorithm, MOA&#xff09;。 2.算法原理 2.1算法思…

PHP中的函数与调用:深入解析与应用

目录 一、函数基础 1.1 函数的概念 1.2 函数的定义 1.3 函数的调用 二、PHP函数的分类 2.1 内置函数 2.2 用户自定义函数 2.3 匿名函数 2.4 递归函数 2.5 回调函数 2.6 魔术方法 三、函数的参数与返回值 3.1 参数传递 3.2 返回值 四、函数的高级特性 4.1 可变函…

【HarmonyOS】鸿蒙中如何获取用户相册图片?photoAccessHelper.PhotoViewPicker

【HarmonyOS】鸿蒙中如何获取用户相册图片&#xff1f;photoAccessHelper.PhotoViewPicker 前言 有同学私聊我说&#xff0c;之前的博客文章提到的没有HarmonyOS白名单帐号&#xff0c;如何在OpenHarmony Gitee开发仓里学习API接口。需要注意一个点&#xff0c;默认看到的文档…

07 物以类聚 基于特征的七种算法模型

你好&#xff0c;我是大壮。在 06 讲中&#xff0c;我们介绍了协同过滤&#xff08;CF&#xff09;算法&#xff0c;它主要通过用户行为构建用户物品共现矩阵&#xff0c;然后通过 CF 算法预测结果实现个性化推荐。其实&#xff0c;除了利用用户行为特征之外&#xff0c;我们还…

决策树(ID3,C4.5,C5.0,CART算法)以及条件推理决策树R语言实现

### 10.2.1 ID3算法基本原理 ### mtcars2 <- within(mtcars[,c(cyl,vs,am,gear)], {am <- factor(am, labels c("automatic", "manual"))vs <- factor(vs, labels c("V", "S"))cyl <- ordered(cyl)gear <- ordered…

VMware与centos安装

目录 VM安装 安装centos VM安装 VMware Workstation Pro是VMware&#xff08;威睿公司发布的一袋虚拟机软件&#xff09;&#xff0c;它主要功能是可以给用户在单一的桌面上同时运行不同的操作系统&#xff0c;也是可以进行开发、测试、部署新的应用程序的最佳解决方案。 开始…

力扣144题:二叉树的先序遍历

给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xff1a;root [1] 输出&am…

跳妹儿学编程之ScratchJr(9):程序控制积木篇—短跑比赛

跳妹儿学编程之ScratchJr(7)&#xff1a;动作积木篇—爸爸去散步 跳妹儿学编程之ScratchJr(8)&#xff1a;外观积木篇—捉迷藏 跳妹儿学编程之ScratchJr(9)&#xff1a;程序控制积木篇—短跑比赛 引言 在之前的一篇文章中&#xff0c;我们了解了ScratchJr的动作积木和外观积…