SpringAop实操之记录关键业务请求数据
AOP,中文名称,切面。在不影响业务代码情况下,实现想要的功能,是个真炫酷的事。
aop是个好东西,记录日志是必须的。
记录数据也一样的,那么也是可以用aop来实现的,这里借助注解一起解决问题吧。
因为是关键业务,所以意味着不是所有业务,那么自然地就想到了,可以使用过滤的方式,也就是使用注解,如果有注解那么就代表要记录数据,否则不记录。
记录过程需要做什么呢?
首先,因为是记录业务数据,那么我们可以抽象出一个方法出来,也就是 xxx.addRec()。也就是就切面里只需调用记录方法即可。
那么还剩下几个问题,怎样找到这个方法?参数的处理怎么办?需要使用规范限制吗?
找到这个方法,我们可以使用反射调用,当然需要注解进行调用的类和方法名称(如果固定死则无需标注),用反射的好处就是自由,想怎么写就怎么写,要么失败要么成功。但是low!
参数判定,可以根据输入的类,进行if..else.. 判定,然后直接处理相应参数即可。这样可以很自由,想怎么写参数就怎么写参数。但是这样的话,这个切面就和业务代码完全耦合在一起了,还多了n多无谓的判断。很low!
那么问题来了,怎样才不low呢?
几个理论可以借鉴下:
1. 面向接口编程而非面向类编程;
2. 使用枚举值进行注解统一规划;
3. 使用泛型,进行参数上下限处理;
4. 使用模板方法模式封装参数;
5. 使用线程池;
让我们细看下~
面向接口编程,即规定所有业务处理类都实现一个公共的接口,从而使方法不至于凌乱,定义一个清晰的接口,更易于理解含义。各实现类只需关注自己的逻辑即可。
使用枚举值进行注解参数的限定,可以使所有业务操作都在一处进行罗列,且都符合必须的规范。另外,当哪天发现参数无法满足某些需求时,可方便地在该枚举中加入相应参数以完成需求。
使用泛型,将参数限定一定范围内,如要求入库参数必须继承某个基类以实现统一参数处理,也防止了传入任意参数而必须做相应转换的性能消耗。
使用模板方法模式进行参数封装,因入参中都要求继承一个基类,也就是具有共性的参数,所以应具备自动处理公共参数的能力,但是不可越权处理各业务实现的处理,应让实现类有能力自行处理个性化参数(实现类也可以不处理)。交由实现类处理个性化参数时,应使其具有绝对的能力,不应限制其发挥。
使用线程池技术,将额外的工作交给额外的线程,从而使主业务不爱影响。
具体代码撸一遍:
1. 注解开启新篇章,无注解,不工作。
@Component
public class UserTestService { // 添加注解,代表需要进行相应的逻辑处理
@BizRecordTrans(bizType = BizRecordTypeEnum.USER_ADD_FLOW)
public ResponseEntity<Boolean> addUser(UserlDto terminalReq) {
ResponseEntity<Boolean> ret = ResponseBuilder.buildResponse();
// 完成自己的业务...
return ret;
}
}
2. 切面配置,做一个幕后的老兵。
@Component
@Aspect
public class BizRecordAop {
private Logger logger = LoggerFactory.getLogger(this.getClass()); @Around("execution(* com.xxx.dubbo.*.*(..))) and @annotation(com.xxx.spring.annotation.BizRecordTrans)")
public Object deal(ProceedingJoinPoint pjp) throws Throwable {
Object retVal = null;
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
Class<?> classTarget = pjp.getTarget().getClass();
Class<?>[] argTypes = ((MethodSignature) pjp.getSignature()).getParameterTypes();
Method objMethod = classTarget.getMethod(methodName, argTypes);
BizRecordTrans bizRecordTrans = objMethod.getAnnotation(BizRecordTrans.class);
BizRecordTypeEnum bizRecordTypeEnum = null;
if (BizRecordTrans != null) {
bizRecordTypeEnum = BizRecordTrans.bizType();
}
try {
retVal = pjp.proceed();
} catch (Exception e) {
// ignore
throw e;
} finally {
if (bizRecordTrans != null) {
Class<? extends BaseEntity> bizRecEntityCls = bizRecordTypeEnum.getBizRecEntityCls();
BaseEntity recEntity = (BaseEntity) bizRecEntityCls.newInstance();
if (args[0] instanceof BaseDto) {
BaseDto baseDto = (BaseDto) args[0];
fillBaseFields(recEntity, baseDto);
}
try {
ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) SpringContextsUtil.getBean("threadPoolTaskExecutor");
executor.execute(() -> {
BizRecBaseService<BaseEntity> recService = (BizRecBaseService<BaseEntity>) SpringContextsUtil
.getBean(bizRecordTypeEnum.getHandlerBeanName());
recService.fitOwnParams(recEntity, args);
recService.addRecord(recEntity);
});
} catch (Exception e) {
logger.error("发生异常", e);
}
}
}
return retVal;
} /**
* 填充公共参数
*/
private void fillBaseFields(BaseEntity baseEntity, BaseDto base) {
baseEntity.setUserId(base.getUserId());
baseEntity.setSeqId(base.getSeqId());
baseEntity.setAddIp(base.getAddIp());
}
}
3. 写一个基础接口,让业务去实现。
public interface BizRecordBaseService<T extends BaseEntity> {
/**
* 执行业务方法
*/
public Integer addRecord(T bizRecord);
/**
* 个性化参数填充方法,选择性实现
*/
default void fixOwnParams(T bizRecord, Object[] rawData) {
System.out.println("可以不实现");
}
}
4. 写一个基础类,让所有其他业务实体继承以实现公共参数的封装。
@Data
public class BaseEntity {
/**
* ID
*/
private Long id; /**
* 用户ID
*/
private Long userId; /**
* 请求序列id
*/
private String seqId; /**
* 添加ip
*/
private String addIp; }
5. 打一个组合拳,搞定。
原理浅显,易懂。性能嘛,也ok,无需太担心。
SpringAop实操之记录关键业务请求数据的更多相关文章
- WebAPI获取客户端请求数据
1.什么是WebAPI,详见:http://www.cxyclub.cn/n/25123/2.一般情况下我们不需要去关心客户端的请求数据,WebAPI会通过自己的方式去将客户端请求的数据转换为实体对象 ...
- [Django框架 - 静态文件配置、request对象方法初识、 pycharm链接数据库、ORM实操增删改查、django请求生命周期]
[Django框架 - 静态文件配置.request对象方法初识. pycharm链接数据库.ORM实操增删改查.django请求生命周期] 我们将html文件默认都放在templates文件夹下 将 ...
- springAOP实现操作日志记录,并记录请求参数与编辑前后字段的具体改变
本文为博主原创,未经允许不得转载: 在项目开发已经完成多半的情况下,需要开发进行操作日志功能的开发,由于操作的重要性,需要记录下操作前的参数和请求时的参数, 在网上找了很多,没找到可行的方法.由于操作 ...
- SFUD+FAL+EasyFlash典型场景需求分析,并记一次实操记录
SFUD+FAL+EasyFlash典型场景需求分析:用整个flash存储数据,上千条数据,读取得时候用easyflash很慢,估计要检索整个flash太慢了. 改进方法:分区检索. 1存数据时,根据 ...
- 第7章使用请求测试-测试API . Rspec: everyday-rspec实操。
测试应用与非人类用户的交互,涵盖外部 API 7.1request test vs feature test 对 RSpec 来说,这种专门针 对 API 的测试最好放在 spec/requests ...
- MyBatis实操进阶版(一)
MyBatis实操进阶版(一) 目前而言,持久层框架中,在业务实现灵活性上,无可出MyBatis之右者.具体原因,后续将逐步展开 ResultMap元素的设置 配置文件中,ResultMap元素的作用 ...
- 第十章 Fisco Bcos 权限控制下的数据上链实操演练
一.目的 前面已经完成fisco bcos 相关底层搭建.sdk使用.控制台.webase中间件平台等系列实战开发, 本次进行最后一个部分,体系化管理区块链底层,建立有序的底层控管制度,实现权限化管理 ...
- 干货 | 京东云应用负载均衡(ALB)多功能实操
应用负载均衡(Application Load Balancer,简称ALB)是京东云自主研发的一款七层负载均衡产品,主要面向HTTP和HTTPS流量的WEB应用程序,提供灵活的功能配置.应用负载均衡 ...
- 72 个网络应用安全实操要点,全方位保护 Web 应用的安全
原文地址:Web Application Security Checklist 原文作者:Teo Selenius(已授权) 译者 & 校正:HelloGitHub-小熊熊 & 卤蛋 ...
随机推荐
- linux 查看系统资源命令
vmstat vmstat 1 3 #每隔一秒刷新3次 lsof lsof | more #process->file lsof | /sbin/init #file->process l ...
- hibernate入门一
---恢复内容开始--- hibernate简介: 1.优秀的持久化(通俗讲把内存上的短时间运行信息存储在持久化硬盘上)框架. 2.作用于持久层,因为没什么侵入性,所以同样适用于其他层面上的存储 3. ...
- 20175325 MyCP (课下作业,必做)
20175325 MyCP (课下作业,必做) 一.目录: 题目 设计思路 运行结果 码云链接 二.题目 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两 ...
- 解决build workspace 缓慢的问题
(1).解决方法 方法1.修改eclipse启动文件 eclipse.ini 中添加启动参数参数: -vmargs -Xmx512m 方法2.关闭自动构建工作区: pr ...
- bootstrap的引用和注意事项
1,在https://v3.bootcss.com/getting-started/#download下载bootstrap的压缩包: 2,将压缩包解压到自己的工程文件中,会得到如下结果: 3,打开这 ...
- Spring-boot在windows上安装CLI(Command Line Interface)的步骤!
首先去下载安装包,我这里整了一个zip包,一个tar包,下载地址:https://github.com/zhangyawei117/Spring-boot-CLI.git 下载完了之后,把zip包解压 ...
- oracle创建与mysql的dblink
1.先简单介绍下环境 操作系统:windows 2008 R2 (64bits) oracle数据库:10gr2 10.2.0.3.0(32bits) mysql数据库:5.1 (32bits) ...
- HTTP一、HTTP介绍与套接字
目录 一.套接字 1.HTTP与Apache 2.应用层协议:HTTP 3.套接字(IP+协议端口的组合) 4.套接字图示 5.套接字相关知识点 二.HTTP 一.套接字 1. ...
- web页面font-family显示
font-family属性很简单,直接写在css或style样式中即可. 如: font-family: "Microsoft YaHei"; 但是如果希望电脑能正确的显示我们设置 ...
- nginx的锁
一.原理 nginx的锁是基于共享内存实现的,这点跟redis中利用一个存储(也就是一个键值对)来实现锁的原理是一致的,每一项操作通过检查锁对象的lock域是否为0,来判断能否获取锁并尝试获取锁. 二 ...