利用spring切面编程特性 实现监控controller操作
2018-04-04 本文已影响0人
人形大叔
利用spring切面编程特性 实现监控controller操作,利用反射自动拿到方法参数以及返回值的名,类型以及值,记录持久化
框架:JEECG JDK:1.8
1. 首先第一步建立一个自定义
annotation
SystemLog
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: 构造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());
// 设置参数列表
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("执行成功");
log.setState(1);
} catch (Exception e) {
log.setDescription("执行失败");
log.setState(-1);
e.printStackTrace();
}
return object;
}
/***
*
* @Title: doBefore
* @Description: 执行开始时间 void 返回类型
* @throws
*/
@Before("logAspect()")
public void doBefore() {
BEGIN_TIME = new Date().getTime();
}
/***
*
* @Title: after
* @Description: 执行结束时间 void 返回类型
* @throws
*/
@After("logAspect()")
public void after() {
END_TIME = new Date().getTime();
}
/***
*
*
* Title: doAfter<br/>
*
* Description: 逻辑执行完 保存数据库<br/>
*/
@AfterReturning(value = "logAspect()", returning = "result")
public void doAfter(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
// 设置方法名
log.setMethods(methodName);
// 返回值
log.setResult(JSON.toJSONString(result));
// 算出执行时间
log.setActionTime(new Date(END_TIME - BEGIN_TIME));
// 设置开始时间
log.setGmtcreate(new Date(BEGIN_TIME));
// 存入数据库
try {
logger.info(log);
logService.save(log);
} catch (Exception e) {
logger.error("日志存入失败=====>" + e.getStackTrace());
}
}
/*************工具方法开始****************
*
* @Title: getparamList
* @Description: 获取参数列表
* @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: 根据属性名获取属性值
* @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();
// 拦截的放参数类型
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框架根据数据库表生成的实体 具体的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;
/** 本次执行描述 */
private java.lang.String description;
/** 本次执行时间 */
private java.util.Date gmtcreate;
/** 该操作状态,1表示成功,-1表示失败! */
private java.lang.Integer state;
/** 不为空的参数列表 */
private java.lang.String paramList;
/** 操作级别 */
private java.lang.Integer operationLevel;
/** 本条记录创建时间 */
private java.util.Date careatTime;
3. 修改配置文件
spring-mvc.xml
以及spring-mvc-hibernate.xml
让JEECG
框架知道日志系统的位置
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="方法描述")
搞定