spring aop实现操作日志记录
spring aop实现操作日志记录
此次的目的是实现 对 controller中的方法执行情况进行记录,记录的有方法执行时间,操作人,请求的路径,方法的入参,模块,功能等。并实现利用注解的方式实现对被操作方法的简单注释(模块,功能)
框架 springmvc+ mybatis
1.Log注解
package com.dzqc.dz.common.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/** 方法模块 */
String title() default "";
/** 功能 */
String action() default "";
}
2.LogAspect切面 (重点)
package com.dzqc.dz.common.aop;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.dzqc.dz.common.dao.LogMapper;
import com.dzqc.dz.common.entity.LogMessage;
/**
* @author Administrator
* @Description 日志记录
*/
public class LogAspect {
@Autowired
private LogMapper logMapper;//日志 mapper
private String requestPath = null ; // 请求地址
private long startTimeMillis = 0; // 开始时间
private long endTimeMillis = 0; // 结束时间
private String user = null; // 操作人
private HttpServletRequest request = null;//请求
/**
* @param joinPoint
* @Description 气质通知 方法调用前触发 记录开始时间,从session中获取操作人
*/
public void before(JoinPoint joinPoint){
startTimeMillis = System.currentTimeMillis();
}
/**
* @param joinPoint
* @Description 获取入参方法参数
* @return
*/
public Map<String, Object> getNameAndValue(JoinPoint joinPoint) {
Map<String, Object> param = new HashMap<>();
Object[] paramValues = joinPoint.getArgs();
String[] paramNames = ((CodeSignature)joinPoint.getSignature()).getParameterNames();
for (int i = 0; i < paramNames.length; i++) {
if(paramValues[i] instanceof Integer || paramValues[i] instanceof String) {
param.put(paramNames[i], paramValues[i]);
}
}
return param;
}
/**
* @param joinPoint
* @Description 后置通知 方法调用后触发 记录结束时间 ,操作人 ,入参等
*/
public void after(JoinPoint joinPoint) {
request = getHttpServletRequest();
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class<?> targetClass = null;
try {
targetClass = Class.forName(targetName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method[] methods = targetClass.getMethods();
String title;
String action;
Class<?>[] clazzs;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
clazzs = method.getParameterTypes();
if (clazzs!=null&&clazzs.length == arguments.length
&&method.getAnnotation(Log.class)!=null) {
request = getHttpServletRequest();
requestPath=request.getServletPath();
user = (String) request.getSession().getAttribute("user");
title = method.getAnnotation(Log.class).title();
action = method.getAnnotation(Log.class).action();
endTimeMillis = System.currentTimeMillis();
LogMessage log=new LogMessage(user, requestPath,
(endTimeMillis-startTimeMillis)+"ms",
getNameAndValue(joinPoint).toString(), title, action);
logMapper.writeLog(log);
break;
}
}
}
}
/**
* @Description: 获取request
*/
public HttpServletRequest getHttpServletRequest(){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes)ra;
HttpServletRequest request = sra.getRequest();
return request;
}
/**
* @param joinPoint
* @return 环绕通知
* @throws Throwable
*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
return null;
}
/**
* @param joinPoint
* @Description 异常通知
*/
public void throwing(JoinPoint joinPoint) {
System.out.println("异常通知");
}
}
3.封装日志信息的实体类
package com.dzqc.dz.common.entity;
public class LogMessage {
private String operationUser;//操作人
private String path;//请求路径
private String time;//方法执行时间
private String parameter;//方法入参
private String title;//操作方法
private String action;//方法描述
public String getOperationUser() {
return operationUser;
}
public LogMessage() {
super();
// TODO Auto-generated constructor stub
}
public LogMessage(String operationUser, String path, String time,
String parameter, String title, String action) {
super();
this.operationUser = operationUser;
this.path = path;
this.time = time;
this.parameter = parameter;
this.title = title;
this.action = action;
}
public void setOperationUser(String operationUser) {
this.operationUser = operationUser;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getParameter() {
return parameter;
}
public void setParameter(String parameter) {
this.parameter = parameter;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
}
4.Mapper
package com.dzqc.dz.common.dao;
import com.dzqc.dz.common.entity.LogMessage;
public interface LogMapper {
public void writeLog(LogMessage message);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.dzqc.dz.common.dao.LogMapper" >
<insert id="writeLog" parameterType="com.dzqc.dz.common.entity.LogMessage">
INSERT into log(operationUser,path,time,parameter,title,action) VALUES(#{operationUser},#{path},#{time},#{parameter},#{title},#{action});
</insert>
</mapper>
5.spring aop配置文件
<aop:aspectj-autoproxy proxy-target-class="true" />
<!--将日志类注入到bean中。-->
<bean id="logAspect" class="com.dzqc.dz.common.aop.LogAspect"></bean>
<aop:config>
<aop:aspect id="LogAspect" ref="logAspect">
<aop:pointcut id="log" expression="execution(* com.dzqc.dz.*.controller.*.*(..))"/>
<aop:before pointcut-ref="log" method="before"/>
<aop:after-throwing pointcut-ref="log" method="throwing" throwing="e"/>
<aop:after pointcut-ref="log" method="after"/>
</aop:aspect>
</aop:config>
6.测试controller
package com.dzqc.dz.common.controller;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.dzqc.dz.common.aop.Log;
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/logtest")
@ResponseBody
@Log(title="logtest",action="测试log日志")
public Map<String,Object> logtest(HttpSession session,Integer id,String username) {
session.setAttribute("user","rjx");
Map<String,Object> map=new HashMap<>();
map.put("code", 200);
map.put("data", "success");
return map;
}
@RequestMapping("/test")
@ResponseBody
public Map<String,Object> test(HttpSession session,Integer id,String username) {
session.setAttribute("user","rjx");
Map<String,Object> map=new HashMap<>();
map.put("code", 200);
map.put("data", "success");
return map;
}
}
我们在logtest方法中加入Log注解,在调用这个方法的时候 会把一些信息记录进数据库,而没有加Log注解的方法则没有记录,但是根据aop的配置可知,TestController中所有的方法都被植入进了 LogAspect 切面中的逻辑,只是在LogAspect中进行了判断,只有带Log注解的方法执行才会 被记录进数据库。
测试:
注意:如果运行出现如下错误,请换较高版本的spring依赖
<spring.version>4.3.5.RELEASE</spring.version>