Spring Data JPA 实现用户信息、时间信息的自动注入

原理解析如下:

  • 首先,我们创建一个Entity类,开启监听类 AuditingEntityListener ,并添加用户属性和时间属性以及对应的注解进行标记
  • 进入 AuditingEntityListener 类可以看到被 @PrePersist 注解 和 @PreUpdate 注解标记的两个方法 touchForCreate(进行持久化新增前的处理)touchForUpdate(持久化修改前的处理)
  • 跟踪 object.markCreated(target) 或 object.markModified(target) 方法进入 AuditingHandler 类,里面调用 touch 方法;
  • 跟踪进入 this.touch 方法,会进行用户信息和时间信息的处理,并返回处理后的bean对象 ;
    • 处理用户信息时,执行 this.touchAuditor 方法,在方法内部调用了接口 AuditorAware 的 getCurrentAuditor() 方法,这个方法需要我们进行重写(获取用户信息例如id),拿到用户id后,判定是新增还是修改,然后注入用户id到 createdBy 或 lastModifiedBy 属性。因为 modifyOnCreation 设置为了 true,因此新增时也会注入用户id 到 lastModifiedBy 属性。
    • 处理时间信息时,执行 this.touchDate 方法,方法内部会调用接口 DateTimeProvider 的getNow() 方法获取时间,该方法已经被 CurrentDateTimeProvider 类重写,无需我们重写。然后进行时间注入(注入逻辑与用户信息一致)。

源码详情如下:

首先,我们**创建一个Entity类,开启监听类 AuditingEntityListener ,并添加用户属性和时间属性以及对应的注解进行标记。(这是我们要做的)**必要代码如下:

//开启 AuditingEntityListener 类的监听
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseAuditingEntity implements Serializable {
  
  private static final long serialVersionUID = 1L;
  
  //标记为创建者
  @CreatedBy
  private String createdBy;
  
  //标记为创建时间
  @CreatedDate
  private Instant createdDate = Instant.now();
  
  //标记为修改者
  @LastModifiedBy
  private String lastModifiedBy;
  
  //标记为修改时间
  @LastModifiedDate
  private Instant lastModifiedDate = Instant.now();
  
}

进入 AuditingEntityListener 类可以看到被 @PrePersist 注解 和 @PreUpdate 注解标记的两个方法 touchForCreate(进行持久化新增前的处理) 和 touchForUpdate(持久化修改前的处理),源码如下:

@PrePersist
public void touchForCreate(Object target) {
    Assert.notNull(target, "Entity must not be null!");
    if (this.handler != null) {
        AuditingHandler object = (AuditingHandler)this.handler.getObject();
        if (object != null) {
            //处理新增信息
            object.markCreated(target);
        }
    }
}

@PreUpdate
public void touchForUpdate(Object target) {
    Assert.notNull(target, "Entity must not be null!");
    if (this.handler != null) {
        AuditingHandler object = (AuditingHandler)this.handler.getObject();
        if (object != null) {
            //处理修改信息
            object.markModified(target);
        }
    }
}

跟踪 object.markCreated(target) 或 object.markModified(target) 方法进入 AuditingHandler 类,里面调用 touch 方法,源码如下:

public <T> T markCreated(T source) {
    Assert.notNull(source, "Entity must not be null!");
    //返回处理后的对象,参数 source 可以看做需要处理的对象,参数true表示新增,false表示修改
    return this.touch(source, true);
}

public <T> T markModified(T source) {
    Assert.notNull(source, "Entity must not be null!");
    return this.touch(source, false);
}

跟踪进入 this.touch 方法,会进行用户信息和时间信息的处理,并返回处理后的bean对象 ,源码如下:

private <T> T touch(T target, boolean isNew) {
    //简单理解为 需要处理的对象吧
    Optional<AuditableBeanWrapper<T>> wrapper = this.factory.getBeanWrapperFor(target);
    return wrapper.map((it) -> {
        //进行用户信息的处理
        Optional<Object> auditor = this.touchAuditor(it, isNew);
        //进行时间信息的处理,dateTimeForNow 被设置为了 true,则会进行时间信息的注入
        Optional<TemporalAccessor> now = this.dateTimeForNow ? this.touchDate(it, isNew) : Optional.empty();
        if (LOGGER.isDebugEnabled()) {
            Object defaultedNow = now.map(Object::toString).orElse("not set");
            Object defaultedAuditor = auditor.map(Object::toString).orElse("unknown");
            LOGGER.debug("Touched {} - Last modification at {} by {}", new Object[]{target, defaultedNow, defaultedAuditor});
        }
        //返回处理后的bean对象(对象已经完成属性注入)
        return it.getBean();
    }).orElse(target);
}

处理用户信息时,执行 this.touchAuditor 方法,在方法内部调用了接口 AuditorAware 的 getCurrentAuditor() 方法,这个方法需要我们进行重写(获取用户信息例如id),拿到用户id后,判定是新增还是修改,然后注入用户id到 createdBy 或 lastModifiedBy 属性。因为 modifyOnCreation 设置为了 true,因此新增时也会注入用户id 到 lastModifiedBy 属性。源码如下:

private Optional<Object> touchAuditor(AuditableBeanWrapper<?> wrapper, boolean isNew) {
    Assert.notNull(wrapper, "AuditableBeanWrapper must not be null!");
    return this.auditorAware.map((it) -> {
        //获取用户id,getCurrentAuditor 方法需要重写
        Optional<?> auditor = it.getCurrentAuditor();
        Assert.notNull(auditor, () -> {
            return String.format("Auditor must not be null! Returned by: %s!", AopUtils.getTargetClass(it));
        });
        auditor.filter((__) -> {
            return isNew;
        }).ifPresent((foo) -> {
            wrapper.setCreatedBy(foo);
        });
        auditor.filter((__) -> {
            //modifyOnCreation 被设置为了true,新增时也会设置lastModifiedBy
            return !isNew || this.modifyOnCreation;
        }).ifPresent((foo) -> {
            wrapper.setLastModifiedBy(foo);
        });
        return auditor;
    });
}

AuditorAware 的实现类如下:(这是我们要进行的操作)

@Component
public class SpringSecurityAuditorAware implements AuditorAware<String> {
    @Override
    public Optional<String> getCurrentAuditor() {
      //自定义用户id的获取逻辑并返回
      String userId = ...;
      return Optional.of(userId);
}

处理时间信息时,执行 this.touchDate 方法,方法内部会调用接口 DateTimeProvider 的getNow() 方法获取时间,该方法已经被 CurrentDateTimeProvider 类重写,无需我们重写。然后进行时间注入(注入逻辑与用户信息一致)。源码如下:

private Optional<TemporalAccessor> touchDate(AuditableBeanWrapper<?> wrapper, boolean isNew) {
    Assert.notNull(wrapper, "AuditableBeanWrapper must not be null!");
    //获取时间信息,getNow 方法无需重写
    Optional<TemporalAccessor> now = this.dateTimeProvider.getNow();
    Assert.notNull(now, () -> {
        return String.format("Now must not be null! Returned by: %s!", this.dateTimeProvider.getClass());
    });
    now.filter((__) -> {
        return isNew;
    }).ifPresent((it) -> {
        wrapper.setCreatedDate(it);
    });
    now.filter((__) -> {
        return !isNew || this.modifyOnCreation;
    }).ifPresent((it) -> {
        wrapper.setLastModifiedDate(it);
    });
    return now;
}

至此,整个自动注入动作完成了!!!
如果还想做其他的新增或修改的注入处理,可以在Entity类下新增处理方法,在方法上添加 @PrePersist 注解 和 @PreUpdate 注解即可。

  • 38
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值