一文学习 Spring AOP 源码全过程

Spring AOP 各阶段的流程图

简而言之, 可以分为三步走一是 spring 如何解析配置, 如何封装关于AOP概念配置对象的?二是 spring 如何根据配置对象创建代理对象?三是 调用代理对象方法的过程, spring 是如何拦截的?

一、AOP 相关配置 的解析过程 (XML)

AOP配置入口点 (XML)

在XML中配置 <aop: ../>启用Aop标签, 在解析XML自定义标签时, 会拿到 AopNamespaceHandler 命名空间处理器:

复制代码
public class AopNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.5+ XSDs
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace in 2.5+
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

其内部的注册的 ConfigBeanDefinitionParser 做了两件事

注册一个名称为org.springframework.aop.config.internalAutoProxyCreator 的Bean Definition(对AOP处理);

对应的类, 根据情况有以下三个可能: org.springframework.aop.config.AopConfigUtils#APC_PRIORITY_LIST, 系统会按优先级注册以下三个自动代理创建器之一:AnnotationAwareAspectJAutoProxyCreator (最高优先级)当项目中存在AspectJ依赖时启用支持@Aspect注解的切面、@Before、@After等通知注解继承自 AspectJAwareAdvisorAutoProxyCreator能够识别并处理基于注解的切面定义AspectJAwareAdvisorAutoProxyCreator (第二优先级)当项目中存在AspectJ依赖但不需要注解支持时启用主要处理XML配置的切面定义继承自 InfrastructureAdvisorAutoProxyCreator提供对AspectJ切点表达式的解析能力InfrastructureAdvisorAutoProxyCreator (最低优先级)当项目中不存在AspectJ依赖时启用只处理基础的advisor,不支持切面编程是最基础的自动代理创建器适用于简单的拦截器场景

注册过程通过AopConfigUtils.registerAutoProxyCreatorIfNecessary方法实现,该方法会根据classpath中是否存在AspectJ相关类来决定注册哪个具体的实现类。

这里对应的是 AspectJAwareAdvisorAutoProxyCreator

解析 aop:config 标签的子元素 (pointcut, advisor, aspect)

每一个通知(Advice) 都会封装为一个 AspectJPointcutAdvisor 的BeanDefinition 将其注册到 BeanFactoryorg.springframework.aop.config.ConfigBeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)

复制代码
@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		/**
		 * 1. 注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的
		 * 名称是: org.springframework.aop.config.internalAutoProxyCreator
		 * 对应的类, 根据情况有以下三个可能: org.springframework.aop.config.AopConfigUtils#APC_PRIORITY_LIST
		 *	    InfrastructureAdvisorAutoProxyCreator.class
		 * 		AspectJAwareAdvisorAutoProxyCreator.class
		 * 		AnnotationAwareAspectJAutoProxyCreator.class
		 *
		 */
		configureAutoProxyCreator(parserContext, element);

		/**
		* 2. 解析 <aop:config> 标签的子元素 (pointcut, advisor, aspect)
		* 解析 <aspect ...>:  
		* 每一个通知(Advice) 都会封装为一个 AspectJPointcutAdvisor 的BeanDefinition 然后将其注册到 BeanFactory  
		*  
		*  AspectJPointcutAdvisor 的包含情况  
		*   AspectJPointcutAdvisor 包括: AspectJXXXAdvice(有五种通知)  
		*   而 AspectJXXXAdvice 则包括: 三个关键属性  
		*  1. java.lang.reflect.Method(通知切面的方法)  
		*  2. org.springframework.aop.aspectj.AspectJExpressionPointcut(切入点表达式)  
		*  3. org.springframework.aop.aspectj.AspectInstanceFactory (切面实例工厂)
		 */
		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			String localName = parserContext.getDelegate().getLocalName(elt);
			switch (localName) {
				/**
				 * 解析 pointcut/切入点  //筛选连接点, 即: 哪些方法需要被代理
				 */
				case POINTCUT -> parsePointcut(elt, parserContext);
				/**
				 *  解析 advisor/通知/建议/增强处理  //即: 增强功能这一部分代码
				 */
				case ADVISOR -> parseAdvisor(elt, parserContext);
				/**
				 *  解析 aspect/切面 //切面是**通知(写日志)** 和**切入点(要被代理的方法)**  的结合,  即:**修改后的整个方法, (有正常需要调用的代码,又有写日志的)**
				 *
				 */
				case ASPECT -> parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}

二、AOP 代理对象 的创建过程

AOP 处理入口点 (BPP)

ApplicationContext 在容器创建bean流程中 {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, RootBeanDefinition, Object[])} 会调用接口方法

InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation如果其该返回不为null, 则直接使用这个bean实例返回, 不走正常实例bean流程.org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition)

复制代码
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// 如果是合成的(mbd是AOP的时候为true) 并且实现 InstantiationAwareBeanPostProcessor 接口
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					/**
					 * 调用 InstantiationAwareBeanPostProcessor
					 * #postProcessBeforeInstantiation
					 * #postProcessAfterInitialization
					 */
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

AspectJAwareAdvisorAutoProxyCreator 创建代理对象

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary

复制代码
/**
	 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
	 * @param bean the raw bean instance
	 * @param beanName the name of the bean
	 * @param cacheKey the cache key for metadata access
	 * @return a proxy wrapping the bean, or the raw bean instance as-is
	 */
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}
		/**
		 * 拿到所有匹配织入当前bean的 所有通知器(Advisor)
		 * 做了三件事, 见: {@link org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean(java.lang.Class, java.lang.String, org.springframework.aop.TargetSource)}
		 * 1. 往返回 `AspectJXXXAdvice`列表数组`0`索引 插入一个{@link org.springframework.aop.interceptor.ExposeInvocationInterceptor} 实例
		 * 方便传递参数用的
		 *
		 * 2. 怎么匹配(Advisor)?
		 * Advisor中的 `AspectJExpressionPointcut` 是实现 {@link ClassFilter} 和 {@link org.springframework.aop.MethodMatcher} 接口
		 * 一个进行类匹配, 一个进行方法匹配.
		 *
		 * 3.  排序, 基于 `有向无环` 图进行排序; 可能匹配到多个切面(aspect)
		 *
		 */
		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			/**
			 *{@link org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#advisedBeans}
			 * 这个变量缓存所有处理过的 bean名称, value 为 boolean值, 如果为false 则不处理
			 */
			this.advisedBeans.put(cacheKey, Boolean.TRUE);//缓存, 表示已处理
			/**
			 * 创建代理
			 *
			 */
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#buildProxy

复制代码
private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) {
			AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);
		}
		/**
		 *
		 * 1. 创建 代理工厂
		 * {@link org.springframework.aop.framework.ProxyFactory}
		 */
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (proxyFactory.isProxyTargetClass()) {
			// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
			if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
				// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
				for (Class<?> ifc : beanClass.getInterfaces()) {
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// No proxyTargetClass flag enforced, let's apply our default checks...
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				// 尝试获取所有接口
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		/**
		 * 所有匹配的 Advisor
		 */
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		/**
		 * 被代理的对象
		 */
		proxyFactory.setTargetSource(targetSource);
		/**
		 * 2.定制化 ProxyFactory (预留扩展的)
		 */
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader smartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = smartClassLoader.getOriginalClassLoader();
		}
		/**
		 * 3.根据 classOnly 决定返回的是: 代理对象的class, 还是 代理对象的实例
		 */
		return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
	}

AOP 的两种织入原理

1.静态AOP (AspectJ)

静态AOP:在编译期,切面直接以字节码的形式编译到目标字节码文件中。AspectJ 属于静态AOP,是在编译时进行增强,会在编译的时候将AOP逻辑织入到代码中,需要专有的编译器和织入器。

优点:被织入的类性能不受影响。缺点:不够灵活

javassist[[Java 字节码指令]]

2.动态AOP
JDK 动态代理

\[10.结构型 - 代理模式 (Proxy Pattern)\]

在正常开发中, 基于 依赖倒置原则(Dependence Inversion Principle), 通常需要接口解决 上层与类的耦合问题;即A类调用B类, 为了解决A依赖与B, 抽取B做成C接口. 变成A依赖以C接口;

在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中。Java从1.3引入动态代理。实现原理是为被代理的业务接口生成代理类,将AOP逻辑写入到代理类中,在运行时动态织入AOP,使用反射执行织入的逻辑。 主要实现方式依赖 java.lang.reflect 包下的InvocationHandler和Proxy类。

优点:Java标准库原生支持,使用简单,无需引用额外的包。相对于静态AOP更灵活。缺点:带代理的类必须是接口,灵活性受到一些限制;使用反射会影响一些性能。

JDK 动态代理必须需要接口, 它的原理是动态的生成class, 类似实现原始类的接口

复制代码
/**
 * JDK 动态代理实现
 */
public static final void JDK_PROXY(){
	/**
	 * 目标类
	 */
	ProductServiceInter carService  = new CarService();
	// 拦截回调处理器
	InvocationHandler handler = new InvocationHandler() {
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			Object result ;
			System.out.println("增强处理 前");
			//调用方法
			result = method.invoke(carService, args);
			System.out.println("增强处理 后");
			return result;
		}
	};
	ProductServiceInter proxyInstance = (ProductServiceInter) Proxy.newProxyInstance(CarService.class.getClassLoader()
			, CarService.class.getInterfaces(), handler);
	proxyInstance.doProduct("白色");

}
CGLIB 动态代理

CGLIB(Code Generation Library)是一个开源项目,是一个强大的, 高性能, 高质量的Code生成类库, 它可以在运行期扩展Java类与实现Java接口, 其实就是cglib可以在运行时动态生成字节码;

使用CGLib实现动态代理, 完全不受代理类必须实现接口的限制, 而且CGLib底层采用ASM字节码生成框架, 使用字节码技术生成代理类, 比使用Java反射效率要高; 唯一需要注意的是, CGLib不能对声明为final的方法进行代理, 因为CGLib原理是动态生成被代理类的子类;

它的原理是: 动态的创建一份class(代理类) 继承/实现被代理的 类/接口

  • net.sf.cglib.proxy.Enhancer -- 主要的增强类

  • net.sf.cglib.proxy.MethodInterceptor -- 主要的方法拦截类它是Callback接口的子接口, 需要用户实现

  • net.sf.cglib.proxy.MethodProxyJDK的java.lang.reflect.Method类的代理类可以方便的实现对源对象方法的调用,如使用: Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象, 也不会出现死循环的问题;

  • net.sf.cglib.proxy.MethodInterceptor 接口是最通用的回调(callback)类型它经常被基于代理的AOP用来实现拦截(intercept)方法的调用; 这个接口只定义了一个方法,public Object intercept(Object object, java.lang.reflect.Method method,Object[] args, MethodProxy proxy) throws Throwable;第一个参数是代理对像, 第二和第三个参数分别是拦截的方法和方法的参数; 原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用, 或者使用 net.sf.cglib.proxy.MethodProxy对象调用;

  • sf.cglib.proxy.MethodProxy通常被首选使用, 因为它更快;

  • 一个动态代理例子

    /**

    • CGLIB 动态代理实现
      /
      public static final void CJLIB_PROXY(){
      Enhancer enhancer = new Enhancer();
      /
      *
      • 设置父类
        */
        enhancer.setSuperclass(CarService.class);
        // 拦截回调处理器
        MethodInterceptor handler = new MethodInterceptor() {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object result ;
        System.out.println("增强处理 前");
        //调用父对象 方法
        result = proxy.invokeSuper(obj, args);
        System.out.println("增强处理 后");
        return result;
        }
        };
        // 设置 拦截回调处理器; 它可以是: * @see MethodInterceptor * @see NoOp * @see LazyLoader * @see Dispatcher * @see org.springframework.cglib.proxy.InvocationHandler * @see FixedValue
        enhancer.setCallback(handler);
        CarService proxyInstance = (CarService) enhancer.create();
        proxyInstance.doProduct("粉色");
        }

三、AOP 代理对象的方法调用过程

源码调试主入口

主入口构成一个责任链的形式org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

复制代码
@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();
		}
		/**
		 * 这个`this.interceptorsAndDynamicMethodMatchers` 数组 就是通知(advised)列表 AspectJXXXAdvice (这里再封装为 XXXAdviceInterceptor)
		 * 第0个是此前插入的 `ExposeInvocationInterceptor` 它哈都没干, 直接 proceed(); 回调回来;
		 * 
		 * 这里累加 ++this.currentInterceptorIndex, 每次回调回来, 拿到下一个 `XXXAdviceInterceptor` 调用
		 */
		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 {
			/**
			 * 将 `this` 传递进去, 各 XXXAdviceInterceptor 将使用这个参数, 回调回来
			 */
			// 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);
		}
	}
Around 通知

org.springframework.aop.aspectj.AspectJAroundAdvice#invoke

复制代码
@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
	if (!(mi instanceof ProxyMethodInvocation pmi)) {
		throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
	}
	/**
	 * 封装一个 ProceedingJoinPoint 参数, 这个就是给 环绕通知方法 接受的参数
	 */
	ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
	JoinPointMatch jpm = getJoinPointMatch(pmi);
	// 环绕通知内部, 调用 `joinPoint.proceed();` 其实还是会回去责任链那里的, 并不是真正的调用原始方法
	return invokeAdviceMethod(pjp, jpm, null, null);
}
After 通知

没啥重点org.springframework.aop.aspectj.AspectJAfterReturningAdvice#afterReturning

复制代码
	@Override
	public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
		if (shouldInvokeOnReturnValueOf(method, returnValue)) {
			invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
		}
	}
Throwing 通知

org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke

复制代码
@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			if (shouldInvokeOnThrowing(ex)) {
				invokeAdviceMethod(getJoinPointMatch(), null, ex);
			}
			throw ex;
		}
	}

文章转载自: ++daidaidaiyu++

原文链接: https://www.cnblogs.com/dddy/p/19634953

体验地址: http://www.jnpfsoft.com/?from=001YH

相关推荐
夏天想17 分钟前
人类将从“执行者“变为“总导演”,学习Ai知识
人工智能·学习
晓梦林1 小时前
Baji1靶场学习笔记
笔记·学习
Java面试题总结1 小时前
java高频面试题(2026最新)
java·开发语言·jvm·数据库·spring·缓存
希冀1231 小时前
【CSS学习第十一篇】
前端·css·学习
苦逼的猿宝1 小时前
学生心理咨询评估系统
java·毕业设计·springboot·计算机毕业设计
隔窗听雨眠1 小时前
doctype、charset、meta如何控制整个渲染流水线
java·服务器·前端
牧羊狼的狼2 小时前
浅谈电商下单微服务流程
spring·spring cloud·微服务
魔法阵维护师2 小时前
从零开发游戏需要学习的c#模块,第十六章(安装 MonoGame 并创建第一个窗口)
学习·游戏·c#·monogame
xian_wwq2 小时前
【学习笔记】大模型备案到底要交什么材料
笔记·学习
西安邮电大学3 小时前
SpringBean完整生命周期
java·spring