使用SpringBoot记录用户操作日志

在工作中我们可能会遇到一个需求,就是记录用户的操作信息,接下来使用spring的aop特性实现这一需求

一、首先引入我们必要的一些依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- aop依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<!-- oracle驱动 -->
<dependency>
   <groupId>com.oracle</groupId>
   <artifactId>ojdbc6</artifactId>
   <version>6.0</version>
</dependency>

<!-- druid数据源驱动 -->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.1.6</version>
</dependency>

二、自定义一个注解,用于标注需要监控的方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

三、在数据库中创建一张表,用来保存用户的操作日志

字段可以有  用户名  用户操作  请求方法  请求参数  响应时间  IP地址和创建时间

CREATE TABLE "SCOTT"."SYS_LOG" (
   "ID" NUMBER(20) NOT NULL ,
   "USERNAME" VARCHAR2(50 BYTE) NULL ,
   "OPERATION" VARCHAR2(50 BYTE) NULL ,
   "TIME" NUMBER(11) NULL ,
   "METHOD" VARCHAR2(200 BYTE) NULL ,
   "PARAMS" VARCHAR2(500 BYTE) NULL ,
   "IP" VARCHAR2(64 BYTE) NULL ,
   "CREATE_TIME" DATE NULL 
);

COMMENT ON COLUMN "SCOTT"."SYS_LOG"."USERNAME" IS '用户名';
COMMENT ON COLUMN "SCOTT"."SYS_LOG"."OPERATION" IS '用户操作';
COMMENT ON COLUMN "SCOTT"."SYS_LOG"."TIME" IS '响应时间';
COMMENT ON COLUMN "SCOTT"."SYS_LOG"."METHOD" IS '请求方法';
COMMENT ON COLUMN "SCOTT"."SYS_LOG"."PARAMS" IS '请求参数';
COMMENT ON COLUMN "SCOTT"."SYS_LOG"."IP" IS 'IP地址';
COMMENT ON COLUMN "SCOTT"."SYS_LOG"."CREATE_TIME" IS '创建时间';

CREATE SEQUENCE seq_sys_log START WITH 1 INCREMENT BY 1;

四、创建数据库表中对应的实体类

public class SysLog implements Serializable{

    private static final long serialVersionUID = -6309732882044872298L;
    
    private Integer id;
	private String username;
	private String operation;
	private Integer time;
	private String method;
	private String params;
	private String ip;
	private Date createTime;

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getOperation() {
		return operation;
	}
	public void setOperation(String operation) {
		this.operation = operation;
	}
	public Integer getTime() {
		return time;
	}
	public void setTime(Integer time) {
		this.time = time;
	}
	public String getMethod() {
		return method;
	}
	public void setMethod(String method) {
		this.method = method;
	}
	public String getParams() {
		return params;
	}
	public void setParams(String params) {
		this.params = params;
	}
	public String getIp() {
		return ip;
	}
	public void setIp(String ip) {
		this.ip = ip;
	}
	public Date getCreateTime() {
		return createTime;
	}
	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}
    
}

 五、新增保存日志的方法

public interface SysLogDao {
    void saveSysLog(SysLog syslog);
}

然后是其实现方法

@Repository
public class SysLogDaoImp implements SysLogDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Override
    public void saveSysLog(SysLog syslog) {
        StringBuffer sql = new StringBuffer("insert into sys_log ");
        sql.append("(id,username,operation,time,method,params,ip,create_time) ");
        sql.append("values(seq_sys_log.nextval,:username,:operation,:time,:method,");
        sql.append(":params,:ip,:createTime)");
        
        NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate(this.jdbcTemplate.getDataSource());
        npjt.update(sql.toString(), new BeanPropertySqlParameterSource(syslog));
    }
}

六、定义切面和切点,定义一个LogAspect类,使用@Aspect标注让其成为一个切面,切点为使用@Log注解标注的方法,使用@Around环绕通知

@Aspect
@Component
public class LogAspect {

    @Autowired
    private SysLogDao sysLogDao;
    
    @Pointcut("@annotation(com.springboot.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();
        SysLog sysLog = new SysLog();
        Log logAnnotation = method.getAnnotation(Log.class);
        if (logAnnotation != null) {
            // 注解上的描述
            sysLog.setOperation(logAnnotation.value());
        }
        // 请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLog.setMethod(className + "." + methodName + "()");
        // 请求的方法参数值
        Object[] args = joinPoint.getArgs();
        // 请求的方法参数名称
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNames = u.getParameterNames(method);
        if (args != null && paramNames != null) {
            String params = "";
            for (int i = 0; i < args.length; i++) {
                params += "  " + paramNames[i] + ": " + args[i];
            }
            sysLog.setParams(params);
        }
        // 获取request
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        // 设置IP地址
        sysLog.setIp(IPUtils.getIpAddr(request));
        // 模拟一个用户名
        sysLog.setUsername("mrbird");
        sysLog.setTime((int) time);
        sysLog.setCreateTime(new Date());
        // 保存系统日志
        sysLogDao.saveSysLog(sysLog);
    }
}

七、测试

@RestController
public class TestController {

    @Log("执行方法一")
    @GetMapping("/one")
    public void methodOne(String name) { }
    
    @Log("执行方法二")
    @GetMapping("/two")
    public void methodTwo() throws InterruptedException {
        Thread.sleep(2000);
    }
    
    @Log("执行方法三")
    @GetMapping("/three")
    public void methodThree(String name, String age) { }
}

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP(面向切面编程)是 Spring 框架的一个模块,它提供了一种在程序运行期间动态代理类的机制,以便能够在不修改原始代码的情况下,实现诸如日志记录、性能统计、安全控制等横向关注点的功能。Spring AOP 基于代理模式实现,通过代理对象包装目标对象,从而实现在目标方法执行前、执行后、执行异常、执行返回时等时刻,插入一些额外的逻辑。下面是 Spring AOP 的基本操作: 1. 定义切面类:切面类包含了一系列的通知(Advice),通知描述了切面类在何时执行某个操作。 2. 定义切入点:切入点指定了哪些类的哪些方法会被切面类的通知所拦截。 3. 定义通知:通知是切面类中的方法,它描述了切面类在拦截到切入点处的程序执行时,应该执行的操作Spring AOP 提供了五种类型的通知: - Before:在目标方法执行前执行通知。 - After:在目标方法执行后执行通知。 - AfterReturning:在目标方法执行后返回结果时执行通知。 - AfterThrowing:在目标方法抛出异常时执行通知。 - Around:在目标方法执行前后都执行通知。 4. 配置切面:切面需要在 Spring 的配置文件中进行配置,以便将切面类与切入点关联。 5. 启用 AOP:启用 AOP 需要在 Spring 的配置文件中配置 <aop:aspectj-autoproxy /> 标签。 通过以上基本操作,就可以在 Spring使用 AOP 实现对目标对象的拦截和增强。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值