diy-aop

news/2024/9/19 19:46:10/文章来源:https://www.cnblogs.com/yuqiu2004/p/18421216

不得了,看到自定义aop,这下终于知道它有啥作用了,这玩意确实好用

做sky-takeout的时候,没有几集就涉及到公共字段的自动赋值,例如createUser、createTime之类的。很容易可以想到,利用类似‘拦截器’的东西就可以实现,但是具体怎么做呢?所以先介绍下通用的流程步骤:

1.声明自定义注解

1.1 元注解 - 注解的注解

@Target 注解可声明位置

  • ElementType.PACKAGE:该注解只能声明在一个包名前。

  • ElementType.ANNOTATION_TYPE:该注解只能声明在一个注解类型前。

  • ElementType.TYPE:该注解只能声明在一个类前。

  • ElementType.CONSTRUCTOR:该注解只能声明在一个类的构造方法前。

  • ElementType.LOCAL_VARIABLE:该注解只能声明在一个局部变量前。

  • ElementType.METHOD:该注解只能声明在一个类的方法前。

  • ElementType.PARAMETER:该注解只能声明在一个方法参数前。

  • ElementType.FIELD:该注解只能声明在一个类的字段前。

**@Retention **保留/注解声明周期

  • RetentionPolicy.SOURCE : 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;

  • RetentionPolicy.CLASS : 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;

  • RetentionPolicy.RUNTIME : 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

对于@Target我没有意见,但是对于@Retention我不明白,Source和Class级别的声明周期有什么用呢?

看到一个回答:‘源代码级别的注解有两个作用,一是作为文档的补充,给人看的,比如@Override,二是作为源代码生成器的材料,比如ButterKnife框架(不懂)’

懂了一些了,所以目前对于业务级别的开发,应该一般就是Runtime界别的

1.2 示例:

/*** 自定义注解,用于标识某个方法需要进行公共字段自动填充处理*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) 
public @interface AutoFill {//数据库操作类型:UPDATE INSERTOperationType value();
}

2.声明自定义切面

2.1 注解介绍

@Aspect:作用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出异常或者正常退出都会执行

2.2 切入点注解语法

    /*** 1、使用within表达式匹配* 下面示例表示匹配com.leo.controller包下所有的类的方法*/@Pointcut("within(com.leo.controller..*)")public void pointcutWithin(){}/*** 2、this匹配目标指定的方法,此处就是HelloController的方法*/@Pointcut("this(com.leo.controller.HelloController)")public void pointcutThis(){}/*** 3、target匹配实现UserInfoService接口的目标对象*/@Pointcut("target(com.leo.service.UserInfoService)")public void pointcutTarge(){}/*** 4、bean匹配所有以Service结尾的bean里面的方法,* 注意:使用自动注入的时候默认实现类首字母小写为bean的id*/@Pointcut("bean(*ServiceImpl)")public void pointcutBean(){}/*** 5、args匹配第一个入参是String类型的方法*/@Pointcut("args(String, ..)")public void pointcutArgs(){}/*** 6、@annotation匹配是@Controller类型的方法*/@Pointcut("@annotation(org.springframework.stereotype.Controller)")public void pointcutAnnocation(){}/*** 7、@within匹配@Controller注解下的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.CLASS)*/@Pointcut("@within(org.springframework.stereotype.Controller)")public void pointcutWithinAnno(){}/*** 8、@target匹配的是@Controller的类下面的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.RUNTIME)*/@Pointcut("@target(org.springframework.stereotype.Controller)")public void pointcutTargetAnno(){}/*** 9、@args匹配参数中标注为@Sevice的注解的方法*/@Pointcut("@args(org.springframework.stereotype.Service)")public void pointcutArgsAnno(){}/*** 10、使用excution表达式* execution(*  modifier-pattern?           //用于匹配public、private等访问修饰符*  ret-type-pattern            //用于匹配返回值类型,不可省略*  declaring-type-pattern?     //用于匹配包类型*  name-pattern(param-pattern) //用于匹配类中的方法,不可省略*  throws-pattern?             //用于匹配抛出异常的方法* )** 下面的表达式解释为:匹配com.leo.controller.HelloController类中以hello开头的修饰符为public返回类型任意的方法*/@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))")public void pointCut() {}

2.3 示例:

/*** 自定义切面,实现公共字段自动填充处理逻辑*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {/*** 切入点*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut(){}/*** 前置通知,在通知中进行公共字段的赋值*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("开始进行公共字段自动填充...");//获取到当前被拦截的方法上的数据库操作类型MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象OperationType operationType = autoFill.value();//获得数据库操作类型//获取到当前被拦截的方法的参数--实体对象Object[] args = joinPoint.getArgs();if(args == null || args.length == 0){return;}Object entity = args[0];//准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同的操作类型,为对应的属性通过反射来赋值if(operationType == OperationType.INSERT){//为4个公共字段赋值try {Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象属性赋值setCreateTime.invoke(entity,now);setCreateUser.invoke(entity,currentId);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {e.printStackTrace();}}else if(operationType == OperationType.UPDATE){//为2个公共字段赋值try {Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象属性赋值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {e.printStackTrace();}}}
}

3.添加注解

.../*** 新增分类* @param category*/@Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, update_user) " +"VALUES " +"(#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")@AutoFill(value = OperationType.INSERT)void insert(Category category);
...

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

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

相关文章

centos789手动无脑用sh脚本安装Java8

#老师给的文件是jdk1.8版本,所以我这边写的也是1.8的脚本 输入命令:mkdir –p /export/data #放置相关的数据文件 输入命令mkdir –p /export/servers #软件的安装目录 输入命令:mkdir –p /export/software 上传文件jdk-8u241-linux-x64.tar.gz到/export/software目录 然后…

C++ vector 的一些操作

▲ 《C++ Primer》 P87▲ 《C++ Primer》 P91

BoardLight

​​这是一个easy程度的靶机,所以博主写的也很简单,总共有2个flag。 信息收集端口扫描: ​​发现开放了22,80端口 Web渗透 ​​最底部发现域名 board.htb写进/etc/hosts​文件中 sudo echo "10.10.11.11 board.htb" | sudo tee -a /etc/hosts 对他进行子域名爆破…

LLM学习笔记-长度外推技术

长度外推为在不需要对模型进行额外训练的情况下,模型可以处理更长的序列。本篇文章主要介绍目前大模型用到的一些长度外推技术,包括以RoPE为基础进行位置插值、NTK-aware、动态NTK、NTK-by-parts 和YaRN。关于RoPE,可参见我的上一篇博客LLM学习笔记-位置编码篇 位置插值 回想…

裘立帆-第一次作业

在博客园建立个人技术博客,完善个人信息及博客设置,并发布一篇包含自我介绍、技能树与技术偏好、课程期望的随笔,以此作为课程参与的开始。这个作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/rjjc/这个作业的目标 熟悉博客的写作和使用,将自己介绍给老师和助教姓名…

章14——集合——集合体系

目录两个难点 底层机制,和不同应用场景下的选择集合体系图,需要背诵!总结: 1、集合主要是两组(单列集合、双列集合) 2、Collection 接口有两个重要的子接口 List Set, 他们的实现子列都是单列集合 3、Map 接口实现的子类是双列集合,存放的是key,value 4、上述两张图要记…

使用 VSCode 调试 Zig

首要条件是你本地需要安装MinGW-w64. 可以参考MinGW-w64安装教程——著名C/C++编译器GCC的Windows版本 - jack_Meng - 博客园 (cnblogs.com) 这里有几点需要注意,在2024年9月时,我没有找exe的安装,你需要在github下载 Releases niXman/mingw-builds-binaries (github.com) …

Go 入门指南:8.5. map 的排序

原创 吃个大西瓜 Coding Big Tree2024年09月19日 08:00 云南map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序(详见第 8.3 节)。 如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort 包,详见第 7.6.6 节),然后可以使用…

频率响应9

频率响应 公式推导 正弦输入的一般形式 \[u(t)=Asin(\omega_i)+bcos(\omega_i) \]整理 \[u(t)=M_i sin(\omega_i t + \phi_i) \\其中 \phi_i=arctan \frac{B}{A} \qquad ,M_i=\sqrt{A^2+B^2} \]输入到系统 G(s) \[\begin{aligned} U(s)& =\mathcal{L}[u\left(t\right)]=\…

vscode 搜索框3个按钮分别代表什么

https://blog.csdn.net/u012292754/article/details/108307288相信坚持的力量,日复一日的习惯.

数据库系统 1 关系数据库

数据库系统 1 关系数据库 三层体系结构外部层:数据库的用户视图 概念层:数据库的整体视图,提供内、外部层的映射和必要的独立性所有实体,实体的属性和实体间的联系 数据的约束 数据的语义信息 安全性和完整性信息内部层:数据库在计算机上的物理表示数据独立性 三层体系的主…

记录一次首页优化的经历

公司最近要进行多品牌合一,原来五个品牌的app要合并为一个。品牌立项、审批、方案确定,历史数据迁移、前期的基础工程搭建,兼容以及涉及三方的交互以及改造,需求梳理等也都基本完成,原来计划9月中旬进行上线,但是上线后服务端的压测一直通不过-首页抗不过太高的并发。app…