在学习AOP面向切面编程时,在对数据库增、改操作自动设置时间信息是最多同学的入门第一课。大四有幸在实际开发中用到了,在自己写了一遍后印象更深刻了。
应用步骤如下:
明确操作类型,定义一个枚举类,一般是增(Insert)和改(Update)操作,在企业开发中,删除用的是逻辑删除,即设置一个标志位Is_delete,0表示未删除,1表示已删除。故删除操作本质上也是一个更新操作,只是要多改一个标志位(Is_delete)。
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 新增操作
*/
INSERT,
/**
* 删除操作
*/
DELETE
}
自定义一个注解,用于后续标注在需要被切的方法(数据库增删改方法)上。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型:Update Insert
OperationType value();
}
@Target和@Retention是用来修饰注解的注解,被称之为元注解。
- @Target的作用是指明目标,即被注解的注解(此处指AutoFill)可以用在什么地方,ElementType.METHOD表示可以用在方法上。
- @Retention的作用是定义被注解的注解的生命周期,RetentionPolicy.RUNTIME策略表名AutoFill注解不仅被保存到class文件中,jvm加载class文件后仍然存在。
定义切面,明确需要操作的切面。
//自定义常量
public class AutoFillConstant {
public static final String SET_CREATE_TIME = "setCreateTime";
public static final String SET_MODIFY_TIME = "setModifyTime";
public static final String SET_CREATE_UNAME = "setCreateUname";
public static final String SET_MODIFY_UNAME = "setModifyUname";
public static final String SET_CREATE_UID = "setCreateUid";
public static final String SET_MODIFY_UID = "setModifyUid";
public static final String SET_IS_DELETED = "setIsDeleted";
public static final String SET_IS_ENABLED = "setIsEnabled";
public static final String SET_VERSION = "setVersion";
}
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
@Pointcut("execution(* com.yintai.report.service..*.*(..)) && @annotation(com.yintai.report.annonation.AutoFill)")
public void autoFillPointCut(){}
@Before("autoFillPointCut()")
public void AutoFill(JoinPoint joinPoint){
//获取到当前被拦截的方法的操作
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();
String uid = SecureUtil.getUserId();
String uname = SecureUtil.getUserName();
if (operationType == OperationType.INSERT){
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME,LocalDateTime.class);
Method setCreateUname = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_UNAME,String.class);
Method setCreateUid = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_UID,String.class);
Method setIsDeleted = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_IS_DELETED,Integer.class);
Method setIsEnabled = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_IS_ENABLED,Integer.class);
Method setVersion = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_VERSION,Integer.class);
setCreateUid.invoke(entity,uid);
setCreateUname.invoke(entity,uname);
setCreateTime.invoke(entity,now);
setIsDeleted.invoke(entity, SqlDefaultConstant.DEFAULT_IS_DELETED);
setIsEnabled.invoke(entity,SqlDefaultConstant.DEFAULT_IS_ENABLED);
setVersion.invoke(entity,SqlDefaultConstant.DEFAULT_VERSION);
}catch (Exception e){
log.info(e.getMessage());
}
}else if (operationType == OperationType.UPDATE){
try {
Method setModifyTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_MODIFY_TIME,LocalDateTime.class);
Method setModifyUname = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_MODIFY_UNAME,String.class);
Method setModifyUid = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_MODIFY_UID,String.class);
setModifyUid.invoke(entity,uid);
setModifyUname.invoke(entity,uname);
setModifyTime.invoke(entity,now);
}catch (Exception e){
log.info(e.getMessage());
}
}else if (operationType == OperationType.DELETE){
try {
Method setModifyTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_MODIFY_TIME,LocalDateTime.class);
Method setModifyUname = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_MODIFY_UNAME,String.class);
Method setModifyUid = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_MODIFY_UID,String.class);
Method setIsDeleted = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_IS_DELETED,Integer.class);
setModifyUid.invoke(entity,uid);
setModifyUname.invoke(entity,uname);
setModifyTime.invoke(entity,now);
setIsDeleted.invoke(entity,1);
}catch (Exception e){
log.info(e.getMessage());
}
}
}
}
- @Aspect:标明当前类为一个切面类;
- @Component:将该类注册到Spring IOC容器中;
- @Pointcut:切入点,需要切入的位置。execution切入点表达式,第一个*表示返回值任意,com.yintai.report.service为需要切入的包名,“..*”表示当前包及其子包下所有类,“.*(..)”表示任何方法名任何参数类型;
- @Before:在方法执行前执行。同时有@After(在方法执行后执行),@Around(环绕切入点执行),@AfterReturning(方法执行完成后执行,有异常则不执行),@AfterThrowing(切入点运行异常时执行);
JoinPoint是切入点,可以通过getSignature()方法获取方法签名对象的详细信息,再获取方法上AutoFill注解对象,就可以该方法的操作类型。
由于增删改的方法都是对第一个参数对象进行的操作,所有直接取args[0]进行操作,通过反射获取对象的详细方法,再根据操作类型对对象进行相应的操作。