1. 使用背景
在Spring框架中,使用AOP配合自定义注解可以方便的实现用户操作的监控。
2. 引入依赖
<!-- aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3. 自定义注解
标注需要监控的不同层级的方法,比如我们可以定义一个SystemControllerLog用于标注Controller级别的方法,定义一个SystemServiceLog用于标注Service层级的方法。
SystemControllerLog.java
package com.cnki.aop;
import java.lang.annotation.*;
/**
* Title: SystemControllerLog
* @date 2020年8月24日
* @version V1.0
* Description: 自定义注解,拦截controller
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
public @interface SystemControllerLog {
String description() default "";
}
SystemServiceLog.java
package com.cnki.aop;
import java.lang.annotation.*;
/**
* Title: SystemServiceLogs
* @date 2020年8月24日
* @version V1.0
* Description: 自定义注解,拦截service
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented//表明这个注解应该被 javadoc工具记录
public @interface SystemServiceLog {
String description() default "";
}
两者本质上是一样的,只是名称不一样,在不同层级标注时用不同的名称。
4. 定义一个LogAspect类
使用@Aspect
标注让其成为一个切面,使用前置通知@Before("controllerAspect()")用于拦截Controller层记录用户的操作,切点为使用@SystemControllerLog
注解标注的方法;使用异常通知@AfterThrowing(pointcut = "serviceAspect()",throwing = "e")
用于拦截service层记录异常日志,切点为使用@SystemServiceLogs
注解标注的方法。
package com.cnki.aop;
import cnki.bdms.common.session.SessionHelper;
import com.cnki.model.UserActionLog;
import com.cnki.model.enums.LogType;
import com.alibaba.fastjson.JSON;
import com.cnki.service.IUserActionLogService;
import com.cnki.tool.base.JsonUtils;
import com.cnki.tool.base.StringUtil;
import com.cnki.tool.base.UserActionLogUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.json.JsonObject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Title: SystemControllerLog
* @date 2018年8月31日
* @version V1.0
* Description: 切点类
*/
@Aspect
@Component
@SuppressWarnings("all")
public class SystemLogAspect {
//注入Service用于把日志保存数据库,实际项目入库采用队列做异步
@Resource
private IUserActionLogService userActionLogService;
//本地异常日志记录对象
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
//Service层切点
@Pointcut("@annotation(SystemServiceLog)")
public void serviceAspect(){
}
//Controller层切点
@Pointcut("@annotation(SystemControllerLog)")
public void controllerAspect(){
}
/**
* @Description 前置通知 用于拦截Controller层记录用户的操作
* @date 2018年9月3日 10:38
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
Map<String, String[]> parameterMap = new HashMap(request.getParameterMap());
//读取session中的用户
String userCode = SessionHelper.getLoginUserCode();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//获取用户请求方法的参数并序列化为JSON格式字符串
// String params = getParams(joinPoint,method);
String params = "";
try {
String methodDes = getControllerMethodDescription(joinPoint);
if(methodDes.indexOf("上传") == -1){
Object contentObj = parameterMap.get("content");
String content = "";
if(contentObj!=null){
String[] contentArr = (String[])contentObj;
if(contentArr.length==1){
content = contentArr[0];
if(content.length() > 100){
content = content.substring(0,100) + "......";
String[] contentArr2 = {content};
parameterMap.put("content",contentArr2);
}
}
}
Object contentStrObj = parameterMap.get("contentStr");
String contentStr = "";
if(contentStrObj!=null){
String[] contentStrArr = (String[])contentStrObj;
if(contentStrArr.length==1){
contentStr = contentStrArr[0];
if(contentStr.length() > 100){
contentStr = contentStr.substring(0,100) + "......";
String[] contentStrArr2 = {contentStr};
parameterMap.put("contentStr",contentStrArr2);
}
}
}
params = JSON.toJSONString(parameterMap);
}else{
params = "上传的文件";
}
//*========控制台输出=========*//
logger.debug("==============前置通知开始==============");
logger.debug("请求方法" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName()));
logger.debug("方法描述:" + methodDes);
logger.debug("请求人:"+userCode);
logger.debug("请求ip:"+ UserActionLogUtil.getIP());
//*========数据库日志=========*//
UserActionLog action = new UserActionLog();
action.setLogInfo(methodDes);
action.setMethodDescription(methodDes);
action.setParams(params);
action.setMethod(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
action.setLogType(LogType.ControllerLog.getValue());
action.setIp(UserActionLogUtil.getIP());
action.setUserCode(userCode);
action.setCreateTime(new Date());
//保存数据库
userActionLogService.addUserActionLog(action);
}catch (Exception e){
//记录本地异常日志
logger.error("==前置通知异常==");
logger.error("异常信息:{}",e.getMessage());
}
}
/**
* @Description 异常通知 用于拦截service层记录异常日志
* @date 2018年9月3日 下午5:43
*/
@AfterThrowing(pointcut = "serviceAspect()",throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint,Throwable e){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
//读取session中的用户
String userCode = SessionHelper.getLoginUserCode();
//获取请求ip
String ip = UserActionLogUtil.getIP();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//获取用户请求方法的参数并序列化为JSON格式字符串
// String params = getParams(joinPoint,method);
String params = "";
if (joinPoint.getArgs()!=null&&joinPoint.getArgs().length>0){
for (int i = 0; i < joinPoint.getArgs().length; i++) {
params+= JsonUtils.objectToJson(joinPoint.getArgs()[i])+";";
}
}
try{
/*========控制台输出=========*/
logger.debug("=====异常通知开始=====");
logger.debug("异常代码:" + e.getClass().getName());
logger.debug("异常信息:" + e.getMessage());
logger.debug("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
logger.debug("方法描述:" + getServiceMethodDescription(joinPoint));
logger.debug("请求人:" + userCode);
logger.debug("请求IP:" + ip);
logger.debug("请求参数:" + params);
/*==========数据库日志=========*/
UserActionLog action = new UserActionLog();
action.setParams(params);
action.setMethod(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
action.setMethodDescription(getServiceMethodDescription(joinPoint));
action.setLogInfo(e.getMessage());
action.setLogType(LogType.ServiceLog.getValue());
action.setUserCode(userCode);
action.setIp(ip);
action.setCreateTime(new Date());
//保存到数据库
userActionLogService.addUserActionLog(action);
}catch (Exception ex){
//记录本地异常日志
logger.error("==异常通知异常==");
logger.error("异常信息:{}", ex.getMessage());
}
}
// 获取请求的参数名称
private String getParams(JoinPoint joinPoint, Method method) {
// 请求的方法参数值
Object[] args = joinPoint.getArgs();
// 请求的方法参数名称
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
String params = null;
if (args != null && paramNames != null) {
for (int i = 0; i < args.length; i++) {
params += " " + paramNames[i] + ": " + args[i];
}
}
return params;
}
/**
* @Description 获取注解中对方法的描述信息 用于service层注解
* @date 2018年9月3日 下午5:05
*/
public static String getServiceMethodDescription(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[] methods = targetClass.getMethods();
String description = "";
for (Method method:methods) {
if (method.getName().equals(methodName)){
Class[] clazzs = method.getParameterTypes();
if (clazzs.length==arguments.length){
description = method.getAnnotation(SystemServiceLog.class).description();
break;
}
}
}
return description;
}
/**
* @Description 获取注解中对方法的描述信息 用于Controller层注解
* @date 2018年9月3日 上午12:01
*/
public static String getControllerMethodDescription(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[] methods = targetClass.getMethods();
String description = "";
for (Method method:methods) {
if (method.getName().equals(methodName)){
Class[] clazzs = method.getParameterTypes();
if (clazzs.length==arguments.length){
description = method.getAnnotation(SystemControllerLog.class).description();
break;
}
}
}
return description;
}
}
在该类中实现将方法的参数,描述等存入mysql。
5. 切点注解标注示例
1)切点为使用@SystemControllerLog
注解标注的方法
/**
* 词频统计接口
* @param request
* @return
* @throws UnsupportedEncodingException
*/
@SystemControllerLog(description = "切分词-调用词频统计接口,保存统计结果并显示")
@ApiOperation(value="词频统计接口,保存统计结果并显示", notes="词频统计接口")
@ResponseBody
@PostMapping(value = "/wordFrequency")
public BaseResponse<Object> wordFrequency(HttpServletRequest request,String corpusId, String intervalStr, String contentStr){//,String contentStr
BaseResponse<Object> result = new BaseResponse<>();
String loginUser = SessionHelper.getLoginUserCode();
try {
if(StringUtil.isBlank(contentStr)){
result.setCode(205);
result.setMsg("请不要导入空文件!");
}else{
int id = 0;
if(StringUtil.isNotBlank(corpusId)){
id = Integer.valueOf(corpusId);
}
int interval = 10;
if(StringUtil.isNotBlank(intervalStr)){
interval = Integer.valueOf(intervalStr);
}
String segmentresult = WordsStatistics.wordFrequency(contentStr,"/", "");
WordSegmentCorpus corpus = wordSegmentCorpusService.findById(id);
corpus.setSegmentresult(segmentresult);
corpus.setModifyuser(loginUser);
corpus.setState(5);
int flag = wordSegmentCorpusService.update(corpus);
if(flag>0){
JSONArray jsonArray = JSONObject.parseArray(segmentresult);
JSONObject resultJson = getFormatJosn(interval,jsonArray);
String resultJsonStr = JSONObject.toJSONString(resultJson,SerializerFeature.DisableCircularReferenceDetect);
result.setData(resultJsonStr);
result.setCode(200);
result.setMsg("词频统计数据获取成功!");
}else{
result.setCode(201);
result.setMsg("添加词频统计结果到数据库失败!");
}
}
} catch (Exception e) {
e.printStackTrace();
result.setCode(500);
result.setMsg("服务器错误!");
}
return result;
}
2)切点为使用@SystemServiceLogs
注解标注的方法
/**
* 批量添加词条
* @param list
* @return
*/
@SystemServiceLog(description = "词典管理-批量添加词条Service")
public int batAdd(String tablename,List<KgWordItem> list){
int result = kgWordItemMapper.batAdd(tablename,list);
return result;
}
更多内容请访问:IT源点
注意:本文归作者所有,未经作者允许,不得转载