Spring-Aop源码解析(中)

Spring-Aop源码解析(上)上文讲解了到底什么是Aop,以及围绕方法该如何去找对应的增强点,包括整个Advisor链路的执行顺序,本文来对上文中存在的一些关键点进行一个深入挖掘

Advice:要增强的逻辑,就是我们执行额外逻辑的那坨代码,比如要在test方法之前横向插入一段逻辑,那么我们就需要实现BeforeAdvice接口,来重写里面的before方法。

JointPoint:具体要增强的哪个点,比如我要对UserService下的test方法进行增强,那么test方法就是JointPoint

PointCut:切入点,我要对哪一些点进行增强, 这些点就被我们称为PointCut,就是一类JoinPoint

Advisor:PointCut+Advice,我要对哪一类进行一个额外的逻辑增强,Advisor可以理解为一种行为

下面的代码可以一目了然:

设置一个Advisor,并且在Advisor中可以设置PointCut和Advice,PointCut中又分为ClassFilter过滤和MethodMatcher过滤,MethodMatcher中的runtime属性设置成true的时候会对方法中的参数进行一个额外的逻辑校验。Advice就是我们自己要实现的增强逻辑,所以这段代码翻译成中文就是,我要对UserService下的test方法,并且test方法中只有一个参数的这个方法进行前置逻辑的增强。那么在Spring中是如何实现这段逻辑的

java 复制代码
		proxyFactory.addAdvisor(new PointcutAdvisor() {
			@Override
			public Pointcut getPointcut() {
				return new Pointcut() {
					@Override
					public ClassFilter getClassFilter() {
					return 	new ClassFilter() {
							@Override
							public boolean matches(Class<?> clazz) {
								//只对UserSer的类进行增强
								return UserService.class.equals(clazz);
							}
						};
					}
					@Override
					public MethodMatcher getMethodMatcher() {
						return new MethodMatcher() {
							@Override
							public boolean matches(Method method, Class<?> targetClass) {
								//只对UserService下的test方法进行增强
								if(method.equals("test")){
									return true;
								}
								return false;
							}

							@Override
							public boolean isRuntime() {
								//如果这边设置成true,那么下面那个matches才会生效
								return true;
							}

							@Override
							public boolean matches(Method method, Class<?> targetClass, Object... args) {
								//根据方法中的参数进行额外逻辑的判断
								if(args.length == 1){
									return true;
								}
								return false;
							}
						};
					}
				};
			}

			@Override
			public Advice getAdvice() {
				//实现了BeforeAdvice的一个类
				return new MyBeforeAdvice();
			}

			@Override
			public boolean isPerInstance() {
				return false;
			}
		});

Spring-Aop源码解析(上)篇中,我们遗留了一个方法getCallbacks的方法,这里面就是Spring处理这些Advisor的实现逻辑,getCallBacks中核心逻辑如下

java 复制代码
	private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
		
		//获取被代理对象的所有方法
		Method[] methods = rootClass.getMethods();
		Callback[] fixedCallbacks = new Callback[methods.length];
		for (int x = 0; x < methods.length; x++) {
			Method method = methods[x];
			//将方法和被代理类传入,进行Advisor的过滤,到底哪些Advisor是被我需要的
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
			//针对于我要增强的方法和类得到的代理链(Advice链),生成一个Interceptor,然后缓存
			fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
					chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
			this.fixedInterceptorMap.put(method, x);
		}
	}

继续追踪源码:AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice->DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice,这块代码的逻辑就完美呈现了PointCut到底是如何去匹配的,如果匹配,就将该Advisor转换成成MethodInterceptor(这里面会借用适配器去强行转换),所以最后我们返回的就是针对于匹配到的被代理对象的一串List<MethodInterceptor>,上文中的疑惑(DynamicMethodMatcher到底是啥玩意,包括Advisor是如何整合成MethodInterceptor的)也在本文中得到了解析

java 复制代码
	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {
		// 从ProxyFactory中拿到所设置的Advice(添加时被封装成了DefaultPointcutAdvisor)
		// 添加的时候会控制顺序
        //这个Advisor是怎么塞进去的会在后续章节详细讲解,主要是在Bean的生命周期初始化后那边塞得
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				// 先匹配类
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					// 再匹配方法
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match = mm.matches(method, actualClass);
					if (match) {
						// 如果匹配则将Advisor封装成为Interceptor,当前Advisor中的Advice可能即是MethodBeforeAdvice,也是ThrowsAdvice
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							//如果是runTIme属性被设置成true,那么会将它封装成Dynamic类型,就是针对方法额外做了一个参数的匹配
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				// 最终,interceptorList中存储的是当前正在执行的Method所匹配的MethodInterceptor,可能动态的,也可能是非动态的,
				// 找到Method所匹配的MethodInterceptor后,就会开始调用这些MethodInterceptor,如果是动态的,会额外进行方法参数的匹配
				}
			}}
	}

    //Advisor是如何转换成MethodInterceptor的
	@Override
	public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
		List<MethodInterceptor> interceptors = new ArrayList<>(3);
		Advice advice = advisor.getAdvice();
        //如果默认是MethodInterceptor,那么就直接转
		if (advice instanceof MethodInterceptor) {
			interceptors.add((MethodInterceptor) advice);
		}
        //借助于适配器,将其转换成MethodInterceptor
		// 将Advice适配成MethodInterceptor
		for (AdvisorAdapter adapter : this.adapters) {
			if (adapter.supportsAdvice(advice)) {
				interceptors.add(adapter.getInterceptor(advisor));
			}
		}
		if (interceptors.isEmpty()) {
			throw new UnknownAdviceTypeException(advisor.getAdvice());
		}
		return interceptors.toArray(new MethodInterceptor[0]);
	}

总结:

Advisor中可以自行的设置PointCut和Advice,在方法执行的时候(method.invoke())PointCut则会对被代理对象的类和方法进行匹配,如果匹配成功,那么就会将该Advisor打包成一个MethodInterceptor链(因为可能有很多增强逻辑,针对于同一个类或者方法),然后返回。匹配逻辑则是先匹配类,在匹配方法,如果runTime属性设置成true,那么会再去匹配方法中的参数是否匹配,然后针对于参数这种情况会返回一个DynamicMethodMatcher(上文中有讲解到这是在哪用的)

将Advisor转换成MethodInterceptor的适配器如下,Spring-Aop并没有提供给我们AfterAdvice的适配器,但是Aspectj里面有就是@After注解(AspectJAfterAdvice),你要的话可以自己使用,他是实现了MethodInterceptor接口的,所以在之前就可以直接强转了,不用这些适配器

java 复制代码
	public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}
相关推荐
是小崔啊7 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
黄公子学安全16 分钟前
Java的基础概念(一)
java·开发语言·python
liwulin050617 分钟前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc22 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
qxlxi22 分钟前
【Spring事务】深入浅出Spring事务从原理到源码
spring
Yuan_o_22 分钟前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
Oneforlove_twoforjob26 分钟前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
程序员一诺1 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
数据小小爬虫1 小时前
利用Java爬虫获取苏宁易购商品详情
java·开发语言·爬虫
小汤猿人类1 小时前
nacos-服务发现注册
java·开发语言·服务发现