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 注解即可。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!