最近项目中需要记录用户的操作,如果每个controller或者service中都写插入语句的话未免不太现实,刚好最近师兄推荐切面编程,故使用切面的方式进行记录操作日志。
第一步:添加需要的jar
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
第二步:在application中打开切面的支持
<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
第三步: 编写自定义注释,这里我只添加了一个参数 remark,实际可根据逻辑添加多个
/**
*
* 操作日志注解
*
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodLog {
/**
* 记录操作描述
* @return
*/
String remark() default "";
第四步: 编写切面
@Component
@Aspect
public class ControllerOperationLog {
@Resource
private OperationLogService operationLogService;
private static final Logger log = LoggerFactory.getLogger(ControllerOperationLog.class);
/**
* 切点
*/
@Pointcut("execution(* com.*.controller..*(..))")
public void recordLog() {
}
/**
* 所拦截方法执行之前执行
*/
@Before("recordLog()")
public void before(){
}
/**
* 同时在所拦截方法的前后执行,学习Around的使用
* 在ProceedingJoinPoint.proceed()前后加逻辑
*/
@Around("execution(public * com.jtexplorer.controller..*.*(..))")
// @Around("recordLog()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
joinPoint.proceed();
long costTime = System.currentTimeMillis() - startTime;
String methodName = joinPoint.getSignature().getName();
//记录执行请求耗时
log.info(methodName + " finished ! Cost : " + costTime + " ms.");
}
/**
* 所拦截方法执行之后执行
*/
@After("recordLog()")
public void after(JoinPoint point){
HttpServletRequest request = this.getRequest(point);
try {
MethodLog methodLog = getMethodRemark(point);
if(methodLog != null) {
insertLog(request, methodLog.remark(), getParam(request));
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取参数request
*
* @param point
* @return
*/
private HttpServletRequest getRequest(JoinPoint point) {
Object[] args = point.getArgs();
for (Object obj : args) {
if (obj instanceof HttpServletRequest) {
return (HttpServletRequest) obj;
}
}
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
return request;
}
/**
* 将日志插入数据库中
**/
public void insertLog(HttpServletRequest request,String operation,String data){
operationLogService.insertLog(request.getSession(), request, operation+"; data : "+data);
}
/**
* 获取方法的中文备注,用于记录用户的操作日志
*
* @param joinPoint
* @return
* @throws Exception
*/
private MethodLog getMethodRemark(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();
for (Method m : method) {
if (m.getName().equals(methodName)) {
Class[] tmpCs = m.getParameterTypes();
if (tmpCs.length == arguments.length) {
MethodLog methodCache = m.getAnnotation(MethodLog.class);
if (methodCache != null && !("").equals(methodCache.remark())) {
return methodCache;
}
break;
}
}
}
return null;
}
/**
* controller中捕获到异常时执行
**/
@AfterThrowing(pointcut = "recordLog()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
}
/**
* 获取IP
* @param request
* @return
*/
private String getRequestIP(HttpServletRequest request) {
String ip = null;
if (request.getHeader("x-forwarded-for") == null) {
ip = request.getRemoteAddr();
}else{
ip = request.getHeader("x-forwarded-for");
}
return ip;
}
/**
* 将controller的请求参数转化成jsonString
* @param request
* @return
*/
private String getParam(HttpServletRequest request) {
Map properties = request.getParameterMap();
Map returnMap = new HashMap();
Iterator entries = properties.entrySet().iterator();
Map.Entry entry;
String name = "";
String value = "";
while (entries.hasNext()) {
entry = (Map.Entry) entries.next();
name = (String) entry.getKey();
Object valueObj = entry.getValue();
value = null;
if (null == valueObj) {
value = "";
} else if (valueObj instanceof String[]) {
String[] values = (String[]) valueObj;
for (int i = 0; i < values.length; i++) {
if (value == null) {
value = (values[i] == null ? "" : values[i]);
}else {
value += "," + (values[i] == null ? "" : values[i]);
}
}
} else {
value = valueObj.toString();
}
returnMap.put(name, value);
}
JSONObject json = new JSONObject(returnMap);
return json.toJSONString();
}
}
第五步: 在controller中使用
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
@MethodLog(remark = "管理端用户登录") //增加此处的注解,remark可填写操作类型
public JsonResult adminLogin(@RequestParam(required = false, defaultValue = "") String username,
@RequestParam(required = false, defaultValue = "") String password,
HttpSession session) {
return adminUserService.adminLogin(username,password,session);
}
除切面实现日志记录外,切面中的@After @Before @Around @AfterThowing 和 注解中表达式的使用会进一步补充。
{{ cmt.username }}
{{ cmt.content }}
{{ cmt.commentDate | formatDate('YYYY.MM.DD hh:mm') }}