AOP-注解实现-记录日志到数据库

概念解释

AOPAspect Oriented Programming

  • Aspect:方面
  • Oriented:面向…的
  • Programming编程

之前我对模块化编程的认识,主要是局限在布局结构、目录结构上。比如Vuetemplate模板。
对方法的模块化编程,我之前的认识局限在封装共用的工具方法上。
但没想到函数方法也可以用类似于模板的方式模块化构建。
利用的就是AOP面向切面编程。对应软件设计模式中的“代理模式”,创建对象的代理对象,代理对象的原始操作。
可以在连接点处插入切面逻辑。

  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
  • 目标对象:Target,通知所应用的对象

image.png

AOP配置

导入依赖

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

image.png
本文实现的是,通过注解,记录日志到数据库。
注解的作用是标注哪些方法需要插入切面逻辑。

  • 创建文件夹annotation存放注解接口,用于标注切入点
  • 创建文件夹aop存放的切面类,用于设计切面
  • mapper文件夹下创建OperateLogMapper接口,用于操作数据库
  • pojo文件夹下创建OperateLog实体类,用于映射user_log

自定义Log注解

Log注解的作用仅用于标识。
添加@Retention(RetentionPolicy.RUNTIME)指定注解运行时生效。
添加@Target(ElementType.METHOD)指定注解作用在方法上。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface UserLog {
}

注解的类型是@interface,就是在interface之前加了个@

切入点表达式

可以使用与或非来组合切入点表达式

  • execution(……):根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配
    • execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
  • @annotation(……):根据注解匹配

image.png
本文中是通过注解匹配,也就是添加该注解的方法在执行完毕后会自动添加到日志。

    @Autowired
    private HttpServletRequest request;
    @Autowired
    private OperateLogMapper operateLogMapper;

    @Around("@annotation(top.wushf.server.annotation.UserLog)")
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        String jwt = request.getHeader("token");
        Claims claims;
        Integer operateUser = null;
        try {
            claims = JwtUtils.parseJwt(jwt);
            operateUser = (Integer) claims.get("id");
        } catch (Exception e) {

        }
        LocalDateTime operateTime = LocalDateTime.now();
        String className = joinPoint.getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String methodParams = Arrays.toString(args);
        long begin = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        String returnValue = JSONObject.toJSONString(result);
        long end = System.currentTimeMillis();
        long costTime = end - begin;
        OperateLog operateLog = new OperateLog(null, operateUser, operateTime, className, methodName, methodParams, returnValue, costTime);
        operateLogMapper.insertUserLog(operateLog);
        return result;
    }
}

切入点的方法、类名、参数、返回值都可以通过joinPoint获取。
但操作用户携带在requestheader中。获取request对象,解析jwt令牌。
当前切入点的request对象通过@Autowired注解自动注入。
parseJwt可以会抛出异常,需要放到try{}catch(){}中。

mapper持久层

插入日志需要调用持久层中的insert方法,将日志记录插入到数据库中。

@Mapper
public interface OperateLogMapper {
    @Insert("insert into user_log (operate_user,operate_time,class_name,method_name,method_params,return_value,cost_time)" + "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")
    public void insertUserLog(OperateLog log);
}

之后可能会扩展多个模块,多个业务有各自的日志。所以实现的是insertUserLog。插入到用户操作日志的数据库中。

pojo实体类

好处很多,便于维护、传参等等,不多赘述。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
    private Integer id;
    private Integer operateUser;
    private LocalDateTime operateTime;
    private String className;
    private String methodName;
    private String methodParams;
    private String returnValue;
    private Long costTime;
}

Jwt生成与解析

黑马PPT用的是0.9的老版本,新版本很多方法已经弃用。目前是0.12.5版本。

//私钥
private final static String SECRET = "";
//过期时间,单位:秒
public final static int ACCESS_EXPIRE = 60 * 60 * 24 * 7;
//加密算法
private final static SecureDigestAlgorithm<SecretKey, SecretKey> ALGORITHM = Jwts.SIG.HS256;
//密钥实例
public static final SecretKey KEY = Keys.hmacShaKeyFor(SECRET.getBytes());

public static String generateJwt(Map<String, Object> claims) {
    Date exprireDate = Date.from(Instant.now().plusSeconds(ACCESS_EXPIRE));
    return Jwts.builder().claims(claims).expiration(exprireDate).issuedAt(new Date()).signWith(KEY, ALGORITHM).compact();
}

public static Claims parseJwt(String jwt) {
return Jwts.parser().verifyWith(KEY).build().parseSignedClaims(jwt).getPayload();
}

较之前的版本,现在的版本应该是更统一化了。
创建和解析的过程,都是先创建builder,利用builder创建或解析jwt令牌。

参考

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WuShF.top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值