利用spring切面編程特性 實現(xiàn)監(jiān)控controller操作,利用反射自動拿到方法參數(shù)以及返回值的名,類型以及值,記錄持久化
框架:JEECG JDK:1.8
1. 首先第一步建立一個自定義
annotationSystemLog
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;
/***
*
*
* Title: SystemLog<br/>
*
* Description: 日志記錄<br/>
*
* @author 王維杰
*
* @date 2018年4月2日
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLog {
/***
*
* @Title: methodsDescription
* @Description: 方法描述
* @return String 返回類型
* @throws
*/
String methodsDescription() default "";
}
2. 書寫攔截類
LogAopAction
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecgframework.core.util.ResourceUtil;
import org.jeecgframework.core.util.aoplog.annotation.SystemLog;
import org.jeecgframework.core.util.aoplog.entity.LogEntity;
import org.jeecgframework.core.util.aoplog.service.AopLogServiceI;
import org.jeecgframework.web.system.pojo.base.TSUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson.JSON;
/***
*
* @ClassName: LogAopAction
* @Description: aop日志工具類
* @author 王維杰
* @date 2018年4月3日
*
*/
@Component
@Aspect
public class LogAopAction {
private static final Logger logger = Logger.getLogger(LogAopAction.class);
private long BEGIN_TIME;
private long END_TIME;
private LogEntity log = new LogEntity();
@Autowired
public AopLogServiceI logService;
/**
* Service層切點
*
* Ponintuct里面有spring的表達式,可以去百度 我寫的意思是對所有帶有類路徑所指示的注解的方法生效
*/
@Pointcut("@annotation(org.jeecgframework.core.util.aoplog.annotation.SystemLog)")
public void logAspect() {
}
/**
*
* @Title: around
* @Description: 構(gòu)造log實體
* @param pjp
* @return
* @throws Throwable
* Object 返回類型 Around是對哪個切點生效
* @throws
*/
@Around("logAspect()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
TSUser user = ResourceUtil.getSessionUser();
log.setUserId(user.getId().toString());
// 當前用戶姓名
log.setLoginAccount(user.getUserName());
// 設(shè)置參數(shù)列表
log.setParamList(this.getparamList(pjp));
// 方法描述
log.setMethodsDescrip(this.getMethodsDescription(pjp));
// 訪問人ip
log.setLoginIp(this.getIp(request));
// 訪問連接
log.setActionUrl(request.getRequestURI());
Object object = null;
try {
object = pjp.proceed();
log.setDescription("執(zhí)行成功");
log.setState(1);
} catch (Exception e) {
log.setDescription("執(zhí)行失敗");
log.setState(-1);
e.printStackTrace();
}
return object;
}
/***
*
* @Title: doBefore
* @Description: 執(zhí)行開始時間 void 返回類型
* @throws
*/
@Before("logAspect()")
public void doBefore() {
BEGIN_TIME = new Date().getTime();
}
/***
*
* @Title: after
* @Description: 執(zhí)行結(jié)束時間 void 返回類型
* @throws
*/
@After("logAspect()")
public void after() {
END_TIME = new Date().getTime();
}
/***
*
*
* Title: doAfter<br/>
*
* Description: 邏輯執(zhí)行完 保存數(shù)據(jù)庫<br/>
*/
@AfterReturning(value = "logAspect()", returning = "result")
public void doAfter(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
// 設(shè)置方法名
log.setMethods(methodName);
// 返回值
log.setResult(JSON.toJSONString(result));
// 算出執(zhí)行時間
log.setActionTime(new Date(END_TIME - BEGIN_TIME));
// 設(shè)置開始時間
log.setGmtcreate(new Date(BEGIN_TIME));
// 存入數(shù)據(jù)庫
try {
logger.info(log);
logService.save(log);
} catch (Exception e) {
logger.error("日志存入失敗=====>" + e.getStackTrace());
}
}
/*************工具方法開始****************
*
* @Title: getparamList
* @Description: 獲取參數(shù)列表
* @param pjp
* @return String 返回類型
* @throws
*/
private String getparamList(ProceedingJoinPoint pjp) {
Object[] paramValues = pjp.getArgs();
String[] paramNames = ((CodeSignature) pjp.getSignature())
.getParameterNames();
List<Map<String, Object>> map_list = new ArrayList<Map<String, Object>>();
Map<String, Object> paramMap = null;
for (int i = 0; i < paramNames.length; i++) {
paramMap = new HashMap<String, Object>();
paramMap.put("paramNames", paramNames[i]);
if (paramValues[i] == null) {
paramMap.put("paramValues", "NULL");
} else {
paramMap.put("paramValues", getFiledsInfo(paramValues[i]));
}
map_list.add(paramMap);
}
return JSON.toJSONString(map_list);
}
/***
*
* @Title: getFieldValueByName
* @Description: 根據(jù)屬性名獲取屬性值
* @param fieldName
* @param o
* @return Object 返回類型
* @throws
*/
private Object getFieldValueByName(String fieldName, Object o) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
Method method = o.getClass().getMethod(getter, new Class[] {});
Object value = method.invoke(o, new Object[] {});
return value;
} catch (Exception e) {
return e.getMessage();
}
}
/***
*
* @Title: getFiledsInfo
* @Description: 獲取屬性類型(type),屬性名(name),屬性值(value)的map組成的list
* @param o
* @return List<Map<String,Object>> 返回類型
* @throws
*/
private List<Map<String, Object>> getFiledsInfo(Object o) {
Field[] fields = o.getClass().getDeclaredFields();
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Map<String, Object> infoMap = null;
for (int i = 0; i < fields.length; i++) {
Object value = getFieldValueByName(fields[i].getName(), o);
if (value != null) {
infoMap = new HashMap<String, Object>();
infoMap.put("type", fields[i].getType().toString());
infoMap.put("name", fields[i].getName());
infoMap.put("value", value);
list.add(infoMap);
}
}
return list;
}
/***
*
*
* Title: getIp<br/>
*
* Description: 獲得ip地址<br/>
*
* @param request
* @return
*/
private String getIp(HttpServletRequest request) {
if (request.getHeader("x-forwarded-for") == null) {
return request.getRemoteAddr();
}
return request.getHeader("x-forwarded-for");
}
/**
*
* @Title: getMethodsDescription
* @Description: 獲取方法描述
* @param pjp
* @return String 返回類型
* @throws
*/
private String getMethodsDescription(ProceedingJoinPoint pjp) {
// 攔截的實體類
Object target = pjp.getTarget();
// 攔截的方法名
String methodName = pjp.getSignature().getName();
// 攔截的放參數(shù)類型
Signature sig = pjp.getSignature();
MethodSignature msig = null;
if (!(sig instanceof MethodSignature)) {
throw new IllegalArgumentException("該注解只能用于方法");
}
msig = (MethodSignature) sig;
Class<?>[] parameterTypes = msig.getMethod().getParameterTypes();
Method method = null;
try {
method = target.getClass().getMethod(methodName, parameterTypes);
} catch (Exception e) {
e.printStackTrace();
}
SystemLog systemlog = method.getAnnotation(SystemLog.class);
return systemlog.methodsDescription();
}
}
3. 給出log實體
LogEntity我這邊是使用JEECG框架根據(jù)數(shù)據(jù)庫表生成的實體 具體的get set方法就不再贅述
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
import org.jeecgframework.poi.excel.annotation.Excel;
/**
*
* @ClassName: LogEntity
* @Description:
* @author 王維杰
* @date 2018年4月3日
*
*/
public class LogEntity{
/** 日志id */
private java.lang.String id;
/** 操作用戶id */
private java.lang.String userId;
/** 操作用戶名 */
private java.lang.String loginAccount;
/** 操作人ip */
private java.lang.String loginIp;
/** 操作請求的鏈接 */
private java.lang.String actionUrl;
/** 方法描述 */
private java.lang.String methodsDescrip;
/** 方法名 */
private java.lang.String methods;
/** 返回值 */
private java.lang.String result;
/** actionTime */
private java.util.Date actionTime;
/** 本次執(zhí)行描述 */
private java.lang.String description;
/** 本次執(zhí)行時間 */
private java.util.Date gmtcreate;
/** 該操作狀態(tài),1表示成功,-1表示失??! */
private java.lang.Integer state;
/** 不為空的參數(shù)列表 */
private java.lang.String paramList;
/** 操作級別 */
private java.lang.Integer operationLevel;
/** 本條記錄創(chuàng)建時間 */
private java.util.Date careatTime;
3. 修改配置文件
spring-mvc.xml以及spring-mvc-hibernate.xml讓JEECG框架知道日志系統(tǒng)的位置
spring-mvc.xml文件,增加aop上下文 仔細看看有沒漏下的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- 啟用切面注解 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!-- 增加aop自動掃描并實例化bean -->
<bean id="logAopAction" class="切面類全路徑"></bean>
如果需要持久化,打開
spring-mvc-hibernate.xml文件,增加一個<value>持久層包路徑</value>
最后用法 直接在需要記錄日志的方法名上 書寫:
@SystemLog(methodsDescription="方法描述")搞定