需求:查看系统各个模块的用户操作记录,通过AOP切面实现,在不同模块的增删改接口上加切面注解
部分示例代码如下,剩下的自行补充
流程:
1.根据配置确定是否启用用户操作记录。UserTraceAspectCondition:实现Condition接口的matches方法,根据配置文件决定是否初始化类
2.切面配置类:AspectConfig 增加@Configuration和 @Bean注解,根据@Conditional注解来确定确认切面的执行条件。配置开启的话,实例化切面类;增加切面注解@interface OperateLog,定义UserTraceAspect切面类代表用户操作痕迹的服务类、操作模块、操作类型、操作内容等参数
3.切面具体实现类:增加@Aspect注解,方法上增加@AfterReturning @AfterThrowing,分别在方法返回后执行切面方法或抛出异常后执行切面方法
4.接口上增加@OperateLog切面注解,并指定该接口对应的模块参数和操作类型的参数、及根据切面处理用户的操作痕迹类(默认DefaultOperateLogServiceImpl)
AspectConfig 切面配置类
UserTraceAspect:用户操作痕迹切面类
@annotation(operateLog)
@interface OperateLog
ApplicationContextUtil.getBean(operateLog.operateLogService()); 接口切面注解上对operateLogService进行了赋值
注意点:处理切面的类分为正常返回对象的切面或者抛出异常的切面;处理用户操作记录的切面处理类的事务是新建的,不影响前面业务操作;切点JoinPoint 的.getArgs方法能从方法中或请求中获取参数值,如果以对象接口的,在方法中无法获取参数值,需要通过request.getParameter(parameter) 获取参数值
UserTraceAspectCondition implements Condition{
@Override
publlic Boolean matches(conditionContext.getEnvironment().getProperty("ams.userTrace.use"))
}
@Configuration
AspectConfig {
@Bean
@Conditional(UserTraceAspectCondition.class)
public UserTraceAspect uerTraceAspect(){
return new UserTraceAspect();
}
}
切面注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {
Class<? extends OperateLogService> operateLogService() default DefaultOperateLogServiceImpl.class;
OperateModule operateModule();
OperateType operateType();
String operateContent() default "";
boolean cover() default false;
}
切面类
@Slf4j
@Aspect
UserTraceAspect {
@AfterReturning(value = "@annotation(operateLog)",returning = "object")
public void doAterReturning(JoinPoint joinPoint, OperateLog operateLog ,Object object)throws Throwable{
try{
OperateLogService operateLogService = ApplicationContextUtil.getBean(operateLog.operateLogService());
operateLogService.process(joinPoint,getUserTraceDTO(operateLog),object);
}catch (Exception e){
log.warn("保存用户操作痕迹时发生异常:",e);
}
}
异常情况
public void doAfterThrowing(JoinPoint joinPoint, OperateLog operateLog, Throwable e) throws Throwable{
try{
OperateLogService operateLogService = ApplicationContextUtil.getBean(operateLog.operateLogService());
operateLogService.process(joinPoint,getUserTraceDTO(operateLog),e);
}catch (Exception e2){
log.warn("保存用户操作痕迹时发生异常:",e2);
}
}
}
@Service
public class DefaultOperateLogServiceImpl extends AbstractOperateLogService{
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void process(JoinPoint joinPoint, UserTraceDTO userTraceDTO)throws Exception {
super.process(joinPoint,userTraceDTO);
setOperateContent(joinPoint,userTraceDTO);
userTraceService.insert(userTraceDTO);
}
}
abstract class AbstractOperateLogService implements OperateLogService {
@Override
public void process(JoinPoint joinPoint, UserTraceDTO userTraceDTO) throws Exception{
//默认成功
userTraceDTO.setOperateResult(Boolean.TRUE);
userTraceDTO.setUsername(SecurityUtils.getCurrentUsername());
userTraceDTO.setOrganFullId(SecurityUtils.getCurrentOrgFullId());
userTraceDTO.setOperateDate(new Date());
userTraceDTO.setClassName(joinPoint.getSignature().getDeclaringTypeName());
userTraceDTO.setMethodName(joinPoint.getSignature().getName());
}
@Slf4j
@Service
public class ConfigOperateLogServiceImpl extends AbstractOperateLogService{
}
public Object getParameter(JoinPoint joinPoint,String parameter) throws Exception{
// 参数值
Object[] args = joinPoint.getArgs();
// 参数名
String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
//参数打印
for (int i = 0,length = argNames.length;i<length;i++) {
if (parameter.equals(argNames[i])){
return args[i];
}
}
//参数形式提交请求的,对象形式提交请求的需要以request
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
return request.getParameter(parameter);
}
对应用户模块接口上增加切面注解,默认执行DefaultOperateLogServiceImpl
@OperateLog(operateModule = OperateModule.USER,operateType = OperateType.INSERT)
@OperateLog(operateModule = OperateModule.USER,operateType = OperateType.UPDATE)
@OperateLog(operateModule = OperateModule.USER,operateType = OperateType.DELETE)
需要指定执行模块的加上operateLogService参数,做二次开发
@OperateLog(operateLogService = ConfigOperateLogServiceImpl.class,operateModule = OperateModule.CONFIG,operateType = OperateType.UPDATE)