美文网首页
用自定义注解和aop实现操作日志

用自定义注解和aop实现操作日志

作者: 安然在路上 | 来源:发表于2020-01-20 10:37 被阅读0次

    昨天和同事一起看怎么用自定义注解和aop实现操作日志,现在来记录一下。

    大致的思路是在修改删除新增的方法前面加上自定义注解,然后定义一个切面,在切面的环绕通知中处理注解和请求带过来的参数。PS:JSONObject真好用,因为现在的查询后的数据都是用JSONObject来接收的,所以对比修改前后的变化连反射都不用。

    首先是自定义注解:

    import java.lang.annotation.ElementType;

    import java.lang.annotation.Retention;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.Target;

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.METHOD) //表示注解用在方法上

    public @interface OperateLogAnnotation {

    String page() default "";

    String opration_table() default "";

    String key() default ""; //id名称

    }

    使用自定义注解:

    注意:因为实现了接口,所以使用的是jdk动态代理,代理那些接口中有的方法,@自定义注解只能加在接口对应的方法上

    @OperateLogAnnotation(page="系统管理——数据字典",opration_table="patee_dictionary",key="id_patee_dictionary")

    public BaseResult editDict(JSONObject req) {

    // TODO Auto-generated method stub

    log.info("editDict in, req:{}", JSONObject.toJSON(req));

    String uuid = req.getString("uuid");

    String oper = req.getString("oper");

    BaseResult result = null;

    if ("edit".equals(oper)) {

    result = updateDictById(req);

    log.info("updateDictById out result:{}", result);

    return result;

    } else if ("del".equals(oper)) {

    result = deleteDictById(req);

    log.info("deleteDictById out result:{}", result);

    return result;

    } else if ("add".equals(oper)) {

    result = addDict(req);

    log.info("addDict out result:{}", result);

    return result;

    }

    return BaseResult.returnSuccessMessage("所传编辑类型有误!", uuid);

    }
    PS:之前一致纠结的问题是怎么让注解知道是修改删除还是新增,即使这个注解是用在方法上的,但当真的用在updateDictById/addDict/deleteDictById上时,aop并不会进去,自定义注解也不支持占位符(mark一下占位符的使用:MessageFormat.format(),莫大神推荐的),还好req里面有oper(edit/add/del),可以在切面里面拿到req的具体值。

    看到莫大神写的一段代码,注解里面是可以有占位符的,但是这里好像也不太适用

    @RequestMapping(value = "dataGridEdit/{service}/{method}", method = RequestMethod.POST)

    @ResponseBody

    public BaseResult dataGridEdit(@PathVariable("service") String service,@PathVariable("method") String method,HttpServletRequest request){

    UUID uuid = UUID.randomUUID();

    JSONObject req = getParameterJson(request);

    req.put("uuid", uuid.toString());

    log.info("dataGridEdit in req : {},url is {}", req,request.getRequestURI());

    return callService(req, "app/public/"+service+"/"+method);

    }

    定义切面:

    @Aspect

    @Component

    public class OperateLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(OperateLogAspect.class);

    @Autowired

    TableChangeRecoderDao tableChangeRecoderDAO;

    /**

    • Controller层切点 注解拦截

    */

    @Pointcut("@annotation(com.paic.patee.core.app.aop.OperateLogAnnotation)")

    public void controllerAspect() {

    }

    // 配置controller环绕通知,使用在方法aspect()上注册的切入点

    @Around(value = "controllerAspect()&&@annotation(operate)")

    public Object around(ProceedingJoinPoint pjp, OperateLogAnnotation operate) throws Throwable {

    logger.info("OperateLogAspect around in");

    JSONObject req = null;

    Object[] args = pjp.getArgs();

    if (args.length > 0 && args[0] instanceof JSONObject) {

    req = (JSONObject) args[0];

    }

    JSONObject oldObject = new JSONObject();

    if (null == req || req.isEmpty()) {

    return pjp.proceed();

    }

    String oper = req.getString("oper");

    if("edit".equals(oper)||"del".equals(oper)) {

    oldObject = searchOldObject(operate.opration_table(),operate.key(),req.get(operate.key()));

    }

    BaseResult result = (BaseResult) pjp.proceed();

    String errorCode = result.getErrorCode();

    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())

    .getRequest();

    String userName=request.getHeader("userName");

    if ("0".equals(errorCode)) {

    JSONObject json = new JSONObject();

    json.put("um_id", userName);

    String page = operate.page();

    json.put("page", page);

    if("edit".equals(oper)) {

    String opration_description = compareTwoObject(req,oldObject);

    json.put("opration_description", "修改了id为:"+req.get(operate.key())+opration_description);

    }else if("del".equals(oper)) {

    json.put("opration_description", "删除!删除掉的数据为:"+oldObject);

    }else if("add".equals(oper)) {

    json.put("opration_description", "新增!新增的数据为:"+req);

    }

    json.put("opration_table", operate.opration_table());

    json.put("change_id", UUID.randomUUID().toString());

    json.put("operation_time",new Date());

    tableChangeRecoderDAO.insertOneRecord(json);

    }

    return result;

    }

    public JSONObject searchOldObject(String tableName,String key,Object id){

    JSONObject req = new JSONObject();

    req.put("table_name", tableName);

    req.put("key", key);

    req.put("id", "'"+id+"'");

    return tableChangeRecoderDAO.queryObj(req);

    }

    public static String compareTwoObject(JSONObject req, JSONObject oldObject) {

    StringBuffer sb = new StringBuffer();

    sb.append("修改的数据为:{");

    JSONObject newObject = req;

    for(String str:newObject.keySet()){

    for(String str1:oldObject.keySet()){

    if((!"update_time".equals(str))&&str.equals(str1)&&(!newObject.get(str).equals(oldObject.get(str1)))) {

    sb.append(str1).append(":").append(oldObject.get(str1));

    sb.append(" 改为 ").append(newObject.get(str)).append(";");

    }

    }

    }

    sb.deleteCharAt(sb.length()-1);

    sb.append("}");

    return sb.toString();

    }

    }

    AOP的基本概念:

    Aspect(切面):通常是一个类,里面可以定义切入点和通知;

    JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器;

    Advice(通知):AOP在特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕);

    Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

    相关文章

      网友评论

          本文标题:用自定义注解和aop实现操作日志

          本文链接:https://www.haomeiwen.com/subject/zxhjzctx.html