之前要做一个记录用户操作的日志记录,找了很多方法,最后选择使用spring AOP来实现。由于是要记录用户操作的日志,所以我使用的是返回通知(@AfterReturning),只有在前端调用了我后端的接口并成功返回,才调用我的切面方法记录用户的操作存储到数据库中。
LogAnnotation.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* 日志
* @author **
*
*/
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Document
public @interface LogAnnotation {
/** 日志描述 */
String description() default "";
/** 业务类型 */
int bizID();
}
切面类
LogAspect.java
@Aspect
@Component
@SuppressWarnings({ "unchecked", "unused", "rawtypes" })
public class LogAspect {
// 注入service,用来将日志信息保存在数据库 这是我的service,你只需要注入你自己的service就行
@Resource
private OprLogDaoService oprLogDaoService;
//在**处填入你的LogAnnotation所在的包
//此代码是拦截所有使用了LogAnnotation注解的接口
@Pointcut("@annotation(**.LogAnnotation)")
// 定义一个切点
private void accAspect() {
}
//返回通知
@AfterReturning(pointcut= "accAspect()",returning="result")
public void around(JoinPoint joinPoint, Object result) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
String classType = joinPoint.getTarget().getClass().getName();
Class<?> clazz = Class.forName(classType);
String clazzName = clazz.getName();
// 拦截的方法名称。当前正在执行的方法
String methodName = joinPoint.getSignature().getName();
// 获取方法的参数
Object[] args = joinPoint.getArgs();
// 获取传入参数的键值对
Map<String, Object> map = (Map<String, Object>)
LogUtil.getFieldsName(this.getClass(), clazzName, methodName,
args);
// 将request中的参数转化为键值对,方便取出
Map<String, Object> map2 = (Map<String, Object>) map.get("request");
String resultArgs = result.toString();
// 获取返回方法的参数
JSONObject jasonObject = JSONObject.fromObject(resultArgs);
Map mapResult = (Map) jasonObject;
//将返回的result参数取出
Map<String, Object> res = (Map<String, Object>) mapResult.get("result");
//这样传入的参数和返回的参数都已变成Map对象
//通过直接 .get("字段名")的方式将对应的字段值取出
//比如取出用户的用户名ID
Integer userID = (Integer) mapResult.get("userID");
// 常见日志实体对象
OprLog oprLog = new OprLog();
//根据自己定义的实体类的属性将数据填入
OprLog.setUserID(userID);
OprLog.set...();
// 保存进数据库
//开头定义的service,这边使用你自己的service就行了
oprLogDaoService.addLog(oprLog);
}
接下来就是在controller上添加注解了
只需要在接口上添加注解@LogAnnotation(description = "日志记录", bizID = 1) bizID是我自己定义的你也可以定义成String类型,写上方法名。
@LogAnnotation(description = "日志记录", bizID = 1)
@RequestMapping(value = "/Test", method = RequestMethod.POST)
获取自定义注解内容的方法
可以直接在注解上写用户的操作内容(修改、添加之类的)
/**
* 获取注解内容
* @param joinPoint
* @return
* @throws Exception
*/
public static Integer getMthodRemark(JoinPoint joinPoint)
throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] method = targetClass.getMethods();
Integer bizId = null;//此处可以根据你想要的类型来修改
for (Method m : method) {
if (m.getName().equals(methodName)) {
Class[] tmpCs = m.getParameterTypes();
if (tmpCs.length == arguments.length) {
ControllerLogAnnotation cla = m.getAnnotation(ControllerLogAnnotation.class);
if (cla != null) {
bizId = cla.bizID();
}
break;
}
}
}
return bizId;
}
}
另外还有一些工具类的方法
这个是转换成map对象的方法
public static Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args)throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(cls);
pool.insertClassPath(classPath);
CtClass cc = pool.get(clazzName);
CtMethod cm = cc.getDeclaredMethod(methodName);
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
if (attr == null) {
// exception
}
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
for (int i = 0; i < cm.getParameterTypes().length; i++) {
map.put(attr.variableName(i + pos), args[i]);// paramNames即参数名
}
return map;
}
最后在springMVCxml中加入
<context:component-scan
//LogAspect 在aop中
base-package="切面所在的包例如(a.b.aop)"></context:component-scan>
<aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>
另外还有一些依赖的jar包,需要的jar包都可以在百度中搜索到,用的时候没有可以在百度查询jar包依赖,放在maven项目中的pom.xml文件中。
PS:后续发现使用事物对接口进行处理更加方便,直接在操作的时候添加日志,接口报错事物自动回滚即可。