Spring-Aop源码解析(上)

Aop:在原有功能不变的基础上, 进行功能增强,比如在方法发生异常/方法发生之前/之后进行额外逻辑的插入。这样隐式横向插入逻辑的动作被我们称为Aop。

在Spring中,我们都是将对象交给singletonObjects单例池管理,在使用这个对象之前,就已经生成了对应的Bean。Spring在AspectJ的基础上,进行了封装(就是直接把AspectJ的代码抄了一下),形成了Spring-Aop,让我们可以在原有功能的基础上,进行额外的逻辑增强(代理模式),就是我放到Spring中的Bean,其实不是我认为的Bean,而是生成了一个代理对象来代理这个Bean伪代码如下

java 复制代码
try{
    proxy extends target//代理对象继承自被代理对象

    methodBeforeAround(proxy)//方法执行环绕前
    methodBefore(proxy)//方法执行前
    method.invoke(proxy)//被代理对象执行
    methodAfterAround(proxy)//方法执行环绕后
}
catch(Throwable e){
    exceptionMethod(proxy);//出异常的时候执行
    throw e;
}
finally{
    methodAfter(proxy);//方法执行完之后
}

我们可以对方法执行前后的各个点进行一个额外逻辑的插入,对原有方法进行功能增强。然后在Bean的生命周期中对target(被代理对象)额外生成一个代理对象,将其放到单例池中,然后在使用的时候将该代理对象依赖注入到对应的属性中用于替代target对象。

java中的Demo实现代码如下

java 复制代码
public static void main(String[] args) {

    UserService userService = new UserService();
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.addAdvice(new MyBeforeAdvice());

    //对UserService的test方法进行前置增强
    UserService proxy = (UserService) proxyFactory.getProxy();
    proxy.test();

}

public class MyBeforeAdvice implements MethodBeforeAdvice {

   @Override
   public void before(Method method, Object[] args, Object target) throws Throwable {
      System.out.println("方法执行前执行");
   }
}


//print:  方法执行前执行
//print:  方法test执行

上述Demo我们对UserService进行了一个前置的代理增强, 可以看到前置处理就是实现了一个MethodBeforeAdvice,那么其他额外功能的插入点,也有对应的Advice接口

AfterReturningAdvice:方法return后执行

MethodInterceptor:围绕方法前后都可以自行实现,需要手动调用proceed方法

ThrowsAdvice:出异常之后执行,这个需要自己手写afterThrowing方法

MethodBeforeAdvice:方法执行前执行

上述就是Spring-Aop提供给我们的Advice,用于额外功能点的增强。这些Advice最后都会被转成MethodInterceptor 。其实还有一种afterAdvice,就是不管有没有异常,都会在最后执行,finally机制,但是spring-Aop提供给我们的接口(AfterAdvice)中并没有对应的实现类,但是Aspectj中有对应的实现类AspectJAfterAdvice

在Spring中我们通常使用Jdk代理和Cglib代理来生成代理对象,我们先来看一下Cglib动态代理方法执行时的核心源码

获取代理对象CglibAopProxy.getProxy()->只提取核心代码

java 复制代码
	public Object getProxy(@Nullable ClassLoader classLoader) {
		//获取被代理的类
		Class<?> rootClass = this.advised.getTargetClass();
		//创建用于生成代理对象的工具
		Enhancer enhancer = createEnhancer();
		// 将代理类的父类设置成被代理类
		enhancer.setSuperclass(proxySuperClass);
		// 代理类额外要实现的接口
		enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        //这是最核心的源码,获取Spring中匹配的Advisor,后续章节会讲到
        //也是这一步将Advisor转换成了MethodInterceptor
		Callback[] callbacks = getCallbacks(rootClass);
		//返回代理对象
		return createProxyClassAndInstance(enhancer, callbacks);
	}

**执行逻辑(额外逻辑的执行)**CglibAopProxy.proceed()->ReflectiveMethodInvocation.proceed()

java 复制代码
	public Object proceed() throws Throwable {
		//interceptorsAndDynamicMethodMatchers:
		// currentInterceptorIndex初始值为-1,每调用一个interceptor就会加1,然后接着会递归走到这里,就是一个简单的执行链
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			//最后一个interceptor执行完后就会执行被代理方法
			return invokeJoinpoint();
		}
		// 每次递归之后currentInterceptorIndex就++
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		// 当前interceptor是InterceptorAndDynamicMethodMatcher,则先进行匹配,匹配成功后再调用该interceptor
		// 如果没有匹配则递归调用proceed()方法,调用下一个interceptor
		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 {
				// 不匹配则执行下一个MethodInterceptor
				return proceed();
			}
		}
		else {
			// 直接调用MethodInterceptor,传入this,在内部会再次调用proceed()方法进行递归
			// 比如MethodBeforeAdviceInterceptor
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

在执行对应的方法的时候,会对传入进来的Interceptor链进行一个操作(递归执行)(这条链其实就是找到的Advisor转换成的MethodInterceptor,在getCallBacks中会转 ),初始值currentInterceptorIndex为-1,然后每次执行的时候就++,观察是否到达了对应的链Size,如果是,那么代表对应的代理逻辑已经被执行完,该执行被代理方法了,后面有一个判断是否是动态的MethodMatcher(关于Matcher,Spring-Aop源码解析(中)会解析),下面代码也是对其进行的一个简要分析。如果满足了对应的matcher匹配,那么就会执行对应的链路中的额外逻辑(这里面会自动调用proceed方法),如果不满足,那么直接递归,观察下一个MethodInterceptor是否满足(不断的递归)

java 复制代码
		proxyFactory.addAdvisor(new PointcutAdvisor() {
			@Override
			public Pointcut getPointcut() {
				return new Pointcut() {
					@Override
					public ClassFilter getClassFilter() {
                        //先匹配类
						return null;
					}

					@Override
					public MethodMatcher getMethodMatcher() {
                        //再匹配方法
						new MethodMatcher() {
							@Override
							public boolean matches(Method method, Class<?> targetClass) {
								return false;
							}

							@Override
							public boolean isRuntime() {
                            //如果runTime设置成true,那么下面的matches会生效
                            //进行额外方法中的参数是否匹配
                            //也就是转换成了上面我们讲的InterceptorAndDynamicMethodMatcher
								return false;
							}

							@Override
							public boolean matches(Method method, Class<?> targetClass, Object... args) {
								return false;
							}
						}
					}
				};
			}
		});

在匹配对应的Advisor是否匹配当前正在执行的方法的时候,会先根据Class做匹配,观察类是否相等,然后对方法进行一个匹配,观察方法是否相等,第三道过滤必须将MethodMatcher中的Runtime属性设置成true,才会转换成InterceptorAndDynamicMethodMatcher,就是会进一步对方法中的参数进行一个额外的判断,观察参数是否也是匹配的

相关推荐
黄昏恋慕黎明29 分钟前
spring MVC了解
java·后端·spring·mvc
有一个好名字2 小时前
MyBatis-Plus 三种数据库操作方式详解 + 常用方法大全
数据库·mybatis
-Xie-2 小时前
Redis(八)——多线程与单线程
java·数据库·redis
抛砖者2 小时前
1、Ubuntu上MySQL安装,密码设置,远程访问,端口修改
mysql·ubuntu
G探险者2 小时前
为什么 VARCHAR(1000) 存不了 1000 个汉字? —— 详解主流数据库“字段长度”的底层差异
数据库·后端·mysql
RainbowSea3 小时前
13. Spring AI 的观测性
java·spring·ai编程
Albert Tan4 小时前
Oracle EBS R12.2.14 清理FND_LOBS并释放磁盘空间
数据库·oracle
L.EscaRC4 小时前
图数据库Neo4j原理与运用
数据库·oracle·neo4j
知己80804 小时前
docker搭建图数据库neo4j
数据库·docker·neo4j
TDengine (老段)4 小时前
什么是 TDengine IDMP?
大数据·数据库·物联网·时序数据库·tdengine·涛思数据