切面编程(操作日志)
自定义注解
import java.lang.annotation.*;
/**
* @author wzm
*/
//注解会在class中存在,运行时可通过反射获取
@Retention(RetentionPolicy.RUNTIME)
//目标是方法
@Target({ElementType.METHOD, ElementType.PARAMETER})
//表示是否将注解信息添加在java文档中
@Documented
public @interface Log {
//这个用户所做的是什么操作
String value() default "未标注操作";
/**
* 1 2 3 4 / 增 删 改 查
* 0 未设置
* @return int
*/
int type() default 0;
}
切面类
SysLogAspect.java
import com.thyc.fabric.annotation.Log;
import com.thyc.fabric.common.utils.IpUtil;
import com.thyc.fabric.entity.business.OperationLogger;
import com.thyc.fabric.service.business.OperationLoggerService;
import com.thyc.fabric.shiro.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
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.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author wzm
*/
@Aspect
@Component
@Slf4j
public class SysLogAspect {
@Autowired
private OperationLoggerService operationLoggerService;
public SysLogAspect() {
}
private static OperationLogger operationLogger = new OperationLogger();
/**
* 注解切点
*/
@Pointcut("@annotation(com.thyc.fabric.annotation.Log)")
public void pointCut() {
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint pjd) throws Throwable {
Object object = null;
try {
// 开始执行时间
long starTime = System.currentTimeMillis();
object = pjd.proceed();
// 获取request
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = null;
if (servletRequestAttributes != null) {
request = servletRequestAttributes.getRequest();
}
if (request != null) {
// 通过token解析操作用户
String token = (String) SecurityUtils.getSubject().getPrincipal();
if (token != null) {
String username = JWTUtil.getUsername(token);
operationLogger.setCreator(username);
}
// 获取访问端(web android ios)
operationLogger.setAccessType(request.getHeader("accessType"));
}
MethodSignature methodName1 = (MethodSignature) pjd.getSignature();
Method method = methodName1.getMethod();
//method方法上面的注解
Annotation[] annotations = method.getAnnotations();
boolean flag = false;
for (Annotation annotation : annotations) {
if (Log.class.equals(annotation.annotationType())) {
flag = true;
}
}
if (flag) {
// 类名
String className = pjd.getTarget().getClass().getName();
operationLogger.setLogClassName(className);
// 操作描述
String operation = method.getAnnotation(Log.class).value();
operationLogger.setOperationContent(operation);
// 操作类型
Integer operationType = method.getAnnotation(Log.class).type();
operationLogger.setOperationType(operationType);
// ip地址
String ip = IpUtil.getIp(request);
operationLogger.setRemoteIpAddress(ip);
// 城市信息
String cityInfo = IpUtil.getCityInfo(ip);
operationLogger.setCityInfo(cityInfo);
// 设置方法名
operationLogger.setLogMethodName(method.getName());
Signature signature = pjd.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
String[] argNames = methodSignature.getParameterNames();
// 如果有参数
if (argNames.length > 0) {
// 参数名列表
List<String> paramNameList =
new ArrayList<>(Arrays.asList(methodSignature.getParameterNames()).subList(0, argNames.length));
// 参数类型
List<String> paramTypeList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
String paramType = parameter.getType().getName();
paramTypeList.add(paramType);
}
// 参数的值
List<Object> paramValueList = new ArrayList<>();
Object[] params = pjd.getArgs();
Collections.addAll(paramValueList, params);
operationLogger.setParamsTypeList(paramTypeList.toString());
operationLogger.setParamsNameList(paramNameList.toString());
operationLogger.setParamsValueList(paramValueList.toString());
} else {
// 没有参数的话就都设置为none
operationLogger.setParamsTypeList("none");
operationLogger.setParamsNameList("none");
operationLogger.setParamsValueList("none");
}
}
// 设置异常信息
operationLogger.setExceptionInfo(null);
// 执行时间 = 执行结束时间 - 开始时间 (单位:毫秒)
long execTime = System.currentTimeMillis() - starTime;
operationLogger.setExecTime(String.valueOf(execTime));
} catch (Exception e) {
operationLogger.setExceptionInfo(e.getMessage());
operationLoggerService.addLogger(operationLogger);
throw e;
}
log.info("[" + operationLogger.getRemoteIpAddress() + "][" + operationLogger.getOperationContent() + "]["
+ operationLogger.getLogClassName() + " - " + operationLogger.getLogMethodName() + "]" + " - "
+ (StringUtils.isNotBlank(operationLogger.getExecTime()) ? operationLogger.getExecTime() : "0") + "ms");
// 录入数据库
operationLoggerService.addLogger(operationLogger);
// 执行方法,获取返回参数
return object;
}
}
service层
OperationLoggerService.java
import com.baomidou.mybatisplus.service.IService;
import com.thyc.fabric.common.exception.BaseException;
import com.thyc.fabric.entity.business.OperationLogger;
/**
* 操作日志记录 Service层
*
* @author wzm
* @version 1.0
*/
public interface OperationLoggerService extends IService<OperationLogger> {
/**
* 添加日志(配置了aop切面)
*
* @param operationLogger log
* @throws BaseException e
*/
void addLogger(OperationLogger operationLogger) throws BaseException;
}
OperationLoggerServiceImpl.java
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.thyc.fabric.common.exception.BaseException;
import com.thyc.fabric.dao.business.OperationLoggerMapper;
import com.thyc.fabric.entity.business.OperationLogger;
import com.thyc.fabric.service.business.OperationLoggerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import javax.annotation.Resource;
import java.util.Date;
/**
* 操作日志记录 ServiceImpl层
*
* @author wzm
* @version 1.0
*/
@Service
@Slf4j
public class OperationLoggerServiceImpl extends ServiceImpl<OperationLoggerMapper, OperationLogger> implements OperationLoggerService {
@Resource
private OperationLoggerMapper operationLoggerMapper;
/**
* 添加日志(配置了aop切面)
*
* @param operationLogger 日志对象
*/
@Transactional(rollbackFor = {Exception.class})
@Override
public void addLogger(OperationLogger operationLogger) throws BaseException {
//尚未添加logger实体和dao
try {
operationLogger.setCreateTime(new Date());
int result = operationLoggerMapper.insert(operationLogger);
//操作成功
if (result < 1) {
log.warn("新增日志失败");
}
} catch (Exception e) {
//手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new BaseException(e.getMessage());
}
}
}
Dao层
OperationLoggerMapper.java
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.thyc.fabric.entity.business.OperationLogger;
import com.thyc.fabric.vo.business.LogVO;
import java.util.List;
/**
* @author wzm
*/
public interface OperationLoggerMapper extends BaseMapper<OperationLogger> {
/**
* 查询操作日志
*
* @param type 日志类型
* @param star 开始时间
* @param end 截止时间
* @return list vo
* @author wzh
* @date 2019/9/29 0029 17:53
*/
List<LogVO> queryLogList(Integer type, String star, String end);
}
OperationLoggerMapper.xml
<?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.thyc.fabric.dao.business.OperationLoggerMapper">
<resultMap id="OperationLoggerMap" type="com.thyc.fabric.entity.business.OperationLogger">
<result column="id" property="id" />
<result column="operation_content" property="operationContent" />
<result column="operation_type" property="operationType" />
<result column="log_class_name" property="logClassName" />
<result column="log_method_name" property="logMethodName" />
<result column="params_type_list" property="paramsTypeList" />
<result column="params_name_list" property="paramsNameList" />
<result column="params_value_list" property="paramsValueList" />
<result column="exception_info" property="exceptionInfo" />
<result column="remote_ip_address" property="remoteIpAddress" />
<result column="access_type" property="accessType" />
<result column="exec_time" property="execTime" />
<result column="city_info" property="cityInfo" />
<result column="creator" property="creator" />
<result column="modifier" property="modifier" />
<result column="create_time" property="createTime" />
<result column="modify_time" property="modifyTime" />
<result column="valid" property="valid" />
</resultMap>
<select id="queryLogList" resultType="com.thyc.fabric.vo.business.LogVO">
select
log.id as logId,
u.nick_name as nickName,
log.operation_type as typeId,
log.operation_content as `desc`,
log.remote_ip_address as ip,
log.log_class_name as className,
log.log_method_name as methodName,
log.exception_info as exceptionInfo,
log.create_time as createTime
from
t_operation_logger as log
inner join t_sys_user as u
on u.id = log.user_id
where log.valid = 1 AND u.valid = 1
<if test="type != null">
AND log.operation_type = #{type}
</if>
<if test="type == null">
AND log.operation_type <![CDATA[!=]]> 4
</if>
<if test="star != null and end != null">
AND DATE_FORMAT(log.create_time,'%Y-%m-%d') BETWEEN STR_TO_DATE(#{star},'%Y-%m-%d') AND STR_TO_DATE(#{end},'%Y-%m-%d')
</if>
order by log.create_time desc
</select>
</mapper>
日志实体类
实体类:
import com.baomidou.mybatisplus.annotations.TableField;
import com.thyc.fabric.entity.BaseEntity;
import com.baomidou.mybatisplus.annotations.TableName;
import lombok.Data;
import java.io.Serializable;
/**
*
* @author wzm
*/
@TableName("t_operation_logger")
@Data
public class OperationLogger extends BaseEntity<OperationLogger>{
private static final long serialVersionUID = 1L;
/**
* 操作内容
*/
@TableField("operation_content")
private String operationContent;
/**
* 操作类型( 0-未设置, 1-新增, 2-删除, 3-修改, 4-查询, 5-登录, 6-登出, 7-转账)
*/
@TableField("operation_type")
private Integer operationType;
/**
* 类名
*/
@TableField("log_class_name")
private String logClassName;
/**
* 方法名
*/
@TableField("log_method_name")
private String logMethodName;
/**
* 参数类型列表
*/
@TableField("params_type_list")
private String paramsTypeList;
/**
* 参数名称列表
*/
@TableField("params_name_list")
private String paramsNameList;
/**
* 参数值列表
*/
@TableField("params_value_list")
private String paramsValueList;
/**
* 异常信息
*/
@TableField("exception_info")
private String exceptionInfo;
/**
* 远程IP地址
*/
@TableField("remote_ip_address")
private String remoteIpAddress;
/**
* 访问类型(0-web, 1-android, 2-ios)
*/
@TableField("access_type")
private String accessType;
/**
* 执行时间
*/
@TableField("exec_time")
private String execTime;
/**
* 城市信息
*/
@TableField("city_info")
private String cityInfo;
@Override
protected Serializable pkVal() {
return this.id;
}
}
控制层
注解加到方法是以记录该接口的行为。
@PostMapping("oss_file_uploader")
@Log(value = "oss文件上传", type = 1)
@ApiOperation(value = "oss文件上传", notes = "文件上传接口", produces = "application/json")
public ApiResult ossUploadFile(@RequestParam(value = "file") MultipartFile file) throws BusinessException {
return commonService.ossUploadFile(file);
}