已知Service层中,每次新增和修改操作都要调用部分以下方法
- 新增:
createTime,updateTime,createUser,updateUser, - 修改:
updateTime, updateUser
现在我们打算把这些方法统一抽取到AOP中,利用反射方法去调用它们。
仅使用execution(* com.sky.mapper.*.*(..)) 会拦截到到一些不必要的方法,例如查询和删除,因此可以配合自定义注解@Autofill,能够更准确的拦截连接点,即在需要增强的方法手动添加该注解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| package com.sky.annotation;
import com.sky.enumeration.OperationType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 此注解用于给AOP指定需要自动填充“更新日期“、”创建日期“的字段的方法上
@Target(ElementType.METHOD) // 生效位置:方法
@Retention(RetentionPolicy.RUNTIME) // 生效时机:运行时
public @interface AutoFill {
// 数据库操作类型,区分UPDATE和INSERT
OperationType value();
}
|
注意到我们在注解中使用了一个OperationType作为成员,这是为了区分UPDATE和INSERT的枚举类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| package com.sky.enumeration;
/**
* 数据库操作类型
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
}
|
现在我们直接在Mapper中拦截所有的INSERT和UPDATE方法,往这些方法上加自定义注解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| package com.sky.mapper;
import com.github.pagehelper.Page;
import com.sky.annotation.AutoFill;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.entity.Category;
import com.sky.enumeration.OperationType;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CategoryMapper {
/**
* 分类-分页查询
* @param categoryPageQueryDTO
* @return
*/
Page<Category> page(CategoryPageQueryDTO categoryPageQueryDTO);
/**
* 分类-新增
* @param category
*/
@AutoFill(OperationType.INSERT)
void insert(Category category);
/**
* 分类-启用或者禁用
* @param category
*/
@AutoFill(OperationType.UPDATE)
void update(Category category);
@Delete("delete from category where id = #{id}")
void deleteById(Long id);
}
|
使用表达式逻辑运算,保证是mapper下的@Autofill注解
1
2
| @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut() {}
|
由于我们要对方法的参数进行赋值,因此需要用反射获取到传入的对象,并调用它的set方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| package com.sky.aop;
import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Objects;
@Aspect
@Slf4j
@Component
public class AutoFillAspect {
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut() {}
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
// 获取方法与注解
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
AutoFill annotation = methodSignature.getMethod().getAnnotation(AutoFill.class);
OperationType operationType = annotation.value();
// 准备需要赋值的数据
Long currentId = BaseContext.getCurrentId();
LocalDateTime now = LocalDateTime.now();
// 获取需要被赋值的对象。约定连接点的第一个参数必须是被赋值对象。
Object[] args = joinPoint.getArgs();
if (Objects.isNull(args) || args.length == 0) { return; }
Object entity = args[0];
// 使用反射获取方法
try {
Method createTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method updateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method createUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method updateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
// 根据操作方法赋值
if (operationType == OperationType.INSERT) {
log.info("插入方法自动填充");
createUser.invoke(entity, currentId);
createTime.invoke(entity, now);
updateUser.invoke(entity, currentId);
updateTime.invoke(entity, now);
} else if (operationType == OperationType.UPDATE) {
log.info("更新方法自动填充");
updateUser.invoke(entity, currentId);
updateTime.invoke(entity, now);
}
} catch (Exception e) {
log.error("AOP出错{}",e.getMessage());
}
}
}
|
注意到我们上述的代码获取反射方法的时候用了自定义常量,实际上这就是setXX方法的字符串名称。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| package com.sky.constant;
/**
* 公共字段自动填充相关常量
*/
public class AutoFillConstant {
/**
* 实体类中的方法名称
*/
public static final String SET_CREATE_TIME = "setCreateTime";
public static final String SET_UPDATE_TIME = "setUpdateTime";
public static final String SET_CREATE_USER = "setCreateUser";
public static final String SET_UPDATE_USER = "setUpdateUser";
}
|
此时,Service层所有的手动赋值都可以注释掉
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| package com.sky.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.constant.StatusConstant;
import com.sky.context.BaseContext;
import com.sky.dto.CategoryDTO;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.entity.Category;
import com.sky.exception.DeletionNotAllowedException;
import com.sky.mapper.CategoryMapper;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetmealMapper;
import com.sky.result.PageResult;
import com.sky.service.CategoryService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private SetmealMapper setmealMapper;
@Autowired
private DishMapper dishMapper;
@Override
public void add(CategoryDTO categoryDTO) {
Category category = new Category();
BeanUtils.copyProperties(categoryDTO, category);
//category.setCreateTime(LocalDateTime.now());
//category.setUpdateTime(LocalDateTime.now()); //category.setCreateUser(BaseContext.getCurrentId()); //category.setUpdateUser(BaseContext.getCurrentId()); // 设置默认禁用分类
category.setStatus(StatusConstant.DISABLE);
categoryMapper.insert(category);
}
@Override
public void disableOrEnable(Long id, Integer status) {
Category build = Category.builder()
.id(id)
//.updateTime(LocalDateTime.now())
//.updateUser(BaseContext.getCurrentId()) .status(status)
.build();
categoryMapper.update(build);
}
@Override
public void update(CategoryDTO categoryDTO) {
Category category = new Category();
BeanUtils.copyProperties(categoryDTO, category);
//category.setUpdateTime(LocalDateTime.now());
//category.setUpdateUser(BaseContext.getCurrentId()); categoryMapper.update(category);
}
// ...
}
|