Spring-AOP-API设计与实现

news2024/9/24 21:17:14

拦截篇-AOP怎么拦截类和方法

Joinpoint

Joinpoint(连接点)是面向切面编程(Aspect-Oriented Programming, AOP)中的一个核心概念。在 Spring AOP 中,它主要指代的是应用程序中的某个特定点,在这个点上可以应用切面(Aspect)的逻辑。具体来说,Joinpoint 通常指的是方法执行的开始或结束点,也可以是在执行构造器、处理异常抛出或其他特定程序结构上的点。
在 Spring AOP 的上下文中,Joinpoint 主要指的是方法执行点。这意味着你可以将切面逻辑添加到方法调用之前、之后或周围。例如,你可以在方法调用前添加日志记录,在方法调用后清理资源,或者在方法调用前后进行事务管理。
在这里插入图片描述
我们看到JointPoint下面是Invocation,由于Spirng只支持方法的拦截,所以右边的构造器拦截没有对应的实现。
MethodInvocation 有两种实现,一种是反射基于jdk动态代理,一种是cglib动态代理。

public interface Joinpoint {
	// 执行下一个拦截器并返回值,对应的就是方法的执行
	Object proceed() throws Throwable;
	// 返回持有当前连接点的对象
	Object getThis();
	// 这里其实就是被拦截的方法
	AccessibleObject getStaticPart();
}
public interface Invocation extends Joinpoint {
	/**
	 * 获取拦截方法的参数,可以通过改变数组的值来改变参数
	 */
	Object[] getArguments();

}
// 下面这个接口也写了getMethod 返回的是和getStaticPart返回一样的结果,也就证明了getStaticPart返回的是被拦截的这个方法
public interface MethodInvocation extends Invocation {
	/**
	 * Get the method being called.
	 * <p>This method is a friendly implementation of the
	 * {@link Joinpoint#getStaticPart()} method (same result).
	 * @return the method being called
	 */
	Method getMethod();
}

Pointcut

有了连接点,我们只在符合条件的连接点进行切入。Pointcut就是用于匹配JointPoint 的条件,决定了我们是不是在这些切入点进行接入。

public interface Pointcut {
	// 判断类是否符合
	ClassFilter getClassFilter();
	// 判断方法是否符合
	MethodMatcher getMethodMatcher();
	// 默认拦截所有的所有的类
	Pointcut TRUE = TruePointcut.INSTANCE;
}
public interface MethodMatcher {
	// 方法是否满足 这个是方法非动态生成不检验参数
	boolean matches(Method method, Class<?> targetClass);
	// 是不是运行时,指的是这个方法是不是被动态创建的
	boolean isRuntime();
	// 参数是否满足要求,这个可以用于我们对参数进行一系列判断操作,如果isRuntime返回false这个方法不会被调用。
	boolean matches(Method method, Class<?> targetClass, Object... args);
    // 默认拦截所有的方法
	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

PointCut根据ClassFilter 先拦截类,然后根据MethodMatcher拦截方法。

PointCut的使用方式

使用方式一 直接实现Pointcut
public class ImplPointcut implements Pointcut {
	// 由于这个是静态的其实我们只需要创建一次以后进行复用就行了所以直接使用单例模式
    public static final ImplPointcut INSTANCE = new ImplPointcut();
    @Override
    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                return clazz.isAssignableFrom(Client.class);
            }
        };
    }
    @Override
    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
            	// 只要是方法名为execute就进行拦截
                return method.getName().equals("execute");
            }
            @Override
            public boolean isRuntime() {
                return false;
            }
            @Override
            public boolean matches(Method method, Class<?> targetClass, Object... args) {
                return false;
            }
        };
    }
}
public class CustomMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        System.out.println("==========执行拦截===" + method);
        // 这里一定要调用否则方法不会执行
        return invocation.proceed();
    }
}
public class Client {
    public static void main(String[] args) {   
    	// 定义一个切面 里面制定了我们要拦截的方法及我们要拦截的规则
        ImplPointcut pointCut = ImplPointcut.INSTANCE;
        // 定义一个被代理对象
        Client client = new Client();
        // 创代理对象
        ProxyFactory proxy = new ProxyFactory(client);
        // pointCut容器
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointCut, new CustomMethodInterceptor());
        proxy.addAdvisor(advisor);
        // 通过代理获取对象
        Client proxyClient = (Client) proxy.getProxy();
        proxyClient.execute();
    }
    public void execute() {
        System.out.println("================== 执行方法内部逻辑 ==============");
    }
}
使用方式二直接继承StaticMethodMatcherPointcut

因为这个在StaticMethodMatcherPointcut 已经实现了ClassFilter 默认为true,也就是拦截所有的类,我们只需要实现方法的matchs就行了。

public class ApiPointCut extends StaticMethodMatcherPointcut {
    // 要拦截的方法
    private final String methodName;
    // 要拦截的类
    private final Class targetClass;
    public ApiPointCut(String methodName, Class targetClass) {
        super();
        this.methodName = methodName;
        this.targetClass = targetClass;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return Objects.equals(methodName, method.getName())
                && (this.targetClass.isAssignableFrom(targetClass) || this.targetClass == targetClass);
    }
}
使用方式三 继承JdkRegexpMethodPointcut

基于正则的判断,可以看到我们有两个数组,相当于配置了白名单和黑名单。
在这里插入图片描述
在这里插入图片描述

使用方式四 继承ControlFlowPointcut

这个是和当前的堆栈进行匹配,有这样几个问题:
1 有可能堆栈链路很长
2 在获取堆栈信息的时候是同步的有性能损耗
在这里插入图片描述
在new Throable()的时候会去执行fillInStackTrace是一个同步的方法:
在这里插入图片描述

所以不推荐使用,因为效率会比较的低下

PointCut的组合ComposablePointcut

在这里插入图片描述
在jdk里面如果是这个类对应的工具类一般是以s结尾,比如ClassFilters,MethodMatchers,Pointcuts,Objects他们都是工具类,下面的求交或取并集都借助了工具类
在这里插入图片描述
主要是靠这几个方法进行了PointCut的组合:
在这里插入图片描述
可以看到如果是求交集,所有的都要匹配:
在这里插入图片描述
如果是求并集则是,匹配到第一个成功的就认为成功:
在这里插入图片描述

PointCut AspectJ实现

在这里插入图片描述
我们看到PointCut有很多实现,其中ExpressionPointcut下面有AspectJExpressionPointcut实现。正是由于这个实现的存在我们可以写切面表达式。
支持下面这些切入点原语,Spring桥接了AspectJ的能力来和Spirng的PointCut进行整合。也就是说Spring实际上是调用是Aspectj的实现,这块的源代码太多了,这里不贴了。直接搜这个类就可以看到有构造PointCut,初始化Pointcutparser等方法。
在这里插入图片描述

AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.liyong.learn.pointcut.Client.echo())");
Client client = new Client();
ProxyFactory factory = new ProxyFactory(client);
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new MethodBeforeAdvice() {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("before method");
    }
});
factory.addAdvisor(advisor);
Client proxy = (Client) factory.getProxy();
proxy.echo();

通知篇-AOP实现Advice通知

Advice通知(分为befroe, after, around需要手动调用)

在这里插入图片描述
在这里插入图片描述

Around Advice - Interceptor
  • 方法拦截器 - MethodInterceptor
  • 构造器拦截-ConsturctInterceptor(没有进行实现)
    Around Advice 重新定义直接通过Interceptor进行操作
前置动作

标准接口:BeforeAdvice
方法级别:MethodBeforeAdvice(只支持方法级别的拦截)

  • 标准实现,没有引入aspectj的实现
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
	private final MethodBeforeAdvice advice;
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}
	// 这个方法来自于MethodInterceptor
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}
}
// 通过这个案例我们发现拦截会先到MethodBeforeAdviceInterceptor
// 然后才是MethodBeforeAdvice的拦截调用的是匿名内部类的方法
public static void main(String[] args) {
    Map<String, Object>  map = new HashMap<>();
    ProxyFactory factory = new ProxyFactory(map);
    factory.addAdvice(new MethodBeforeAdvice() {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            if (method.getName().equals("put")) {
                System.out.println("=========== 前置拦截 ================");
            }
        }
    });
    Map<String, Object> proxy = (Map<String, Object>) factory.getProxy();
    proxy.put("1", 1);
}

MethodBeforeAdvice有多少个对应的就有多少个MethodBeforeAdviceInterceptor的实现, 其实本质上都是通过MethodInterceptor来实现的

  • AspectJ实现
    正是有了AspectJ实现, 才支持表达式@Before(“execution(* com.example.service..(…))”)
// 也会被转换为MethodBeforeAdviceInterceptor
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
	public AspectJMethodBeforeAdvice(
			Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
		super(aspectJBeforeAdviceMethod, pointcut, aif);
	}
	@Override
	public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
		// 核心逻辑
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
	@Override
	public boolean isBeforeAdvice() {
		return true;
	}
	@Override
	public boolean isAfterAdvice() {
		return false;
	}
}
后置动作

AfterAdvice
AfterReturningAdvice
ThrowsAdvice
三者不太可能同时出现

  • AfterReturningAdvice
// AfterReturningAdvice标准实现
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

	private final AfterReturningAdvice advice;
	public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}
}
  • ThrowsAdvice
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
	private static final String AFTER_THROWING = "afterThrowing";
	private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class);
	private final Object throwsAdvice;
	private final Map<Class<?>, Method> exceptionHandlerMap = new HashMap<>();
	public ThrowsAdviceInterceptor(Object throwsAdvice) {
		Assert.notNull(throwsAdvice, "Advice must not be null");
		this.throwsAdvice = throwsAdvice;
		Method[] methods = throwsAdvice.getClass().getMethods();
		for (Method method : methods) {
			// 我们添加的这个方法需要等于 afterThrowing 并且这个只提供了两种方式 要么是传一个参数要么就是传4个参数
			if (method.getName().equals(AFTER_THROWING) &&
					(method.getParameterCount() == 1 || method.getParameterCount() == 4)) {
				Class<?> throwableParam = method.getParameterTypes()[method.getParameterCount() - 1];
				if (Throwable.class.isAssignableFrom(throwableParam)) {
					// An exception handler to register...
					this.exceptionHandlerMap.put(throwableParam, method);
					if (logger.isDebugEnabled()) {
						logger.debug("Found exception handler method on throws advice: " + method);
					}
				}
			}
		}
		if (this.exceptionHandlerMap.isEmpty()) {
			throw new IllegalArgumentException(
					"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
		}
	}
	public int getHandlerMethodCount() {
		return this.exceptionHandlerMap.size();
	}
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
		    // 发生异常的时候回去找对应的处理方法进行处理,这个处理方法是由我们在设置Advice的时候进行设置的
			Method handlerMethod = getExceptionHandler(ex);
			if (handlerMethod != null) {
				invokeHandlerMethod(mi, ex, handlerMethod);
			}
			throw ex;
		}
	}
	}

使用:

public class ThrowsAdviceDemo {
    public static void main(String[] args) {
        Demo demo = new Demo();
        ProxyFactory factory = new ProxyFactory(demo);
        factory.addAdvice(new ThrowsAdvice() {
            public void afterThrowing(Exception e) {
                System.out.printf("Exception : %s \n", e);
            }
            public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
                System.out.printf("Exception : %s args : %s target %s, exception %s\n", method, Arrays.asList(args), target, e);
            }
        });
        Demo proxy = (Demo) factory.getProxy();
        proxy.echo();
    }
}

在这里插入图片描述
这是因为匿名内部类不是public 所以我们将这个定义到外面去。

public class ThrowsDemo implements ThrowsAdvice {
    public void afterThrowing(Exception e) {
        System.out.printf("Exception : %s \n", e);
    }
    public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
        System.out.printf("Exception : %s args : %s target %s, exception %s\n", method, Arrays.asList(args), target, e);
    }
}
factory.addAdvice(new ThrowsDemo());
  • aspectJ实现
public class AspectJAfterAdvice extends AbstractAspectJAdvice
		implements MethodInterceptor, AfterAdvice, Serializable {
	public AspectJAfterAdvice(
			Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
		super(aspectJBeforeAdviceMethod, pointcut, aif);
	}
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		finally {
			// 在这里实现了我们定义的throws拦截
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}
	@Override
	public boolean isBeforeAdvice() {
		return false;
	}
	@Override
	public boolean isAfterAdvice() {
		return true;
	}
}

整合篇 Pointcut 与 Advice 容器

PointcutAdvisor 的作用

  • 封装通知和切入点:PointcutAdvisor 接口将通知(Advice)和切入点(Pointcut)封装在一起,使得可以在运行时确定哪些连接点(Joinpoint)应该应用通知。
  • 灵活性:通过将通知和切入点分离,PointcutAdvisor 提供了更大的灵活性,使得可以在不改变通知逻辑的情况下更改切入点的定义。
  • 统一管理:它提供了一个统一的接口来管理通知和切入点,便于管理和配置。

Pointcut 与 Advice连接器 PointAdvisor

在这里插入图片描述

  • 通用实现:DefaultPointcutAdvisor
  • AspectJ实现:AspectJExpressionPointcutAdvisor,AspectJPointcutAdvisor 主要是提供了设置pointcut和操纵顺序的能力
  • 静态方法实现:StaticMethodMatcherPointcutAdvisor
  • IOC容器实现:AbstractBeanFactoryPointcutAdvisor
public interface PointcutAdvisor extends Advisor {
    // 获取pointcut的能力
	Pointcut getPointcut();
}
public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdvisor {
	private Advice advice = EMPTY_ADVICE;
	// 存储了Advice
	public void setAdvice(Advice advice) {
		this.advice = advice;
	}
}
// 提供了顺序能力
public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered, Serializable {
	// 有Advice
	private final AbstractAspectJAdvice advice;
	// 有PointCut
	private final Pointcut pointcut;
}

IntroductionAdvisor

public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
	ClassFilter getClassFilter();
	void validateInterfaces() throws IllegalArgumentException;
}
// 提供了获取接口的能力
public interface IntroductionInfo {
	Class<?>[] getInterfaces();
}

IntroductionAdvisor提供了动态设置范围的能力,比如我们实现了两个接口,但是我们只想其中的一个接口被代理,或者我们希望拓展一个接口(当然这个接口最好是被我们的目标对象所实现)

public class Client implements EchoService, RandomService{
    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(new Client());
        factory.addAdvisors(new DefaultIntroductionAdvisor(new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                System.out.println("before");
            }
        }, new IntroductionInfo() {
            @Override
            public Class<?>[] getInterfaces() {
                return new Class[]{EchoService.class};
            }
        }));
        EchoService echoService = (EchoService) factory.getProxy();
        RandomService randomService = (RandomService) factory.getProxy();
        echoService.echo();
        randomService.getRandomNum();
    }

    @Override
    public void echo() {
        System.out.println("output a word");
    }

    @Override
    public int getRandomNum() {
        return 0;
    }
}

这段代码是会报错的,因为我们申明了只代理一个接口,所以无法转换为RandomService。
在这里插入图片描述
如果这样写就没有这个错误,两个接口都会被代理:

// 因为我们set方法没有去获取这个对象的所有接口
ProxyFactory factory = new ProxyFactory(new Client());

因为这样写构造方法里面默认获取了代理对象的所有的接口:
在这里插入图片描述
当然我们可以继续进行拓展,上面提到了一般我们不这么使用,最好是我们被代理的对象也要实现这个接口,如果我们明确不需要代理某一个接口可以通过上面getInterfaces 排除这个接口:

public Class<?>[] getInterfaces() {
     return new Class[]{EchoService.class, RandomService.class, Comparable.class};
 }

适配篇 AdvisorAdapter

AdvisorAdapter(Interceptor适配器)

接口允许扩展到Spring AOP框架,以允许处理新的advisor和Advice类型。实现对象可以从自定义通知类型创建AOP拦截器,使这些通知类型可以在Spring AOP框架中使用,它在底层使用拦截。

在这里插入图片描述

// 可以验证,所有的Advice都是通过Interceptor来实现的,后面都返回了一个Interceptor
// 先判断是否支持,如果支持就进行下一步操作
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}
	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}
}

我的理解是引入适配器提供了拓展性,可以让我们除了AOP提供的这几种通知以外的通知,我们只需要定制一个拦截器即可。

代理对象生成篇

AopProxy(提供了获取代理对象的能力)

在这里插入图片描述

public interface AopProxy {
	Object getProxy();
	Object getProxy(@Nullable ClassLoader classLoader);
}

AopProxy 通过AdvisedSupport关联

AdvisedSupport 类则包含了创建代理所需的所有配置信息,比如目标对象、通知(advice)、切点(pointcut)等
AopProxy 通过内部持有 AdvisedSupport 的实例来获取创建代理所需的配置信息。当需要创建一个新的代理对象时,AopProxy 会根据 AdvisedSupport 中的信息来决定使用哪种代理机制,并完成代理对象的创建。

// 从而可以进行配置实现工厂 
private final AdvisedSupport advised;

在这里插入图片描述

在这里插入图片描述
我们可以看到默认就是DefaultAopProxyFactory来进行实现创建AopProxy。

//所以默认情况下只有两种情况Jdk动态代理或者是Cglib字节码提升
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
	@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)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}
	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}
}

如果我们希望替换Factory进行拓展,可以通过下面方式:
在这里插入图片描述

字节码生成的两种方式

JdkDynamicAopProxy
// 一个config对应了一个Factory 是通过config进行约束的 并且是通过DefaultAopProxyFactory进行创建
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		// 如果没有关联动作 Advice
		if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
	}
// 获取代理对象
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
// 获取advice 拦截链(MethodIntercepter链表)
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
	// We can skip creating a MethodInvocation: just invoke the target directly
	// Note that the final invoker must be an InvokerInterceptor so we know it does
	// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
	Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
	retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
	// We need to create a method invocation...
	MethodInvocation invocation =
			new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
	// Proceed to the joinpoint through the interceptor chain.
	retVal = invocation.proceed();
}
CglibAopProxy
// 这里定义了一些常量
private static final int AOP_PROXY = 0;
private static final int INVOKE_TARGET = 1;
private static final int NO_OVERRIDE = 2;
private static final int DISPATCH_TARGET = 3;
private static final int DISPATCH_ADVISED = 4;
private static final int INVOKE_EQUALS = 5;
private static final int INVOKE_HASHCODE = 6;
// 多了一个分发对象
public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
	Assert.notNull(config, "AdvisedSupport must not be null");
	if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
		throw new AopConfigException("No advisors and no TargetSource specified");
	}
	this.advised = config;
	// 加了一个分发器 
	this.advisedDispatcher = new AdvisedDispatcher(this.advised);
}

我们可以看到CallBack其实也是MethodInterceptor:
在这里插入图片描述

public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 这一步的执行实际上是上面获取的责任链 一个Advice列表
    // 这里有个游标 如果没有Advice 或者是最后一个Advice都会执行invokeJoinpoint()
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
		return invokeJoinpoint();
	}

Object interceptorOrInterceptionAdvice =
		this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
		InterceptorAndDynamicMethodMatcher dm =
				(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
		Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
		if (dm.methodMatcher.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 {
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

AopProxyFactory配置管理器AdvisedSupport

在这里插入图片描述

这个类下面的子类通常是工厂类,这个类存储了Advices 和 Advisors ,TragetSource等 但是没有实现创建代理对象的方法,创建对象的方法是被子类实现。

//继承了ProxyConfig  实现了Advised 接口 提供了配置 和 增加Advice等能力
public class AdvisedSupport extends ProxyConfig implements Advised {}

AdvisorChainFactory

在这里插入图片描述

它只有一种实现DefaultAdvisorChainFactory

// 核心方法
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {
		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
		return interceptorList;
	}

被代理对象的来源

targetSource

被代理的目标对象,可以有以下来源。
SingletonTargetSource(单例模式)
AbstractBeanFactoryBasedTargetSource(可以与IOC打通)
AbstractPrototypeBasedTargetSource

  • AbstractPoolingTargetSource
    • CommonsPool2TargetSource
    • CommonsPoolTargetSource
  • PrototypeTargetSource(原型模式)
  • ThreadLocalTargetSource
    HotSwappableTargetSource (可以动态的替换)

代理对象的创建

ProxyCreatorSupport

在这里插入图片描述

使得我们可以配置创建AopProxyFactory

  • 标准实现ProxyFactory并没有拓展很多内容,基本的功能还是来源于父类ProxyCreatorSupport。
  • ProxyFactoryBean 与IOC进行打通
public class ProxyFactoryBean extends ProxyCreatorSupport
		implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {}
  • AspectJ实现
    AspectJAdvisorFactory 通过反射射获取Advice ReflectiveAspectJAdvisorFactory
  • IOC容器自动代理实现(AbstractAutoProxyCreator)
    • 默认实现DefaultAdvisorAutoProxyCreator
    • 名称匹配实现BeanNameAutoProxyCreator
    • InfrastructureAdvisorAutoProxyCreator

在这里插入图片描述

  • IOC 容器自动代理抽象
    在这里插入图片描述

  • IOC AspectJ自动实现 AspectJAwareAdvisorAutoProxyCreator

  • AOP Infrastructure Bean 接口
    在这里插入图片描述

AdvisedSupportListener

可以监听我们Advice的变化,其原理如下,添加完以后会主动调用changed,发生变化以后可以得到通知:
在这里插入图片描述
在这里插入图片描述

public class Client implements EchoService {
    public static void main(String[] args) {
        Client client = new Client();
        ProxyFactory proxyFactory = new ProxyFactory(client);
        proxyFactory.addAdvice(new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                System.out.println("===========> before");
            }
        });
        proxyFactory.addListener(new AdvisedSupportListener() {
            @Override
            public void activated(AdvisedSupport advised) {
                System.out.println("===========> activate");
            }
            @Override
            public void adviceChanged(AdvisedSupport advised) {
                System.out.println("===========> change");
            }
        });
        EchoService proxy = (EchoService) proxyFactory.getProxy();
        proxy.echo();
        proxyFactory.addAdvice(new AfterReturningAdvice() {
            @Override
            public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
                System.out.println("afterReturning");
            }
        });
        
    }
    @Override
    public void echo() {
        System.out.println("echo");
    }
}

Spring AOP工具类

AOP 上下文辅助类

API - org.springframework.aop.framework.AopContext
• 语义 - ThreadLocal 的扩展,临时存储 AOP 对象

AOP 工具类

API - org.springframework.aop.framework.AopProxyUtils
• 代表方法
• getSingletonTarget - 从实例中获取单例对象
• ultimateTargetClass - 从实例中获取最终目标类
• completeProxiedInterfaces - 计算 AdvisedSupport 配置中所有被代理的接口
• proxiedUserInterfaces - 从代理对象中获取代理接口
• isAopProxy- 判断对象是否为代理对象
• isJdkDynamicProxy - 判断对象是否为 JDK 动态代理对象
• isCglibProxy - 判断对象是否为 CGLIB 代理对象
• getTargetClass - 从对象中获取目标类型
• invokeJoinpointUsingReflection - 使用 Java 反射调用 Joinpoint(目标方法)

AspectJ Enable 模块驱动实现

注解 - org.springframework.context.annotation.EnableAspectJAutoProxy
• 属性方法
• proxyTargetClass - 是否已类型代理
• exposeProxy - 是否将代理对象暴露在 AopContext 中
• 设计模式 - @Enable 模块驱动
• ImportBeanDefinitionRegistrar 实现 -
org.springframework.context.annotation.AspectJAutoProxyRegistrar
• 底层实现
• org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
在这里插入图片描述
参考资料:小马哥极客时间AOP。

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

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

相关文章

国产游戏技术:创新驱动下的全球影响力

国产游戏技术能否引领全球&#xff1f; 前言技术亮点&#xff1a;国产游戏的创新之路面临的挑战&#xff1a;技术创新与市场适应发展机遇&#xff1a;拥抱新技术&#xff0c;拓展国际市场IT技术创新&#xff1a;推动行业发展的新引擎人才需求&#xff1a;技术人才的紧缺与机遇结…

一个利用率超高的楼宇智能化实验室是如何练成的?

在当今快速发展的智能化时代&#xff0c;楼宇智能化实验室作为培养未来智能科技人才的重要基地&#xff0c;其利用率的高低直接关系到科研效率和成果产出。本文将详细探讨如何打造一个利用率超高的楼宇智能化实验室&#xff0c;从设计规划、设备选型、实训内容、管理运营等多个…

Git克隆仓库太大导致拉不下来的解决方法 fatal: fetch-pack: invalid index-pack output

一般这种问题是因为某个文件/某个文件夹/某些文件夹过大导致整个项目超过1G了导致的 试过其他教程里的设置depth为1,也改过git的postBuffer,都不管用 最后还是靠克隆指定文件夹这种方式成功把项目拉下来 1. Git Bash 输入命令 git clone --filterblob:none --sparse 项目路径…

解决pip install fitz 失败问题

背景 实现PDF转图片&#xff0c;需要使用fitz&#xff0c;结果安装的时候出现问题。 先上解决方案&#xff0c;再进行问题分析. 解决方案 安装PyMuPDF能直接使用fitz&#xff0c;按照介绍PyMuPDF也叫称为fitz库。 pip install PyMuPDF 问题分析 关键报错信息&#xff1a;…

WebRTC支持H.265编码:技术挑战与EasyCVR视频汇聚平台解决方案

随着互联网技术的快速发展&#xff0c;视频通信已成为人们日常生活和工作中不可或缺的一部分。WebRTC&#xff08;Web Real-Time Communication&#xff09;作为一种实时通信技术&#xff0c;因其便捷性和高效性而受到广泛关注。然而&#xff0c;在视频编码格式上&#xff0c;W…

武汉凯迪正大—三倍频发生器电源发生装置 倍频试验装置 多频装置

产品概述 武汉凯迪正大KDSF 感应耐压试验装置是根据国家标准《GB311-64》和原水电部发布的《电气设备预防性试验规程》&#xff0c;为满足电力系统对高压电压互感器、倍频感应耐压试验设备的要求而设计的&#xff0c;用于电力系统35-220KV等级电压互感器的交流耐压试验&#x…

证券行业加密业务安全风险监测与防御技术研究

摘要&#xff1a;解决证券⾏业加密流量威胁问题、加密流量中的应⽤⻛险问题&#xff0c;对若⼲证券⾏业的实际流量内容进⾏调研分析&#xff0c; 分析了证券⾏业加密流量⾯临的合规性⻛险和加密协议及证书本⾝存在的⻛险、以及可能存在的外部加密流量威 胁&#xff0c;并提出防…

详解Xilinx GTP结构原理以及验证

文章目录 一、GT简介二、GTP内部结构2.1 整体结构2.2 Quad的内部结构2.3 一对收发器的内部结构2.3.1 发送器的内部结构2.3.2 接收器的内部结构 三、时钟和复位3.1 输入的参考时钟结构3.1.1 IBUFDS_GTE2原语3.1.2 IBUFDS_GTE2原语端口属性说明 3.2 参考时钟选择和分配3.2.1 GTPE…

VScode + Python 下载及安装|python环境配置|中文乱码解决

1&#xff0c;下载安装VScode 直接点击2024.9https://vscode.download.prss.microsoft.com/dbazure/download/stable/fee1edb8d6d72a0ddff41e5f71a671c23ed924b9/VSCodeUserSetup-x64-1.92.2.exe 或者百度搜索VScode&#xff0c;可下载安装最新版 点击后等一下&#xff0c;就…

es、kibana及分词器的安装

文章目录 1、搜索引擎2、为什么使用新型搜索&#xff1f;3、底层原理&#xff1a;倒排索引4、底层API5、你使用了什么分词器&#xff1f;6、ElasticSearch安装6.1、准备目录并授予权限6.2、制作配置文件6.3、初始化es容器6.4、重置es用户密码6.5、安装中文分词器6.5.1、 把资料…

人机环境系统智能中的知己、趣时、变通

在易经中&#xff0c;知己、趣时、变通是重要的概念&#xff0c;它们在人机环境系统智能中的作用可以理解为&#xff1a; 知己&#xff1a;指的是对系统自身的深刻了解。在人机环境系统中&#xff0c;这意味着系统能够清晰地识别和理解自身的能力、限制以及用户的需求&#xff…

鸿蒙内核源码分析(gn应用篇) | gn语法及在鸿蒙中巧夺天工

gn是什么? gn 存在的意义是为了生成 ninja,如果熟悉前端开发,二者关系很像 Sass和CSS的关系. 为什么会有gn,说是有个叫even的谷歌负责构建系统的工程师在使用传统的makefile构建chrome时觉得太麻烦,不高效,所以设计了一套更简单,更高效新的构建工具gnninja,然后就被广泛的使用…

从数据生成到图数据库:Linux下Neo4j的CSV导入

文章目录 简介找到import文件夹准备csv表格数据导入neo4jTeacherStudent 简介 介绍如何在Linux系统中设置和使用Neo4j数据库。 首先&#xff0c;找到Neo4j的import文件夹&#xff0c;通常位于Neo4j安装目录下的data文件夹内&#xff0c;并展示通过neo4j.conf配置文件查找和修…

EmguCV学习笔记 C# 6.S 特别示例

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

Datawhale AI夏令营 第五期 CV方向 Task1笔记

Task1&#xff1a;跑通YOLO方案baseline&#xff01; 赛题解读 根据您提供的图片内容&#xff0c;这是一份关于城市管理违规行为智能识别竞赛的赛题描述。以下是对内容的分析&#xff1a; 一、赛题描述 背景&#xff1a;随着城市化进程的加速&#xff0c;城市管理面临新的挑…

JavaScript初级——获取元素的样式

1、获取元素当前的显示样式 语法&#xff1a;元素.currentStyle.样式名 可以用来读取当前元素正在显示的样式&#xff0c;如果当前的元素没有设置样式&#xff0c;则获取他的默认值 currentStyle只有IE8浏览器支持&#xff0c;其他的浏览器不支持。 2、在其他浏览器中&#xf…

修复 502 Bad Gateway 错误的 6 种方法

通常&#xff0c;我们在使用网站时可能会遇到一系列错误。有些非常常见&#xff0c;例如 404&#xff0c;有些则不太常见&#xff0c;例如 101。这些被称为 HTTP 状态代码。其中&#xff0c;502 错误是某种服务器错误。那么&#xff0c;让我们先了解一下 Bad Gateway 502 的含义…

JetBrains Rider 2024 for Mac/Win:跨平台.NET IDE集成开发环境的全面解析

JetBrains Rider 2024作为一款专为Mac和Windows用户设计的跨平台.NET IDE集成开发环境&#xff0c;以其强大的功能和卓越的性能&#xff0c;在.NET开发领域脱颖而出。这款IDE不仅集成了IntelliJ IDEA的代码编辑优势&#xff0c;还融合了ReSharper的C#开发体验&#xff0c;为开发…

STM32(F103ZET6)第十六课:WIFI模块的配置与应用

目录 需求一、wifi模块简述二、配置流程1.配置通信串口2.配置引脚与中断接受3.简述AT指令4.程序编写 三、需求实现代码 需求 完成WiFi模块的配置,使其能连接服务器并最终能和服务器相互发送消息。 一、wifi模块简述 本项目开发版上没有封装好的WIFI模块&#xff0c;所以借助…

SpringBoot日常:基于DeferredResult的异步处理

文章目录 示例代码代码执行结果代码执行过程解析DeferredResult 的优势 本章内容主要讲讲基于DeferredResult的异步处理 在 Servlet 容器中启用了异步请求处理功能&#xff0c;控制器方法就可以用 包装任何支持的控制器方法返回值DeferredResult&#xff0c;控制器可以从不同的…