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());
	}
相关推荐
Daniel 大东22 分钟前
BugJson因为json格式问题OOM怎么办
java·安全
Theodore_10224 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸5 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象6 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了6 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·7 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic7 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王7 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康7 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
qq_17448285757 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序