springboot自定义注解实战

基础知识概述

aop 面向切面编程

1、切面(aspect)
散落在系统各处的通用的业务逻辑代码,如日志模块,权限模块,事务模块等,切面用来装载pointcut和advice
2、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

  • Before advice:在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。
  • After advice:当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
  • After returnadvice:在某连接点正常完成后执行的通知,不包括抛出异常的情况。
  • Around advice:包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法执行的前后实现逻辑,也可以选择不执行方法
  • Afterthrowing advice:在方法抛出异常退出时执行的通知。

3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut)
拦截的方法,连接点拦截后变成切入点
6、目标对象(Target Object)
代理的目标对象,指要织入的对象模块
7、织入(weave)
通过切入点切入,将切面应用到目标对象并导致代理对象创建的过程
8、AOP代理(AOP Proxy)
AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理

元注解

元注解:修饰注解的注解
@Target:注解的作用目标
@Retention:注解的生命周期
@Documented:注解是否应当被包含在 JavaDoc 文档中
@Inherited:是否允许子类继承该注解

@Target:用于指明被修饰的注解最终可以作用的目标是谁,也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的。语法如下:

@Target(value = {ElementType.METHOD})

ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
ElementType.FIELD:允许作用在属性字段上
ElementType.METHOD:允许作用在方法上
ElementType.PARAMETER:允许作用在方法参数上
ElementType.CONSTRUCTOR:允许作用在构造器上
ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
ElementType.ANNOTATION_TYPE:允许作用在注解上
ElementType.PACKAGE:允许作用在包上

@Retention: 注解指定了被修饰的注解的生命周期。语法如下:
@Retention(value = RetentionPolicy.RUNTIME)

RetentionPolicy.SOURCE:注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视
RetentionPolicy.CLASS:注解只被保留到编译进行的时候,不会被加载到JVM中
RetentionPolicy.RUNTIME:注解可以保留到程序运行的时候,会被加载到JVM中,所以程序运行时可以获取到它


剩下两种类型的注解我们日常用的不多,也比较简单,需要知道他们各自的作用即可:

@Documented 注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。
@Inherited 注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。

实现步骤:


1、定义一个切面类Aspect
声明一个切面类,增加@Component和@Aspect两个注解,同时SpringBoot要引入spring-boot-stater-aop依赖包。
2、定义切点Pointcut
定义切点,并定义切点在哪些地方执行,采用@Pointcut注解完成,如@Pointcut(public * com.xxx.xxx..(…))
规则:修饰符(可以不写)+返回类型+包下的类+方法+方法参数 “”代表不限,“…”两个点代表参数不限,例如:
切点名称myPointcut,在返回类型不限的com.binlog.study.aop.controller包下的所有类,所有方法并且参数不限。
参考:@Pointcut(value="execution(
com.binlog.study.aop.controller..(…))")
3、定义Advice通知
利用通知的5种类型注解@Before、@After、@AfterReturning、@AfterThrowing、@Around来完成在某些切点的增强动作。

springboot 增加 pom

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

如果是初学者,可以看这个简单的例子;
下面第二个例子是进阶版

实战

第一个例子

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {String detail() default "";
}// LogAspect
@Aspect
@Component
public class LogAspect {/*** 此处的切点是注解的方式,也可以用包名的方式达到相同的效果*/@Pointcut("@annotation(com.aop.Log)")public void operationLog(){}// 环绕增强@Around("operationLog()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {Object res = null;try {res =  joinPoint.proceed();return res;} finally {try {System.out.println("方法执行后打印日志");}catch (Exception e){System.out.println("LogAspect 操作失败:" + e.getMessage());e.printStackTrace();}}}/*** 处理完请求,返回内容*/@AfterReturning(returning = "ret", pointcut = "operationLog()")public void doAfterReturning(Object ret) {System.out.println("方法的返回值 : " + ret);}/*** 后置异常通知*/@AfterThrowing("operationLog()")public void throwss(JoinPoint jp){System.out.println("方法异常时执行.....");}/*** 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行*/@After("operationLog()")public void after(JoinPoint jp){System.out.println("方法最后执行.....");}}// controller 
@RestController
@RequestMapping("user")
public class Controller {@Autowiredprivate UserService  userService;@GetMapping("/findUserNameByTel")public String findUserNameByTel(@RequestParam("tel") String tel){return userService.findUserName(tel);}
}// UserService 
@Service
public class UserService {@Log(detail = "通过手机号[{{tel}}]获取用户名")public String findUserName(String tel) {System.out.println("tel:" + tel);return "zhangsan";}
}

第二个例子

自定义注解打印日志和抛出异常信息

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestAnnotate {String value() default "";
}

切面类

@Aspect
@Slf4j
@Component
public class RequestAspet {// PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名// 切面最主要的就是切点,所有的故事都围绕切点发生// logPointCut()代表切点名称@Pointcut(value = "@annotation(RequestAnnotate)")private void requestLog() {}//定义切点@Pointcut(value="execution(* com.example.demo.controller.*.*(..))")public void operErrorPointCut() { }/*** 执行切点之前* @param joinPoint*/@Before(value = "requestLog()")private void doBefore(JoinPoint joinPoint) throws ClassNotFoundException {log.info("执行前置");try {// 获取被代理对象的类名String targetName = joinPoint.getTarget().getClass().getName();// 获取 Signature 对象,包含目标方法名和所属类的 class 信息String methodName = joinPoint.getSignature().getName();// 获取方法参数Object[] arguments = joinPoint.getArgs();// 根据类名获取类Class<?> targetClazz = Class.forName(targetName);Method[] methods = targetClazz.getMethods();String operation = "";//遍历方法名和参数长度一致的方法for (Method m :methods){// 对比方法名一致if (methodName.equals(m.getName())){Class<?>[] clazz = m.getParameterTypes();// 参数长度一致if (clazz.length == arguments.length){// 获取的是方法上注解后面的 value 值,可以作为注释传入operation = m.getAnnotation(RequestAnnotate.class).value();break;}}}StringBuilder sb = new StringBuilder();for (Object arg : arguments) {sb.append(arg);sb.append("&");}// *========控制台输出=========*//log.info("[X用户]执行了[" + operation + "],类:" + targetName + ",方法名:" + methodName + ",参数:" + sb.toString());} catch (Throwable e){log.info("around " + joinPoint + " with exception : " + e.getMessage());}}/*** 执行切点之后* @param joinPoint*/@After(value = "requestLog()")private void doAfter(JoinPoint joinPoint) {log.info("执行后置");try {String targetName = joinPoint.getTarget().getClass().getName();String targetMethod = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();Class<?> targetClazz = Class.forName(targetName);Method[] methods = targetClazz.getMethods();String operation = "";for (Method m :methods){if (targetMethod.equals(m.getName())){if (m.getParameterTypes().length == args.length){operation= m.getAnnotation(RequestAnnotate.class).value();}}}StringBuilder sb = new StringBuilder();for (Object arg:args){sb.append(arg);sb.append("&");}log.info("用户执行了 operation " + operation + "类:" + targetName + "方法名:" + targetMethod + "参数:" +sb.toString() );}catch (Throwable e){log.info("around " + joinPoint + " with exception : " + e.getMessage());}}
}

controller

    @RequestMapping("test")@RequestAnnotate(value = "测试")public void test() {System.out.println("执行测试");}

测试结果:
image.png

异常接口:

    @RequestMapping("test")@RequestAnnotate(value = "测试")public void test() {System.out.println("执行测试");int[] array = {1,2};System.out.println(array[2]);}

image.png

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

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

相关文章

AI时代 编程高手的秘密武器:世界顶级大学推荐的计算机教材

文章目录 01 《深入理解计算机系统》02 《算法导论》03 《计算机程序的构造和解释》04 《数据库系统概念》05 《计算机组成与设计&#xff1a;硬件/软件接口》06 《离散数学及其应用》07 《组合数学》08《斯坦福算法博弈论二十讲》 清华、北大、MIT、CMU、斯坦福的学霸们在新学…

【Leetcode】235. 二叉搜索树的最近公共祖先

文章目录 题目思路代码结果 题目 题目链接 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度…

深度学习手写字符识别:推理过程

说明 本篇博客主要是跟着B站中国计量大学杨老师的视频实战深度学习手写字符识别。 第一个深度学习实例手写字符识别 深度学习环境配置 可以参考下篇博客&#xff0c;网上也有很多教程&#xff0c;很容易搭建好深度学习的环境。 Windows11搭建GPU版本PyTorch环境详细过程 数…

抖音视频下载软件的优势|视频批量下载

抖音视频下载软件具有以下优势&#xff1a; 多种搜索方式&#xff1a;用户可以通过输入关键词进行批量视频提取&#xff0c;也可以通过分享链接进行单个视频的提取和下载。这样用户可以根据自己的需求选择最合适的搜索方式。 直观的界面设计&#xff1a;软件的界面分为五大块&…

分布式知识整理

分布式锁 以商场系统超卖现象举例 超卖现象一 现象&#xff1a; 商品卖出数量超出了库存数量。 产生原因&#xff1a; 扣减库存的动作在程序中进行&#xff0c;在程序中计算剩余库存&#xff0c;在并发场景下&#xff0c;导致库存计算错误。 代码复现 es.shutdown(); cycl…

ardupilot如何从四元数转旋转矢量

目录 文章目录 目录具体转换过程 本节主要记录四元数如何转成旋转矢量的推导过程&#xff0c;欢迎批评指正&#xff01; 四元数可以方便地转换为旋转矢量&#xff0c;旋转矢量是一个三维向量&#xff0c;描述了旋转的方向和大小。在将四元数转换为旋转矢量时&#xff0c;我们首…

Linux之项目部署与发布

目录 一、Nginx配置安装&#xff08;自启动&#xff09; 1.一键安装4个依赖 2. 下载并解压安装包 3. 安装Nginx 4. 启动 nginx 服务 5. 对外开放端口 6. 配置开机自启动 7.修改/etc/rc.d/rc.local的权限 二、后端部署tomcat负载均衡 1. 准备2个tomcat 2. 修改端口 3…

Elasticsearch:了解人工智能搜索算法

作者&#xff1a;来自 Elastic Jessica Taylor, Aditya Tripathi 人工智能工具无处不在&#xff0c;其原因并不神秘。 他们可以执行各种各样的任务并找到许多日常问题的解决方案。 但这些应用程序的好坏取决于它们的人工智能搜索算法。 简单来说&#xff0c;人工智能搜索算法是…

打印水仙花数---c语言刷题

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 题述 求出0&#xff5e;100000之间的所有“水仙花数”并输出。 “水仙花数”是指一个n位数&#xff0c;其各位数字的n次方之和确好等于该数本身&#xff0c;如:153&#…

分布式架构(分布式ID+分布式事务)

分布式架构 分布式事务产生的场景&#xff1a; 跨JVM进程产生的分布式事务 单体系统访问多个数据库实例 多服务访问同一个数据库实例 CAP理论 C&#xff1a;一致性&#xff0c;指写操作后的读操作可以读取到最新的数据状态&#xff0c;当数据分布在多个节点上&#xff0…

第三节:Vben Admin登录对接后端login接口

系列文章目录 第一节&#xff1a;Vben Admin介绍和初次运行 第二节&#xff1a;Vben Admin 登录逻辑梳理和对接后端准备 文章目录 系列文章目录前言一、Flask项目介绍二、使用步骤1.User模型创建2.迁移模型3. Token创建4. 编写蓝图5. 注册蓝图 三. 测试登录总结 前言 上一节&…

python自动化管理和zabbix监控网络设备(有线网络配置部分)

目录 一、拓扑图 二、core-sw1 三、core-sw2 四、sum-sw1 五、sum-sw2 一、拓扑图 二、core-sw1 sys sysname core-sw1 vlan batch 10 20 30 40 50 60 100 vlan batch 200 210 220 230 240 250 stp region-configuration region-name huawei revision-level 1 instance…