Spring Boot 使用AOP切面编程记录用户操作日志,并保存到数据库
- 1、pom.xml导入依赖
- 2、实体类(com.stodgy.bean)
- 3、SystemLogDao Dao层(com.stodgy.dao)
- 4、新建对应dao层的mapper.xml文件
- 5、Service (com.stodgy.service)
- 6、ServiceImpl (com.stodgy.service.impl)
- 前面这些基本只是一些准备工作,下面步入主题
- 7、Log (com.stodgy.annotation)
- 8、工具类 IPUtils 和 HttpContextUtils (com.stodgy.common)
- 9、LogAspect(com.stodgy.aspect)
- 10、在需要监控的控制器上(com.stodgy.controller)
- 最后,一起看看效果吧
前言:最近想做一个用户日志记录,几经寻找,发现都比较复杂,有的代码不详细,根本看不懂,我觉得这个方法比较简单,所以分享给大家
在Spring框架中使用AOP配合自定义注解监控用户操作。首先搭建一个基本的Spring Boot Web环境开启
1、pom.xml导入依赖
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入druid依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.14</version>
</dependency>
<!--Spring Boot Mybatis 依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<!-- MySQL 连接驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
<!-- 这里我使用lombok生成getting和setting等方法,如果没安装插件的,可以不需要加这个 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
<!-- aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、实体类(com.stodgy.bean)
这里我使用lombok插件快速生成的getting和setting,如果没装插件的要记得手动加上getting和setting方法
@Getter
@Setter
@ToString
@Data
//系统日志管理
public class SystemLog {
/**
* 主键,标识列,自动生成
*/
private Integer logid;
/**
* 关联员工表主键
*/
private String empname;
/**
* Ip地址
*/
private String ipaddr;
/**
* 操作时间
*/
private Date optime;
/**
* 操作的内容
*/
private String params;
/**
* 信息
*/
private String msg;
}
3、SystemLogDao Dao层(com.stodgy.dao)
@Repository
@Transactional
public interface SystemLogDao {
//保存日志信息到数据库
int insert(SystemLog systemLog);
}
4、新建对应dao层的mapper.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.stodgy.dao.SystemLogDao">
<insert id="insert" keyColumn="logId" keyProperty="logid" parameterType="com.stodgy.bean.SystemLog" useGeneratedKeys="true">
<!--@mbg.generated-->
insert into systemLog (empName, ipAddr, optime,
`params`, msg)
values (#{empname,jdbcType=VARCHAR}, #{ipaddr,jdbcType=VARCHAR}, #{optime,jdbcType=TIMESTAMP},
#{params,jdbcType=VARCHAR}, #{msg,jdbcType=VARCHAR})
</insert>
</mapper>
5、Service (com.stodgy.service)
public interface SystemLogService {
int insert(SystemLog systemLog);
}
6、ServiceImpl (com.stodgy.service.impl)
@Service
public class SystemLogServiceImpl implements SystemLogService {
@Resource
private SystemLogDao sd;
@Override
public int insert(SystemLog systemLog) {
return sd.insert(systemLog);
}
}
前面这些基本只是一些准备工作,下面步入主题
7、Log (com.stodgy.annotation)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
8、工具类 IPUtils 和 HttpContextUtils (com.stodgy.common)
IPUtils
public class IPUtils {
/**
* 获取IP地址
*
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
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.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}
HttpContextUtils
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}
9、LogAspect(com.stodgy.aspect)
@Aspect
@Component
public class LogAspect {
@Autowired
private SystemLogDao sd;
@Pointcut("@annotation(com.ht.annotation.Log)")
public void pointcut() { }
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) {
Object result = null;
long beginTime = System.currentTimeMillis();
try {
// 执行方法
result = point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
// 执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveLog(point, time);
return result;
}
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SystemLog systemLog = new SystemLog();
Log logAnnotation = method.getAnnotation(Log.class);
if (logAnnotation != null) {
//获取注解@Log在controller上的描述
systemLog.setMsg(logAnnotation.value());
}
// 获取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//获取session
HttpSession session = request.getSession();
// 设置IP地址
systemLog.setIpaddr(IPUtils.getIpAddr(request));
//这里我是获取登录的用户,我已将登录的用户的信息放进了session
//获取session的用户
Emp emp = (Emp)session.getAttribute("emp");
systemLog.setEmpname(emp.getEmpname());
//设置登录时间
systemLog.setOptime(new Date());
//设置登录参数
systemLog.setParams(emp.toString());
// 保存系统日志
sd.insert(systemLog);
}
}
10、在需要监控的控制器上(com.stodgy.controller)
这里我已登录员工为例子
@Controller
@RequestMapping("/login")
public class LoginController {
@Resource
private LoginService ls;
//在每个方法前加,当访问这个方法的时候,就会自动去保存你已经设置好的日志信息
@Log("登录系统")
//查询是否该员工账号
@RequestMapping("/isnEmp")
@ResponseBody
public Map isnEmp(Emp emp, HttpSession session){
Emp login = ls.login(emp);
Map map = new HashMap();
if (login!=null){
session.setAttribute("emp",login);
map.put("i",1);
}
return map;
}
//退出登录
@RequestMapping("/out")
public String out(HttpSession session){
session.removeAttribute("emp");
return "login";
}
}