网站标题字数,营销手段有哪些,禁止指定ip访问网站,企业门户网站制作教程Auditing 及其事件详解
Auditing 翻译过来是审计和审核#xff0c;Spring 的优秀之处在于帮我们想到了很多繁琐事情的解决方案#xff0c;我们在实际的业务系统中#xff0c;针对一张表的操作大部分是需要记录谁什么时间创建的#xff0c;谁什么时间修改的#xff0c;并且… Auditing 及其事件详解
Auditing 翻译过来是审计和审核Spring 的优秀之处在于帮我们想到了很多繁琐事情的解决方案我们在实际的业务系统中针对一张表的操作大部分是需要记录谁什么时间创建的谁什么时间修改的并且能让我们方便的记录操作日志。Spring Data JPA 为我们提供了审计功能的架构实现提供了四个注解专门解决这件事情
CreatedBy 哪个用户创建的。CreatedDate 创建的时间。LastModifiedBy 修改实体的用户。LastModifiedDate 最后一次修改时间。
Auditing 如何配置
我们以一个快速的例子看看它是怎么配置生效的。
1先新建一个 EntityUserCustomerEntity 里面的写法如下。
Entity
Table(name user_customer, schema test, catalog )
EntityListeners(AuditingEntityListener.class)
public class UserCustomerEntity {IdColumn(name id, nullable false)
GeneratedValue(strategy GenerationType.IDENTITY)private Integer id;CreatedDateColumn(name create_time, nullable true)private Date createTime;CreatedByColumn(name create_user_id, nullable true)private Integer createUserId;LastModifiedByColumn(name last_modified_user_id, nullable true)private Integer lastModifiedUserId;LastModifiedDateColumn(name last_modified_time, nullable true)private Date lastModifiedTime;Column(name customer_name, nullable true, length 50)private String customerName;Column(name customer_email, nullable true, length 50)private String customerEmail;
......
}
Entity 实体中我们需要做两点
相应的字段添加 CreatedBy、CreatedDate、LastModifiedBy and LastModifiedDate注解。增加 EntityListeners(AuditingEntityListener.class)。
2实现 AuditorAware 接口告诉 JPA 当前的用户是谁。
实现 AuditorAware 接口实现 getCurrentAuditor 方法返回一个 Integer 的 user ID。以下代码介绍了两种做法
public class MyAuditorAware implements AuditorAwareInteger {/*** Returns the current auditor of the application.* return the current auditor*/Overridepublic Integer getCurrentAuditor() {
// 第一种方式如果我们集成了spring的Security我们直接通过如下方法即可获得当前请求的用户ID.
// Authentication authentication SecurityContextHolder.getContext().getAuthentication();
// if (authentication null || !authentication.isAuthenticated()) {
// return null;
// }
// return ((LoginUserInfo) authentication.getPrincipal()).getUser().getId();//第二种方式通过request里面取或者session里面取ServletRequestAttributes servletRequestAttributes
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();return (Integer) servletRequestAttributes.getRequest().getSession().getAttribute(userId);}
}
而 AuditorAware 的源码如下
public interface AuditorAwareT {T getCurrentAuditor();
}
通过实现 AuditorAware 接口的 getCurrentAuditor() 方法告诉 JPA 当前的用户是谁里面实现方法千差万别作者举例了两种最常见的
通过 Security 取。通过 Request 取。
3通过 EnableJpaAuditing 注解开启 JPA 的 Auditing 功能。
并且告诉应用 AuditorAware 的实现类是谁也就是我们通过 Bean 注解把上面的实现类放到 Spring 的 Bean 管理里面当然了也可以上面的类加上 Component。具体配置方式如下
SpringBootApplication
EnableJpaAuditing
public class QuickStartApplication {public static void main(String[] args) {SpringApplication.run(QuickStartApplication.class, args);}Beanpublic AuditorAwareInteger auditorProvider() {return new MyAuditorAwareImpl();}
}
验证结果如下。
通过以上的三步我们已经完成了 auting 的配置通过 userCustomerRepository.save(new UserCustomerEntity(1,Jack)); 的执行我们看数据库里面的 4 个字段已经给填上去了。
MappedSuperclass
实际工作中我们还会对上面的实体部分进行改进引入 MappedSuperclass 注解我们将 Id、CreatedBy、CreatedDate、LastModifiedBy and LastModifiedDate 抽象到一个公用的基类里面方便公用和形成每个表的字段约束。可以将其放到我们公司的框架代码上对表设计形成统一的强约束。
步骤如下
1改进后我们新增一个 AbstractAuditable 的抽象类
MappedSuperclass
EntityListeners(AuditingEntityListener.class)
public abstract class AbstractAuditable {IdColumn(name id, nullable false)GeneratedValue(strategy GenerationType.IDENTITY)private Integer id;CreatedDateColumn(name create_time, nullable true)private Date createTime;CreatedByColumn(name create_user_id, nullable true)private Integer createUserId;LastModifiedByColumn(name last_modified_user_id, nullable true)private Integer lastModifiedUserId;LastModifiedDateColumn(name last_modified_time, nullable true)private Date lastModifiedTime;
......
}
2而我们每个需要 Auditing 的实体只需要继承 AbstractAuditable 即可。
内容如下
Entity
Table(name user_customer, schema test, catalog )
public class UserCustomerEntity extends AbstractAuditable {Column(name customer_name, nullable true, length 50)private String customerName;Column(name customer_email, nullable true, length 50)private String customerEmail;
......}
Auditing 原理解析
1我们先看一下关键的几个源码的关系图 2AuditingEntityListener 的源码如下
Configurable
public class AuditingEntityListener {private ObjectFactoryAuditingHandler handler;public void setAuditingHandler(ObjectFactoryAuditingHandler auditingHandler) {Assert.notNull(auditingHandler, AuditingHandler must not be null!);this.handler auditingHandler;}//在新增之前通过handler来往我们的Entity里面的auditor的那些字段塞值。PrePersistpublic void touchForCreate(Object target) {if (handler ! null) {handler.getObject().markCreated(target);}}//在更新之前通过handler来往我们的Entity里面的auditor的那些字段塞值。PreUpdatepublic void touchForUpdate(Object target) {if (handler ! null) {handler.getObject().markModified(target);}}
}
3通过调用关系图和 AuditingEntityListener我们其实可以发现以下两点情况
AuditingEntityListener 通过委托设计模式委托 AuditingHandler 进行处理而我们看 AuditingHandler 的源码会发现里面就是根据 ID 和 Version后面介绍来判断我们的对象是新增还是更新从而来更改时间字段和 User 字段。而 User 字段是通过 AuditorAware 的实现类来取的并且 AuditorAware 没有默认实现类只有我们自己的实现类也就是 AuditorAware 的实现类必须我们自己来定义否则启动会报错。AuditingEntityListener 的代码如此简单我们能不能自定义呢答案是肯定的通过 PrePersist、PreUpdate 查看源码得出Java Persistence API 底层又帮我们提供的 Callbacks而这些回调方法用于侦听保存、查询抓取、更新和删除数据库中的数据。注解方式如下Type 描述 PrePersist 新增之前 PreRemove 删除之前 PostPersist 新增之后 PostRemove 删除之后 PreUpdate 更新之前 PostUpdate 更新之后 PostLoad 加载后 注意这个方法都是同步机制一但报错将会影响所有底层代码执行。在实际工作中实现这些方法的时候方法体里面开启异步线程或者消息队列来异步处理日志或者更繁重的工作。
Listener 事件的扩展
自定义 EntityListener
随着 DDD 的设计模式逐渐被大家认可和热捧JPA 通过这种 Listener 这种机制可以很好的实现事件分离、状体分离。假如订单的状态变化可能对我们来说比较重要我们需要定一个类去监听订单状态变更通知相应的逻辑代码各自去干各自的活。
1新增一个 OrderStatusAuditListener 类在相应的操作上添加 Callbacks 注解。
public class OrderStatusAuditListener {PostPersistprivate void postPersist(OrderEntiy entity) {//当更新的时候做一些逻辑判断及其事件通知。}PostRemoveprivate void PostRemove(OrderEntiy entity) {//当删除的时候做一些逻辑判断。}PostUpdate private void PostUpdate(OrderEntiy entity) {//当更新的时候// entity.getOrderStatus()做一些逻辑判断}
}
2我们的订单实体变化如下
Entity
Table(orders)
EntityListeners({AuditingEntityListener.class, OrderStatusAuditListener.class})
public class OrderEntity extends AbstractAuditable{Enumerated(EnumType.STRING)Column(order_status)private OrderStatusEnum orderStatus;......
}
即可完成自定义 EntityListener。
实际工作记录操作日志的实例
public class ActionsLogsAuditListener {private static final Logger logger LoggerFactory.getLogger(ActionsLogsAuditListener.class);PostLoadprivate void postLoad(Object entity) {this.notice(entity, OperateType.load);}PostPersistprivate void postPersist(Object entity) {this.notice(entity, OperateType.create);}PostRemoveprivate void PostRemove(Object entity) {this.notice(entity, OperateType.remove);}PostUpdateprivate void PostUpdate(Object entity) {this.notice(entity, OperateType.update);}private void notice(Object entity, OperateType type) {logger.info({} 执行了 {} 操作, entity, type.getDescription());//我们通过active mq 异步发出消息处理事件ActiveMqEventManager.notice(new ActiveMqEvent(type, entity));}enum OperateType {create(创建), remove(删除),update(修改),load(查询);private final String description;OperateType(String description) {this.descriptiondescription;}public String getDescription() {return description;}}
}
我们通过自定义的 ActionsLogsAuditListener 来监听我们要处理日志的实体然后将事件变更通过消息队列进行异步处理这样就可以完全解耦了。当然了这里我们解耦的方式也可以通过 Spring 的事件机制进行解决。通过工作中的此示例来帮助大家更好的理解 Audit 的机制顺便说一下处理操作的日志的正确思路记录当前真实发生的数据和状态及其时间即可具体变化了什么那是在业务展示层面上要做的事情这里没有必要做比对的事情记住这一点之后就会让你的日志处理实现机制豁然明朗变得容易许多。