spring aop 初探

news2024/11/26 5:57:14

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

分析JDK动态代理

生成的代理对象  构造函数,入参为 InvocationHandler

public com.sun.proxy.$Proxy164(java.lang.reflect.InvocationHandler)

生成动态代理Class对象:

Class<?> cl = getProxyClass0(loader, intfs);

实例化动态代理对象:

final Constructor<?> cons = cl.getConstructor({ InvocationHandler.class });

cons.newInstance(new Object[]{h});

方法

equals,toString,hashCode 

0 = {Method@10598} "public final boolean com.sun.proxy.$Proxy164.equals(java.lang.Object)"
1 = {Method@10599} "public final java.lang.String com.sun.proxy.$Proxy164.toString()"
2 = {Method@10600} "public final int com.sun.proxy.$Proxy164.hashCode()"

和 所有代理接口中的 所有方法。

代理对象的方法实现逻辑:比如 equals方法

return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();

就是调用 

java.lang.reflect.InvocationHandler#invoke

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

}

 第一个参数,代理对象本身

第二个参数,调用的代理的接口中的方法

第三个参数,调用的方法 入参

JDK动态代理 需要开发者关注的,就是 实现 InvocationHandler 接口。实际 也并不需要很复杂的实现,可以引用另一个服务来实现。

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator

在spring 实例化 bean之后,会执行

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

完成aop 代理对象的生成。

JDK动态代理的 InvocationHandler 的实现类为 JdkDynamicAopProxy,其增强功能由 advised 完成。和上面画的结构 基本一致。

Advisor

寻找Advisor

// org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

第一部分,直接查找所有的 Advisor类型的bean

this.beanFactory.getBean(name, Advisor.class)

第二部分,查询标有 @Aspect 注解的 bean

private boolean hasAspectAnnotation(Class<?> clazz) {
		return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
	}

 针对每个bean,找到所有 没有标记 @Pointcut 注解的方法

对每个方法查找对应的切面,标记 标记 @Around等注解的方法

private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
			Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

protected enum AspectJAnnotationType {

		AtPointcut, AtAround, AtBefore, AtAfter, AtAfterReturning, AtAfterThrowing
}

获取 注解的value值,通常返回的是 标记有 @Pointcut 注解的 方法名

Method method = annotation.annotationType().getDeclaredMethod(attributeName);
ReflectionUtils.makeAccessible(method);
return method.invoke(annotation);

开始包装该切面方法:

关键代码块

switch (aspectJAnnotation.getAnnotationType()) {
			case AtPointcut:
				if (logger.isDebugEnabled()) {
					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
				}
				return null;
			case AtAround:
				springAdvice = new AspectJAroundAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtBefore:
				springAdvice = new AspectJMethodBeforeAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfter:
				springAdvice = new AspectJAfterAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfterReturning:
				springAdvice = new AspectJAfterReturningAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterReturningAnnotation.returning())) {
					springAdvice.setReturningName(afterReturningAnnotation.returning());
				}
				break;
			case AtAfterThrowing:
				springAdvice = new AspectJAfterThrowingAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
					springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
				}
				break;
			default:
				throw new UnsupportedOperationException(
						"Unsupported advice type on method: " + candidateAdviceMethod);
		}

根据切面的类型,返回不同的Advice类型的对象。

寻找可以应用在当前bean的 advisor。这个逻辑有点复杂,先跳过

return canApply(pca.getPointcut(), targetClass, hasIntroductions);

 

接下来就是创建代理对象

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);
		}
	}

这边代码看起来绕来绕去,实际设计的比较精炼。使用一个工厂类的一个方法就返回了代理对象。

proxyFactory.getProxy(getProxyClassLoader());

createAopProxy().getProxy(classLoader)

getAopProxyFactory().createAopProxy(this)

多个方法级联调用,只是这里进行了 一个拆分。

public JdkDynamicAopProxy(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;
	}

 

上面描述了,JdkDynamicAopProxy就是 InvocationHandler的实现类。

其invoke方法内部 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 完了对切面的执行调用。

执行过程:

 

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

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

相关文章

免交互和嵌入执行模式

目录 概念 语法格式 统计行数 赋值变量 修改密码​编辑往文件里添加内容 ​编辑​编辑引入变量 整体赋值​编辑 加引号不赋值变量 expect实现免交互 免交互设置密码 免交互切换用户 嵌入执行模式 添加用户并免交互设置密码 免交互登录 传参实现ssh 练习 概念 …

day22--77. 组合+216.组合总和III+17.电话号码的字母组合

一、77. 组合 题目链接&#xff1a;https://leetcode.cn/problems/combinations/ 文章讲解&#xff1a;https://programmercarl.com/0077.%E7%BB%84%E5%90%88.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1ti4y1L7cv 1.1 初见思路 组合问题用回溯学会使用剪…

Java的异常处理体系

目录 异常处理1、Java的异常类层次结构2、try-catch-finally 使用注意事项3、在Web应用中如何实现全局异常处理机制 异常处理 1、Java的异常类层次结构 其中Error表示程序运行错误 常见的错误类型有&#xff1a; OutOfMemoryError (内存溢出错误) StackOverFlowError (栈内存溢…

计算机等级考试二级Java-第二篇:基本数据类型

1.运算符的优先级以及复杂表达式 优先级运算符结合性1( ) [ ]  .从左到右2!  ~    –从右到左3*  /  %从左到右4  -从左到右5<<  >>  >>>从左到右6<  <  >  >  instanceof从左到右7  !从左到右8&从左到右9^从左到右10|从…

定制聊天机器人,实现智能客服,完成冰箱报修对话 | Chatopera

系列文章 1/3 智能客服体验分析&#xff0c;使用小程序海尔服务完成电器报修 | Chatopera2/3 Chatopera 云服务实现类海尔服务智能客服的功能点比较 | Chatopera3/3 定制聊天机器人&#xff0c;实现智能客服&#xff0c;完成冰箱报修对话 | Chatopera 本篇为第三篇&#xff0…

突破SaaS产品运营困境:多渠道运营如何集中管理?

随着数字化时代的到来&#xff0c;SaaS&#xff08;软件即服务&#xff09;产品已成为企业日常运营不可或缺的工具。然而&#xff0c;在竞争激烈的市场环境下&#xff0c;SaaS产品运营越来越重视多渠道、多平台布局&#xff0c;以更广泛地触及潜在用户&#xff0c;然而&#xf…

报工计件工资核算h5开源版开发

报工计件工资核算h5开源版开发 小型计件工资管理系统&#xff0c;支持后台制定工价&#xff0c;核算工资。支持员工H5端报工&#xff0c;和查看工资情况。 H5手机端 支持在线报工&#xff0c;支持查看我的工资。 自定义费用项 在基础计件工资基础上增加扣除和增加项&#xff…

日元一路暴跌,对日股是利好还是利空? “年中高息”效应不再,货基与回购收益率走低

日元汇率自5月突破155后&#xff0c;股市已开始认识到日元疲软的负面影响&#xff0c;日元贬值与股价上涨的相关性已被打破&#xff0c;股市投资者现在需要关注日元贬值的水平。 6月28日周五&#xff0c;美国重磅PCE物价指数公布前夕&#xff0c;日元再度深跌至1美元兑161日元&…

武汉星起航:亚马逊卖家必读——FBA与FBM发货物流模式全面对比

亚马逊作为全球电商的巨头&#xff0c;其成功的背后离不开高效的物流服务。在亚马逊平台上&#xff0c;卖家可以选择两种主要的发货物流模式&#xff1a;FBA&#xff08;Fulfillment by Amazon&#xff09;和FBM&#xff08;Fulfillment by Merchant&#xff09;。这两种模式各…

预制聚氨酯保温管:高效节能管道保温

在现代能源输送领域&#xff0c;预制聚氨酯保温管正凭借其出色的性能&#xff0c;成为保障能源高效传输的关键角色。 预制聚氨酯保温管&#xff0c;顾名思义&#xff0c;其核心在于聚氨酯保温层。这一独特的设计赋予了它卓越的保温性能。聚氨酯材料具有极低的导热系数&#xff…

扫扫地,搞搞卫生 ≠ 车间5S管理

在制造业的日常运营中&#xff0c;车间管理是一项至关重要的工作&#xff0c;它直接关系到生产效率、产品质量以及员工的工作环境。然而&#xff0c;许多人常常将简单的“扫扫地&#xff0c;搞搞卫生”等同于车间5S管理&#xff0c;这种误解不仅可能导致管理效果不佳&#xff0…

飞凌嵌入式RK3576核心板的四大优势详解

为了充分满足AIoT市场对高性能、高算力和低功耗主控日益增长的需求&#xff0c;飞凌嵌入式近期推出了基于Rockchip RK3576处理器开发设计的FET3576-C核心板&#xff0c;具有出色的图像和视频处理能力、丰富的接口和扩展能力、低功耗以及广泛的应用场景。本篇内容&#xff0c;小…

Ubuntu20.04安装Prometheus监控系统

环境准备&#xff1a; 服务器名称内网IP公网IPPrometheus服务器192.168.0.23047.119.21.167Grafana服务器192.168.0.23147.119.22.8被监控服务器192.168.0.23247.119.22.82 更改主机名方便辨认 hostnamectl set-hostname prometheus hostnamectl set-hostname grafana hostn…

「性能指标」CPU飙高排查实战

上篇提到了什么是 CPU 以及如何计算的。接下来主要说说实际工作中遇到 CPU 飙高如何去排查呢&#xff1f; 往期回顾&#xff1a; 聊聊性能指标 CPU 利用率如何计算的&#xff1f; 问题发现 前段时间我们新上线了一个应用&#xff0c;由于流量一直不大&#xff0c;集群的每秒…

Python25 Numpy基础

1.什么是Numpy NumPy&#xff08;Numerical Python 的简称&#xff09;是 Python 语言的一个扩展程序库&#xff0c;支持大量的维度数组与矩阵运算&#xff0c;此外也针对数组运算提供大量的数学函数库。NumPy 的前身是 Numeric&#xff0c;这是一个由 Jim Hugunin 等人开发的…

Flexsim物流仿真入门-利用网络节点控制行走路线

在布局优化过程中&#xff0c;往往叉车&#xff0c;操作人员的搬运&#xff0c;是会按照一定的行走路线进行的&#xff0c;这就需要我们进行节点的设计&#xff0c;以此来实现特定路径的行走。 在构建过程中&#xff0c;这里将会以案例的形式进行讲解说明。 1&#xff09;构建…

基于单片机的智能温控风扇设计

摘 要 : 本次设计是基于单片机的智能温控风扇 。 以 STC89C52 单片机为核心 &#xff0c; 可以实现对风扇的有效控制 。 可以根据需要设置不同的温度 &#xff0c;如果温度在设定值最大值和最小值之间时则启动风扇弱风档&#xff0c; 如果温度超过设定的数值时将会变到大风档…

mfc140.dll怎么安装?mfc140.dll丢失安装详细解决方法

当电脑出现找不到mfc140.dll丢失问题&#xff0c;我们需要怎么办&#xff1f;怎么解决mfc140.dll丢失问题&#xff1f;mfc140.dll到底是什么&#xff1f;下面我给大家详细介绍与分析&#xff0c;最重要的是mfc140.dll的解决方法&#xff01; 一、文件丢失原因分析 在分析mfc14…

C++笔记:实现一个字符串类(构造函数、拷贝构造函数、拷贝赋值函数)

实现一个字符串类String&#xff0c;为其提供可接受C风格字符串的构造函数、析构函数、拷贝构造函数和拷贝赋值函数。 声明依赖文件 其中ostream库用于打印标准输入输出&#xff0c;cstring库为C风格的字符串库 #include <iostream> #include <cstring> 声明命…

印度股市连续三天创新高,国际知名评级机构穆迪发出警告

KlipC报道&#xff1a;6月27日&#xff0c;印度股市连续三个交易日创新高&#xff0c;截至当日收盘&#xff0c;印度SENSEX30指数涨0.72%&#xff0c;报79243.18点。印度SENSEX30指数今年以来涨幅接近10%。但国际知名评级机构穆迪发出警告 据悉&#xff0c;到6月25日&#xff…