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

昨天和同事一起看怎么用自定义注解和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实现操作日志)