目录
一、AOP记录用户操作日志
1、pom.xml引入依赖
<!-- AOP切面 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、自定义注解
2-1、新增包名:org.springboot.springboot01.annotation
2-2、在annotation包名下,定义一个方法级别的@Log注解,用于标注需要监控的方法
/**
* 自定义注解:用于监控方法
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String operation() default "";
}
3、创建库表和实体
3-1、创建表
-- 系统操作日志
create table sys_log
(
id int(11) not null AUTO_INCREMENT PRIMARY KEY comment '系统日志ID',
user_name VARCHAR(64) comment '用户名',
operation VARCHAR(64) comment '用户操作',
spend_time decimal(11, 0) comment '响应耗时',
method VARCHAR(512) comment '请求方法',
params VARCHAR(1024) comment '请求参数',
ip VARCHAR(64) comment '操作者IP',
location VARCHAR(64) comment '操作地点',
create_time datetime not null comment '创建时间'
);
3-2、库表对应的实体
// 在org.springboot.springboot01.bean包下
public class SysLog implements Serializable {
private static final long serialVersionUID = -8829671443483555001L;
private Integer id;
private String userName;
private String operation;
private Integer spendTime;
private String method;
private String params;
private String ip;
private String location;
private Date createTime;
// get和set省略......
}
3-3、新增保存日志的方法
// 在org.springboot.springboot01.service包下,新增接口及方法
public interface SysLogService {
/**
* 保存日志
* @param syslog 日志对象
* @return 影响行数
*/
int saveSysLog(SysLog syslog);
}
// 在org.springboot.springboot01.service.impl包下,新增实现类
@Service
public class SysLogServiceImpl implements SysLogService {
@Resource
private SysLogMapper sysLogMapper;
@Override
public int saveSysLog(SysLog syslog) {
return sysLogMapper.insertSysLog(syslog);
}
}
// 在org.springboot.springboot01.mysqldao下,新增操作sql的接口
@Mapper
public interface SysLogMapper {
@Insert({
"insert into sys_log (user_name, operation, spend_time, method, params, ip, location, create_time) ",
"values (",
"#{userName, jdbcType=VARCHAR}, #{operation, jdbcType=VARCHAR}, ",
"#{spendTime, jdbcType=VARCHAR}, #{method, jdbcType=VARCHAR}, ",
"#{params, jdbcType=VARCHAR}, #{ip, jdbcType=VARCHAR}, ",
"#{location, jdbcType=VARCHAR}, #{createTime, jdbcType=TIMESTAMP})"
})
int insertSysLog(SysLog syslog);
}
4、新增切面,监控方法
注:注意,要新增class类,不要Aspect,如下图所示
// 新增org.springboot.springboot01.aspect包名,并在包名下增加切面类
@Aspect
@Component
public class LogAspect {
@Resource
private SysLogService sysLogService;
/**
* @annotation()注解 方式是针对某个注解来定义切面,比如我们对具有 @Log 注解的方法做切面
*/
@Pointcut("@annotation(org.springboot.springboot01.annotation.Log)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) {
log.info("进入around方法");
long begin = System.currentTimeMillis();
// 0. 设置进入切面后,返回的结果
Object result = null;
try {
result = point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
// 1. 获取方法信息
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
// 2. 获取操作信息
LogEndpoint annotation = method.getAnnotation(LogEndpoint.class);
String operation = annotation.operation();
// 3. 获取方法名及全路径
String className = point.getTarget().getClass().getName();
String methodName = signature.getName();
// 4. 获取参数
Object[] args = point.getArgs();
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
StringBuilder params = new StringBuilder();
if (args != null && paramNames != null) {
for (int i = 0; i <args.length; i++) {
if (args[i] instanceof MultipartFile) {
MultipartFile file = (MultipartFile) args[i];
params.append(" ").append(paramNames[i]).append(": ").append(file.getName());
} else {
params.append(" ").append(paramNames[i]).append(": ").append(args[i]);
}
}
}
// 5. 获取ip,并计算耗时
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
long time = System.currentTimeMillis() - begin;
// 6. 保存
SysLog sysLog = new SysLog();
sysLog.setUserName("liyb"); // 先默认,等之后使用到SecurityUtils方法,获取用户信息
sysLog.setOperation(operation);
sysLog.setSpendTime((int) time);
sysLog.setMethod(className + "." + methodName + "()");
sysLog.setParams(params.toString());
sysLog.setIp(IpUtil.getIpAddr(request));
sysLog.setLocation("暂无");
sysLog.setCreateTime(new Date());
sysLogService.saveSysLog(sysLog);
log.info("退出around方法,新增日志成功,详情:{}", JSON.toJSONString(sysLog));
return result;
}
}
// 在org.springboot.springboot01.utils包下,新增IpUtil类
public class IpUtil {
/**
* 获取Ip地址(对代理服务器也适用)
*
* @param request HttpServlet请求
* @return String
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}
5、测试
5-1、增加测试类方法
@RestController
public class TestController {
@Log(operation = "一个参数")
@RequestMapping("/inputParam")
public String inputParams(@RequestParam String param) {
return "输入的参数是:" + param;
}
@Log(operation = "无参数")
@RequestMapping("/notParams")
public String inputParams() {
return "不输入参数";
}
@Log(operation = "多个参数")
@RequestMapping("/inputParams")
public String inputParams(@RequestParam String name, @RequestParam String age) {
return "输入的参数是:" + "name=" + name + ",age=" + age;
}
}
5-2、测试地址
http://127.0.0.1:8080/springboot/inputParam?param=123
http://127.0.0.1:8080/springboot/notParams
http://127.0.0.1:8080/springboot/inputParams?name=zhangsan&age=20