前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AOP-注解实现-记录日志到数据库

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

作者头像
WuShF
发布2024-03-10 08:06:48
1070
发布2024-03-10 08:06:48
举报
文章被收录于专栏:笔记分享笔记分享

概念解释

AOPAspect Oriented Programming

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

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

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

AOP配置

导入依赖

代码语言:javascript
复制
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

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

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

自定义Log注解

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

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface UserLog {
}

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

切入点表达式

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

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

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

代码语言:javascript
复制
    @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方法,将日志记录插入到数据库中。

代码语言:javascript
复制
@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实体类

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

代码语言:javascript
复制
@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版本。

代码语言:javascript
复制
//私钥
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令牌。

参考

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-03-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念解释
  • AOP配置
  • 自定义Log注解
  • 切入点表达式
  • mapper持久层
  • pojo实体类
  • Jwt生成与解析
  • 参考
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档