springboot 自定义注解 ,aop切面@Around; 为接口实现日志插入【强行喂饭版】

前言:有个开发共享api的需求,需要把调用的api插入到接口调用日志里面。
但代码重复工作量大的时候,现在总不能还跟之前一样那里需要写哪里吧。
所以想到了用自定义注解 ,aop切面的方式拦截处理。

思路:aop切面使用@Around来控制目标方法的执行。
既然是日志表的话,那么需要 获取方法(接口)的参数方法名方法执行结果方法执行时间调用者的ip

另一篇:springboot 自定义注解 ,实现接口限流(计数器限流)【强行喂饭版】

好了,直接上代码:
一:创建注解

import java.lang.annotation.*;// 注解的作用目标为方法
@Target(ElementType.METHOD) // 注解在运行时保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用
@Retention(RetentionPolicy.RUNTIME) // 指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值
@Documented 
public @interface ShareApiLog {// 定义注解的属性,需要什么属性自己增加public String key() default "";
}


二、实现aop切面拦截


import 你上面注解的路径.ShareApiLog;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.text.DecimalFormat;
import java.util.HashMap;// 声明这是一个切面类
@Aspect// 表明该类是一个组件,将该类交给spring管理。
@Component// 指定执行顺序,值越小,越先执行。由于这里会涉及到接口限流的注解,
//     所以这里要指定执行顺序 比接口限流 慢执行
@Order(2)public class ShareApiLogAspect {// 这个注解作用及普及 见文章后面解析@Around("@annotation(shareApiLog)")public Object afterShareApiLogMethod(ProceedingJoinPoint joinPoint, ShareApiLog shareApiLog) throws Throwable {System.out.println("执行了带有@ShareApiLog注解的方法:" + joinPoint.getSignature().getName());//获取方法的参数,取决于自己业务需不需要Object[] args = joinPoint.getArgs();String appId = null;if (args != null && args.length >= 1) {//获取appidappId = (String) args[0];}// 获取接口key,取决于自己业务需不需要String apiKey = shareApiLog.key();// 获取调用方法的IP地址String ipAddress = getClientIp();//根据IP地址,获取调用方的国家-省份-城市String ipLocation = getIpLocation(ipAddress);// 记录方法开始时间long startTime = System.nanoTime();// 执行目标方法HashMap<String, Object> result = (HashMap<String, Object>) joinPoint.proceed();// 记录方法结束时间long endTime = System.nanoTime();// 计算方法运行时间,单位为纳秒double duration = (endTime - startTime) / 1_000_000_000.0;DecimalFormat decimalFormat = new DecimalFormat("0.00");String formattedDuration = decimalFormat.format(duration) + "秒";System.out.println("---执行时间--==");System.out.println(formattedDuration);// 进行你的日志表插入代码与逻辑代码// 一般建议根据返回code来判断执行结果成功与否return result;}/*** 获取调用方真实ip [本机调用则得到127.0.0.1]* 首先尝试从X-Forwarded-For请求头获取IP地址,如果没有找到或者为unknown,则尝试从X-Real-IP请求头获取IP地址,* 最后再使用request.getRemoteAddr()方法作为备用方案。注意,在多个代理服务器的情况下,* X-Forwarded-For请求头可能包含多个IP地址,我们取第一个IP地址作为真实客户端的IP地址。*/public String getClientIp() {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();String ipAddress = request.getHeader("X-Forwarded-For");if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("X-Real-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();}// 多个代理服务器时,取第一个IP地址int index = ipAddress.indexOf(",");if (index != -1) {ipAddress = ipAddress.substring(0, index);}return ipAddress;}/*** 根据给定的IP地址查询该IP地址的地理位置信息(国家、省份、城市)。* 使用了ip-api.com提供的免费IP地址查询接口。 * @param ipAddress ip地址* @return 地理位置信息(国家、省份、城市)【内网ip查询不到,即返回 Unknown】*/public  String getIpLocation(String ipAddress) {try {URL url = new URL("http://ip-api.com/json/" + ipAddress);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));StringBuilder response = new StringBuilder();String line;while ((line = reader.readLine()) != null) {response.append(line);}reader.close();connection.disconnect();ObjectMapper objectMapper = new ObjectMapper();JsonNode responseNode = objectMapper.readTree(response.toString());String country = responseNode.get("country").asText();String regionName = responseNode.get("regionName").asText();String city = responseNode.get("city").asText();// 拼接成字符串String locationString = country + "-" + regionName + "-" + city ;return locationString;} catch (Exception e) {e.printStackTrace();}return "Unknown";}}

三、哪里需要点哪里

@PostMapping("/接口api")@ShareApiLog(key = "根据自己业务选择是否需要key")public AjaxResult selectToUserId(参数){}



解析:

@Around("@annotation(shareApiLog)")- 使用了@Around注解来表示它是一个环绕通知。在Spring AOP中,环绕通知是最强大的通知类型,
它可以在目标方法执行之前和之后执行自定义的逻辑。- @annotation(shareApiLog) 中的shareApiLog是指参数名称,而不是注解名称。- @annotation(shareApiLog) 中的shareApiLog参数类型为ShareApiLog,
表示你将拦截被@ShareApiLog注解标记的方法,并且可以通过这个参数来获取@ShareApiLog注解的信息。
如果你想拦截其他注解,只需将第二个参数的类型修改为对应的注解类型即可。注意: 环绕通知必须显式调用 joinPoint.proceed() 来继续执行目标方法,
否则目标方法将被阻塞,不会得到执行。

普及:

JoinPoint是Spring AOP中的一个接口,它代表了在程序执行过程中能够被拦截的连接点(Join Point)。
连接点指的是在应用程序中,特定的代码块,比如方法的调用、方法的执行、构造器的调用等。JoinPoint在AOP中的作用是用于传递方法调用的信息,比如方法名、参数、所属的类等等。
当AOP拦截到一个连接点时,就可以通过JoinPoint对象来获取这些信息,并根据需要进行相应的处理。在AOP中,常见的通知类型(advice)如下:@Before:在目标方法执行之前执行。
@After:在目标方法执行之后(无论是否抛出异常)执行。
@AfterReturning:在目标方法成功执行之后执行。
@AfterThrowing:在目标方法抛出异常后执行。
@Around:在目标方法执行前后都执行,可以控制目标方法的执行。在以上各种通知中,可以使用JoinPoint参数来获取连接点的相关信息。
例如,在@Around通知中,可以使用JoinPoint对象来获取目标方法的信息,
比如方法名、参数等。这样,我们就可以根据这些信息来实现我们需要的切面逻辑。eg:
// 获取方法名
String methodName = joinPoint.getSignature().getName();//获取方法参数
Object[] args = joinPoint.getArgs();// 获取所属类名
String className = joinPoint.getSignature().getDeclaringTypeName();// 获取源代码位置信息
SourceLocation sourceLocation = joinPoint.getSourceLocation();

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

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

相关文章

怎么在线制作证件?教你一键生成证件照

无论是申请身份证、护照、驾照还是学生证&#xff0c;都需要一张清晰、规范的证件照。但是&#xff0c;为了拍摄一张完美的证件照&#xff0c;需要付出不少时间和精力。而现在&#xff0c;我们可以使用压缩图网站提供的证件照制作工具&#xff0c;轻松制作出一张清晰、规范的证…

神经网络的初始化方法

文章目录 1、随机初始化2、Xavier初始化3、He初始化4、权重预训练初始化5、零初始化 对于神经网络的训练过程中&#xff0c;合适的参数初始化方法有助于更好的处理梯度消失和梯度爆炸问题。通常有以下几种初始化方法&#xff1a; 1、随机初始化 随机初始化&#xff08;Random…

飞行动力学-第15节-part2-松杆中性点 之 基础点摘要

飞行动力学-第15节-part2-松杆中性点 之 基础点摘要 1. 松杆中性点2. 松浮角2. 杆力梯度3. 参考资料 1. 松杆中性点 stick fixed&#xff1a; N 0 N_0 N0​&#xff0c;握杆&#xff0c;升降舵固定stick free&#xff1a; N 0 ′ N_0 N0′​&#xff0c;松杆&#xff0c;升降舵…

java static修饰的静态成员

静态成员 特点&#xff1a; 1.静态成员可以被本类所有对象共享2.静态成员可以通过类名调用也可以推荐对象调用&#xff0c;但是推荐使用类名调用&#xff01;3.静态成员随着类的加载而加载&#xff0c;优先于对象存在的静态方法的注意事项&#xff1a; 1.非静态方法可以访问任…

APP开发入门:了解主流的编程语言

在过去的几年里&#xff0c;有许多程序员开始学习和使用编程语言。这其中包括C、C、 Java和 Python。尽管有许多语言可供选择&#xff0c;但大多数程序员都会选择最容易学习的编程语言。 如今&#xff0c;有很多编程语言供选择。程序员们在学习这些语言时可以自由地选择他们喜…

【CSS】outline 属性

outline属性 outline 是 CSS 属性&#xff0c;用于设置元素的外边框样式&#xff0c;包括颜色、样式和宽度。外边框是一个在元素周围的轮廓&#xff0c;类似于边框&#xff08;border&#xff09;&#xff0c;但不会占用空间&#xff0c;并且不会影响布局。 outline 属性可以…

Docker 镜像构建 搭建分布式LNMP论坛 实践

地址规划 nginx 172.18.0.10 mysql 172.18.0.20 php 172.18.0.30 宿主机准备 拉取镜像&#xff0c;下面以此镜像为基础 docker pull centos:7 创建自定义网段以便指定 IP 不变动 docker network create --subnet172.18.0.0/16 --opt "com.docker.network.bridge.na…

Spark Streaming流媒体引擎

Spark Streaming是Spark的上一代流媒体引擎。Spark Streaming不再有更新&#xff0c;它是一个遗留项目。Spark中有一个更新且更易于使用的流媒体引擎&#xff0c;称为结构化流媒体 概述 Spark Streaming是核心Spark API的扩展&#xff0c;支持实时数据流的可扩展、高吞吐量、…

条款38:对变化多端的线程句柄析构函数行为保持关注

条款37解释过&#xff0c;可联结的线程对应着一个底层系统执行线程&#xff0c;未推迟任务&#xff08;参见条款36&#xff09;的期值和系统线程有类似关系。这么一来&#xff0c;std::thread型别对象和期值对象都可以视作系统线程的句柄。 从这个视角来看&#xff0c;std::th…

Opencv的Mat内容学习

来源&#xff1a;Opencv的Mat内容小记 - 知乎 (zhihu.com) 1.Mat是一种图像容器&#xff0c;是二维向量。 灰度图的Mat一般存放<uchar>类型 RGB彩色图像一般存放<Vec3b>类型。 (1)单通道灰度图数据存放样式&#xff1a; (2)RGB三通道彩色图存放形式不同&#x…

Flutter 添加 example流程

一、已有Flutter工程&#xff08;命令&#xff09;添加 example 1、cd 工程(flutter_plugin ,是自己创建的)根目录 例: flutter create example 执行命令创建example PS&#xff1a;cd example 后执行flutter doctor 后就可以看到效果 2、如果需要指定iOS/Android 语言,请添加…

动态数组 Vector(难度1)(答案)

答案如下: #include<iostream> #include <iomanip> #include <cassert> using namespace std;//------下面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------ #include <algorithm> #include <cstdlib> #include <iostream&…

9SX UPS串口SHUT通讯协议

9SX UPS通讯协议 SHUT通讯协议串口定义同步SHUT数据帧UPS CommandsGET REPORT command SHUT通讯实例 在改造中碰到了某公司的9SX UPS串口通讯。原来的控制器采用了C语言编程。因为不想费事&#xff0c;向原厂家询问通讯协议。前台的小姐很骄傲的说他们不提供通讯协议了&#xf…

如何建立Docker私有仓库?

文章目录 docker私有仓库harborHarbor仓库部署Harbor仓库使用 docker私有仓库 Docker 私有仓库是一个用于存储和管理 Docker 镜像的私有存储库。它允许你在内部网络中创建和管理 Docker 镜像&#xff0c;并提供了更好的安全性和控制&#xff0c;因为你可以完全控制谁能够访问和…

MFC 透明窗体

如何制作透明窗体 &#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f; 使用SetLayeredWindowAttributes可以方便的制作透明窗体&#xff0c;此函数在w2k以上才支持,而且如果希望直接使用的话&#xff0c;可能需要下载最新的SDK。不过此函数在w2k的user32.dll里有实…

ansible自动化运维(一)

&#x1f618;作者简介&#xff1a;正在努力的99年公司职员。 &#x1f44a;宣言&#xff1a;人生就是B&#xff08;birth&#xff09;和D&#xff08;death&#xff09;之间的C&#xff08;choise&#xff09;&#xff0c;做好每一个选择。 &#x1f64f;创作不易&#xff0c;…

机器学习 day31(baseline、学习曲线)

语音识别的Jtrain、Jcv和人工误差 对于逻辑回归问题&#xff0c;Jtrain和Jcv可以用分类错误的比例&#xff0c;这一方式来代替单单只看Jtrain&#xff0c;不好区分是否高偏差。可以再计算人类识别误差&#xff0c;即人工误差&#xff0c;作为基准线来进行比较Jtrain与baselin…

JAVA Date 类型的对象,只想保留 “yyyy-MM-dd“ 格式的部分

Date 类型的对象&#xff0c;如果你只想保留 "yyyy-MM-dd" 格式的部分 1.创建一个 SimpleDateFormat 对象&#xff0c;并设置日期格式为 "yyyy-MM-dd"。 import java.text.SimpleDateFormat; import java.util.Date;Date date new Date(); SimpleDateFo…

回归预测 | MATLAB实现TCN-BiLSTM时间卷积双向长短期记忆神经网络多输入单输出回归预测

回归预测 | MATLAB实现TCN-BiLSTM时间卷积双向长短期记忆神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现TCN-BiLSTM时间卷积双向长短期记忆神经网络多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现TCN-BiLSTM时间卷积…

Thymeleaf入门

Thymeleaf是前端开发模板&#xff0c;springboot默认支持。前端模板用法大多数是类似的jsp、thymeleaf、vue.js都有while\for\if\switch等使用&#xff0c;页面组件化等。 1.前端模板区别 jsp是前后端完全不分离的&#xff0c;jsp页面写一堆Java逻辑。 thymeleaf好处是html改…