开发环境: springboot + maven + ssm
开发工具: idea
写这篇文章,一方面是方便广大java开发同胞,另一方面也是存储代码,哈哈。
声明自定义注解(jdk1.5之后)
import com.quantum.ferrari.enums.simple.OperationLogLevelEnum; import com.quantum.ferrari.enums.simple.OperationModuleEnum; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 @Target({ElementType.FIELD, ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法 @Documented//说明该注解将被包含在javadoc中 public @interface OpeAnnotation { /** * 模块名称 * * @return */ OperationModuleEnum modelName() default OperationModuleEnum.Default; /** * 类型名称 * * @return */ String typeName() default ""; OperationLogLevelEnum logLevel() default OperationLogLevelEnum.INFO; }
声明切面,使用环绕通知切到有自定义注解的方法
import com.alibaba.fastjson.JSONObject; import com.quantum.ferrari.controller.BaseController; import com.quantum.ferrari.enums.simple.OperationLogLevelEnum; import com.quantum.ferrari.enums.simple.OperationModuleEnum; import com.quantum.ferrari.model.manager.UserModel; import com.quantum.ferrari.model.simple.OperationLogModel; import com.quantum.ferrari.service.RedisService; import com.quantum.ferrari.service.simple.OperationLogService; import com.quantum.ferrari.vo.UserLoginSession; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; 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 javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.util.*; /** * 实现用户crud操作的日志记录 * * @author liujt */ @Aspect @Component public class OperationAspect extends BaseController { @Resource private RedisService redisService; @Autowired private OperationLogService operationLogService; @Around("@annotation(opeAnnotation)") public Object doAfterReturning(ProceedingJoinPoint pjp, OpeAnnotation opeAnnotation) { Object proceed = null; try { proceed = pjp.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } //模块 OperationModuleEnum operationModuleEnum = opeAnnotation.modelName(); //操作类型 String typeStr = opeAnnotation.typeName(); //请求参数 JSONObject requestParamsJSON = getRequestParams(); //响应参数 JSONObject responseParamsJSON = getResponseParams(proceed); //结果集 JSONObject resultJSON = getResult(proceed); //获取用户id和用户名称 id 0 名称 1 String[] split = getUserIdAndName().split(","); OperationLogLevelEnum operationLogLevelEnum = opeAnnotation.logLevel(); //饿汉式单例获取操作日志model OperationLogModel ope = OperationLogModelSingleton.getInstance(); ope.setOpeModule(operationModuleEnum); ope.setOpeType(typeStr); ope.setOpeUserid(split[0]); ope.setOpeUsername(split[1]); ope.setOpeReqParam(requestParamsJSON.toJSONString()); ope.setOpeRespParam(responseParamsJSON.toJSONString()); ope.setOpeResult(resultJSON.toJSONString()); ope.setOperationLogLevelEnum(operationLogLevelEnum); operationLogService.saveOperationLog(ope); return proceed; } public String getUserIdAndName() { UserLoginSession userLoginSession = this.getUserLoginSession(); UserModel userModel = userLoginSession.getUserModel(); //用户id和用户名称拼接起来 String userIdAndName = userModel.getId() + "," + userModel.getUserName(); return userIdAndName; } private JSONObject getResponseParams(Object proceed) { JSONObject jsonObject = new JSONObject(); Field[] fields = proceed.getClass().getDeclaredFields(); if (null != fields) { for (int i = 0, len = fields.length; i < len; i++) { // 对于每个属性,获取属性名 String varName = fields[i].getName(); try { // 获取原来的访问控制权限 boolean accessFlag = fields[i].isAccessible(); // 修改访问控制权限 fields[i].setAccessible(true); // 获取在对象f中属性fields[i]对应的对象中的变量 Object o; try { o = fields[i].get(proceed); jsonObject.put(varName, o); /* String jsonObjecttoString = jsonObject.toString(); JSONObject.parseObject(jsonObjecttoString);*/ } catch (IllegalAccessException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } // 恢复访问控制权限 fields[i].setAccessible(accessFlag); } catch (IllegalArgumentException ex) { ex.printStackTrace(); } } } else { jsonObject.put("key", "没有请求参数"); } return jsonObject; } private JSONObject getResult(Object proceed) { JSONObject jsonObject = new JSONObject(); Field[] fields = proceed.getClass().getDeclaredFields(); if (null != fields) { for (int i = 0, len = fields.length; i < len; i++) { // 对于每个属性,获取属性名 String varName = fields[i].getName(); try { // 获取原来的访问控制权限 boolean accessFlag = fields[i].isAccessible(); // 修改访问控制权限 fields[i].setAccessible(true); // 获取在对象f中属性fields[i]对应的对象中的变量 Object o; try { o = fields[i].get(proceed); if (i == fields.length - 1) { jsonObject.put(varName, o); } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } // 恢复访问控制权限 fields[i].setAccessible(accessFlag); } catch (IllegalArgumentException ex) { ex.printStackTrace(); } } } else { jsonObject.put("key", "没有结果集"); } return jsonObject; } public JSONObject getRequestParams() { JSONObject jsonObject = new JSONObject(); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); Enumeration<String> enu = request.getParameterNames(); if (null != enu) { while (enu.hasMoreElements()) { String paraName = (String) enu.nextElement(); jsonObject.put(paraName, request.getParameter(paraName)); } } else { jsonObject.put("key", "没有传入参数"); } return jsonObject; } }
大致就是这样,需要注意的是,在记录操作日志的时候,要使用多线程。使用多线程原因有两点:1.提高运行效率 2.异步添加操作日志。第二点比较重要,也就是说无论我在记录操作日志的时候是否报错,都不能干扰项目的操作。
IDEA上使用,引入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>