runwith注解为什么用不了_AspectJ + Spring Boot 注解方式记录日志,动态配置注解参数(注解参数中包含变量)...

提出需求

这里所说的记录日志,是指插入一条记录到数据库的日志表。只是插入一条数据库记录而已,一点技术含量都没有。我直接在MyBatis的mapper编写插入语句,在Java代码调用mapper方法就可以了。比如我可以这样,

sysBaseAPI

写一行语句,我就能插入一条日志了,

5f2317d80a49d9f55b5cd2af325925fd.png

做了10年Java开发了,如果还用这个弱智方法,以后面试怎么吹水呢?不行,我们要简单问题复杂化。我今天要用AOP切面+注解的方式来记录日志。我们要达到的效果是,Controller方法前面写一行@SystemLog注解,就能自动插入一条数据库记录。

/**

75d5d28debab41f70b6e632414d2f7f0.png

其实用注解的方式,和直接在代码里面调用MyBatis mapper,实现的功能是一样的,写的代码量也是一样的,都要写长长的一行,只是代码出现的位置不同而已。 直接在代码里面调用,出现的位置是Java方法体内。注解的方式,出现的位置是Java方法签名之前。注解这个东西很多时候只是一个审美上的选择,有人觉得注解用起来很有逼格,和stream,Lambda之类的同理。由于注解出现在方法签名之前,有的人把它看作是配置而不是代码,造成一种零代码实现业务功能的错觉(无代码时代要来临了)。自己编写注解类,那更是有一种自己在编写框架的错觉,毕竟大多数的注解都是各种框架带的,自己写注解的情况很少。

怎样实现注解类

我们以前水平菜的时候,以为注解类只是一个类,比如@Autowired这个注解,我们打开Autowired类的源码看了半天,也没看出什么东西,不明白为什么这样就能实现自动装配的功能。其实注解类的功能分成两部分实现,一个是注解类,另一个是切面处理类。真正的逻辑都写在切面处理类里面。注解类本身只是定义了几个参数(属性)而已。

注解类代码

我今天写的注解取名为SystemLog,名字随便起,为了显示逼格,你可以起一个看上去像框架官方API的名字。

package 

切面处理类代码

切面处理类用到了AspectJ Weaver框架,这个框架把注解所在的那个方法携带的各种数据都封装到一个JoinPoint对象中,然后我们从JoinPoint中可以获取到这些数据。

AspectJ Weaver框架包含在了Spring Boot AOP中。

<dependency>
       

除了AspectJ Weaver和FastJSON之外, 我这里引入的一些其他类库是具体业务相关的,而不是切面处理所通用的,可以忽略。

package 

切面处理类核心的方法是around方法。根据网上资料,around方法是在注解所在的方法执行前和执行后触发执行,我亲自调试,觉得它是在方法执行期间、且还没有return之前(return的前一句)触发。(见我下面画的箭头位置)

	/**
	 * 机构管理 - 新增机构
	 */
	@RequestMapping(value = "/add", method = RequestMethod.POST)
	@SystemLog(logContent = "机构管理-新增机构,机构名:departName,上级机构ID:parentId,备注信息:notes", operateType = OPERATE_TYPE_ADD)
        // 用@SystemLog注解实现记录日志
	@CacheEvict(value= {CacheConstant.SYS_DEPARTS_CACHE,CacheConstant.SYS_DEPART_IDS_CACHE}, allEntries=true)
	public Result<SysDepart> add(@RequestBody SysDepart sysDepart, HttpServletRequest request) {
		// 业务逻辑
		// 此处完全不出现插入数据库的语句,不调用MyBatis mapper

                // 此处触发执行切面处理类around方法  <———————— 就是在这个地方触发
                return result;
	}

这一套方案简单来说就是,上面这段代码,执行到箭头的位置,会触发@SystemLog注解,去执行切面处理类的around()方法。

凡是和AOP切面处理相关的代码,都不能用JRebel热部署,热部署会造成莫名其妙的问题。所以每次修改代码,建议重启Spring Boot工程。

动态配置注解参数(注解参数中包含变量)

我们使用Java注解都有一个体会,注解的参数里面是不能包含变量的,只能是固定的值。比如@RequestMapping注解,

@RequestMapping

这里的value = "/edit",是一个固定字符串,不能是变量。

而我们在记录日志的时候,可能要根据不同的情况,记录不同的日志内容,用固定字符串是满足不了需求的。

插入的日志内容为“机构管理-新增机构,机构名:浩宇镇,上级机构ID:,备注信息:添加机构成功”,我们注解中配置的参数值为 "机构管理-新增机构,机构名:departName,上级机构ID:parentId,备注信息:notes"。可见这里有三个变量,在记录日志时,dynamicParam()方法会把变量名替换成变量的值。

10accd3436ef61da6abf9868d7bfabed.png

9d943e4177c2010954e58a9c72dc9384.png

有三种途径可以给这些变量赋值。

1、注解所在的方法参数中,含有与变量名相同的属性。

136afdc32278399c14467e05328fd066.png

这里的sysDepart对象包含了departName和parentId两个属性,这些信息都会包含在切面处理类的JoinPoint对象中,从而能获取到departName和parentId两个变量的值。

2、HttpServletRequest对象中,含有与变量名相同的ParameterName

3、HttpServletRequest对象中,含有与变量名相同的AttributeName

这里的notes变量就是通过Attribute去赋值的,要在方法体内写一句代码给它赋值,

request

我在切面处理类的实现中,已经把以上3种情况都考虑到了,满足其中任何一种,都有相应代码去获取到变量值,从而把变量名替换成变量值。

这里有一个我设计得不合理的地方,我是用String.replaceAll()替换字符串,把变量名替换成变量值,这里的变量名是departName, parentId, notes,假如在HttpServletRequest对象中正好有一个Parameter或者Attribute,名为 depart,或者名为 parent,只要是名字能跟变量名中的一部分匹配上的,我的算法也会把它给替换掉。

比如,我们假设HttpServletRequest对象中有另一个Parameter为 depart = "浩宇",

我的算法并不知道 departName 才是完整的变量名,它匹配到了 depart,认为这就是变量名,于是把 depart 替换成 浩宇。 这样一来,记录的日志内容可能会变成:

机构管理-新增机构,机构名:浩宇Name,上级机构ID:,备注信息:添加机构成功

要改进也很简单,用指定的符号(可以是大括号)把变量名括起来,在replaceAll那里,把大括号也一起替换掉即可。由于前后有大括号包着,只有与变量名完全匹配的参数才会替换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值