需求:在Mapper层中添加/更新数据操作,需要对应修改表字段的创建时间/最后修改时间
需求分析:可以给Mapper层中的新增/修改方法添加自定义注解,然后使用AOP的前置通知,在通知内通过反射获取到Mapper层中的方法对象,再获取到方法上的注解,再根据注解的值进行对应的操作。新增数据,设置创建时间、最后修改时间;修改数据,设置最后修改时间。
方式一
@Before("切点表达式")
public void before1(JoinPoint jp) {
// 先拿到被增强的方法的签名对象
Signature signature = jp.getSignature();
// 判断被增强的目标是否是一个方法 如果是进行强转
if (!(signature instanceof MethodSignature)) {
// 如果不是抛出自定义异常
throw Exception.throwException(ErrorEnums.AOP_ADVICE_ERROR);
}
MethodSignature ms = (MethodSignature) signature;
// 拿到目标方法
Method method = ms.getMethod();
// 获取method上的注解 并且拿到注解上的value值,AutoFill.class为自定义注解类
AutoFill annotation = method.getAnnotation(AutoFill.class);
// 如果注解为空,抛出自定义异常
if (ObjUtil.isNull(annotation)) {
throw SkyException.throwException(ErrorEnums.AOP_ADVICE_ERROR);
}
// 获取注解的值
String value = annotation.value();
// 如果注解为空,抛出自定义异常
if (ObjUtil.isNull(jp.getArgs()) || ArrayUtil.isEmpty(jp.getArgs())) {
throw Exception.throwException(ErrorEnums.AOP_ADVICE_ERROR);
}
// 获取参数
Object arg = jp.getArgs()[0];
// 增加注解的方法都执行的操作,如新增数据的创建时间(createTime)字段
ReflectUtil.setFieldValue(arg, "updateTime", LocalDateTime.now());
// 如果注解值==标志,如“UPDATE”(更新),则进行更新操作
if (StrUtil.equalsIgnoreCase(value, "INSERT")) {
// 设置需要更新字段
ReflectUtil.setFieldValue(arg, "createTime", LocalDateTime.now());
……
}
}
方式二
@Before("切点表达式")
public void before2(JoinPoint jp) throws NoSuchMethodException {
// 先拿到被增强的方法的签名对象
Signature signature = jp.getSignature();
// 判断被增强的目标是否是一个方法 如果是进行强转
if (!(signature instanceof MethodSignature)){
// 如果不是抛出自定义异常
throw new Exception(ErrorEnums.AOP_ADVICE_ERROR);
}
MethodSignature ms = (MethodSignature) signature;
// 获取代理方法对象
Class<?> aClass = jp.getTarget().getClass();
// 根据方法名、方法参数获取代理类中的方法对象
Method method = aClass.getMethod(ms.getName(), ms.getParameterTypes());
// 获取方法上的注解,AutoFill.class为自定义注解类
AutoFill annotation = method.getAnnotation(AutoFill.class);
// 如果注解为空,抛出自定义异常
if (ObjUtil.isNull(annotation)){
throw new Exception(ErrorEnums.AOP_ADVICE_ERROR);
}
// 获取注解的值
String value = annotation.value();
// 增加注解的方法都执行的操作,如新增数据的创建时间(createTime)字段
ReflectUtil.setFieldValue(method,"createTime",LocalDateTime.now());
// 如果注解值==标志,如“UPDATE”(更新),则进行更新操作
if (StrUtil.equalsIgnoreCase(value,"UPDATE")){
// 设置需要更新字段
ReflectUtil.setFieldValue(method,"updateTime",LocalDateTime.now())
……
}
}
区别&分析
将断点打在获取注解的后一行,查看method、annotation的内容
区别
方式一
方式二
观察可以发现,区别在于方式一获取到的是Mapper中的方法(即定义的接口方法)对象,方式二获取到的是MyBatis通过代理帮助我们实现Mapper类的实现方法对象
然后,因为我们AOP的注解是加在Mapper类中的抽象方法上的,所以通过方式二,是获取不到我们自定义的注解的
总结
使用AOP对方法增强,如果自定义注解是加在一般方法上,两者没有区别;
但如果需要增强的目标方法是Mapper里面的方法,因为Mybatis对方法进行了实现,则使用第二种方法,是获取不到我们自定义的注解的,因为方式二获取到的是实现类的方法对象,而实现类的方法对象是没有我们加的自定义注解的。