【源码】Spring Data JPA原理解析之Auditing执行原理

 Spring Data JPA系列

1、SpringBoot集成JPA及基本使用

2、Spring Data JPA Criteria查询、部分字段查询

3、Spring Data JPA数据批量插入、批量更新真的用对了吗

4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

5、Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用

6、【源码】Spring Data JPA原理解析之Repository的自动注入(一)

7、【源码】Spring Data JPA原理解析之Repository的自动注入(二)

8、【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

9、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(一)

10、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

11、【源码】Spring Data JPA原理解析之Repository自定义方法添加@Query注解的执行原理

12、【源码】SpringBoot事务注册原理

13、【源码】Spring Data JPA原理解析之事务注册原理

14、【源码】Spring Data JPA原理解析之事务执行原理

15、【源码】SpringBoot编程式事务使用及执行原理

16、【源码】Spring事务之传播特性的详解

17、【源码】Spring事务之事务失效及原理

18、【源码】Spring Data JPA原理解析之Hibernate EventListener使用及原理

19、【源码】Spring Data JPA原理解析之Auditing执行原理

前言

在Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用-CSDN博客这篇博文中,跟大家分享了JPA中的Auditing审计功能。JPA通过注解的方式,提供了对数据库表中的数据记录自动添加操作人及操作时间,以及自定义监听器,实现实体插入、编辑、删除的监听,添加日志等。

Auditing使用回顾

2.1 在启动类中添加@EnableJpaAuditing注解,开启Auditing;

2.2 在实体类中添加监听器及操作人、操作时间的注解,代码如下:

package com.jingai.jpa.dao.entity;

@Data
@Entity
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
// 添加AuditingEntityListener实体监听
@EntityListeners(AuditingEntityListener.class)
@Table(name = "tb_user")
public class UserEntity {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    private String name;
    private String state;
 
    // 添加审计的注解
    @CreatedBy
    private Long createBy;
    @CreatedDate
    private Date createTime;
    @LastModifiedBy
    private Long modifyBy;
    @LastModifiedDate
    private Date modifyTime;
 
}

2.3 自定义AuditorAware,重写getCurrentAuditor(),返回审计员主键,实现数据记录的创建人、操作人的自动填充;

通过以上三步,即可实现tb_user表中的createBy、createTime、modifyBy、modifyTime四个字段数据的自动填充。详见:

Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用-CSDN博客

Auditing的执行原理

Auditing审计功能提供了对数据库表中的数据记录自动添加操作人及操作时间,其核心实现在AuditingEntityListener。

3.1 AuditingEntityListener

AuditingEntityListener的源码如下:

package org.springframework.data.jpa.domain.support;

@Configurable
public class AuditingEntityListener {

	// AuditingHandler处理器
	private @Nullable ObjectFactory<AuditingHandler> handler;

	public void setAuditingHandler(ObjectFactory<AuditingHandler> auditingHandler) {

		Assert.notNull(auditingHandler, "AuditingHandler must not be null");
		this.handler = auditingHandler;
	}

	/**
	 * 用于设置目标对象的修改和创建日期以及审核程序。该方法添加了@PrePersist注解,在新增时回调
	 */
	@PrePersist
	public void touchForCreate(Object target) {

		Assert.notNull(target, "Entity must not be null");

		if (handler != null) {
			// 获取AuditingHandler的真实对象
			AuditingHandler object = handler.getObject();
			if (object != null) {
				// 标记新创建
				object.markCreated(target);
			}
		}
	}

	/**
	 * 用于设置目标对象的修改和创建日期以及审核程序,方法添加了@PreUpdate注解
	 */
	@PreUpdate
	public void touchForUpdate(Object target) {

		Assert.notNull(target, "Entity must not be null");

		if (handler != null) {
			// 获取AuditingHandler的真实对象
			AuditingHandler object = handler.getObject();
			if (object != null) {
				// 标记被修改
				object.markModified(target);
			}
		}
	}
}

在AuditingEntityListener中,定义了两个方法,分别添加了@PrePersist和@PerUpdate,标记在新增和修改前回调。即AuditingEntityListener也是使用的Hibernate中的EventListener的监听回调注解。详见:

【源码】Spring Data JPA原理解析之Hibernate EventListener使用及原理-CSDN博客

通过上面的博文可知,当执行persist()时,会先执行添加@PrePersist注解的监听器方法,即Auditing中的AuditingEntityListener的touchForCreate()方法会被回调。该方法会判断handler是否为空,如果不为空,则执行handler的markCreated()方法。所以核心实现在handler,而该handler是AuditingHandler对象。

在Auditing的使用中,其中一步是需要添加@EnableJpaAuditing注解,开启Auditing。该注解的作用就在于为AuditingEntityListener的handler赋值。

3.2 EnableJpaAuditing注解

EnableJpaAuditing注解的源码如下:

package org.springframework.data.jpa.repository.config;

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// 自动引入JpaAuditingRegistrar
@Import(JpaAuditingRegistrar.class)
public @interface EnableJpaAuditing {

}

该注解会自动引入JpaAuditingRegistrar。

3.3 JpaAuditingRegistrar

JpaAuditingRegistrar的相关代码如下:

package org.springframework.data.jpa.repository.config;

class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {

	private static final String BEAN_CONFIGURER_ASPECT_CLASS_NAME = "org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect";

	@Override
	protected Class<? extends Annotation> getAnnotation() {
		return EnableJpaAuditing.class;
	}

	@Override
	protected String getAuditingHandlerBeanName() {
		return "jpaAuditingHandler";
	}

	@Override
	public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {

		Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null");
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		// 注册AnnotationBeanConfigurerAspect
		registerBeanConfigurerAspectIfNecessary(registry);
		// 注册审计处理的bean,AuditingHandler
		super.registerBeanDefinitions(annotationMetadata, registry);
		// 注册AuditingBeanFactoryPostProcessor后置处理器
		registerInfrastructureBeanWithId(
				BeanDefinitionBuilder.rootBeanDefinition(AuditingBeanFactoryPostProcessor.class).getRawBeanDefinition(),
				AuditingBeanFactoryPostProcessor.class.getName(), registry);
	}

	/**
	 * 注册AuditingEntityListener监听器
	 */
	@Override
	protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
			BeanDefinitionRegistry registry) {
		// 如果不存在JpaMetamodelMappingContext,注册JpaMetamodelMappingContextFactoryBean
		// MappingContext用来映射实体与数据库之间关系的机制
		if (!registry.containsBeanDefinition(JPA_MAPPING_CONTEXT_BEAN_NAME)) {
			registry.registerBeanDefinition(JPA_MAPPING_CONTEXT_BEAN_NAME, //
					new RootBeanDefinition(JpaMetamodelMappingContextFactoryBean.class));
		}
		// 创建AuditingEntityListener监听器的bean
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(AuditingEntityListener.class);
		// 设置AuditingEntityListener的auditingHandler属性,值为jpaAuditingHandler对象
		builder.addPropertyValue("auditingHandler",
				ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), null));
		// 注册AuditingEntityListener
		registerInfrastructureBeanWithId(builder.getRawBeanDefinition(), AuditingEntityListener.class.getName(), registry);
	}

	// 省略其他
}

JpaAuditingRegistrar实现了ImportBeanDefinitionRegistrar,所以在Spring启动的时候,会执行registerBeanDefinitions()方法。在registerBeanDefinitions()方法中,核心是调用父类的AuditingBeanDefinitionRegistrarSupport的registerBeanDefinitions()方法。

3.4  AuditingBeanDefinitionRegistrarSupport

AuditingBeanDefinitionRegistrarSupport的源码如下:

public abstract class AuditingBeanDefinitionRegistrarSupport implements ImportBeanDefinitionRegistrar {

	private static final String AUDITOR_AWARE = "auditorAware";
	private static final String DATE_TIME_PROVIDER = "dateTimeProvider";
	private static final String MODIFY_ON_CREATE = "modifyOnCreation";
	private static final String SET_DATES = "dateTimeForNow";

	/**
	 * Spring容器会自动执行ImportBeanDefinitionRegistrar.registerBeanDefinitions(),添加AnnotationAuditingConfiguration装配类
	 * @param annotationMetadata
	 * @param registry
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {

		Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null");
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		// 注册审计处理的bean,AuditingHandler
		AbstractBeanDefinition ahbd = registerAuditHandlerBeanDefinition(getConfiguration(annotationMetadata), registry);
		// 注册AuditingEntityListener监听器
		registerAuditListenerBeanDefinition(ahbd, registry);
	}

	/**
	 * 注册审计处理的bean,AuditingHandler
	 * @param configuration AnnotationAuditingConfiguration对象,记录@EnableJpaAuditing的配置信息
	 * @param registry
	 * @return
	 */
	protected AbstractBeanDefinition registerAuditHandlerBeanDefinition(AuditingConfiguration configuration,
			BeanDefinitionRegistry registry) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(configuration, "AuditingConfiguration must not be null");
		// 使用给定AuditingConfiguration中的默认属性配置AuditingHandler的BeanDefinitionBuilder
		BeanDefinitionBuilder builder = getAuditHandlerBeanDefinitionBuilder(configuration);
		postProcess(builder, configuration, registry);
		// 获取AuditingHandler的bean
		AbstractBeanDefinition ahbd = builder.getBeanDefinition();
		registry.registerBeanDefinition(getAuditingHandlerBeanName(), ahbd);
		return ahbd;
	}

	protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration,
			BeanDefinitionRegistry registry) {}


	/**
	 * 使用给定AuditingConfiguration中的默认属性配置AuditingHandler的BeanDefinitionBuilder
	 */
	protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {

		Assert.notNull(configuration, "AuditingConfiguration must not be null");

		return configureDefaultAuditHandlerAttributes(configuration,
				BeanDefinitionBuilder.rootBeanDefinition(AuditingHandler.class));
	}

	/**
	 * 使用给定AuditingConfiguration中的默认属性配置给定的BeanDefinitionBuilder
	 */
	protected BeanDefinitionBuilder configureDefaultAuditHandlerAttributes(AuditingConfiguration configuration,
			BeanDefinitionBuilder builder) {

		if (StringUtils.hasText(configuration.getAuditorAwareRef())) {
			// 创建一个懒加载的目标资源的auditorAwareRef的BeanDefinition对象
			builder.addPropertyValue(AUDITOR_AWARE,
					createLazyInitTargetSourceBeanDefinition(configuration.getAuditorAwareRef()));
		} else {
			builder.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
		}

		// 设置AuditingHandler的父类AuditingHandlerSupport的dateTimeForNow和modifyOnCreation
		builder.addPropertyValue(SET_DATES, configuration.isSetDates());
		builder.addPropertyValue(MODIFY_ON_CREATE, configuration.isModifyOnCreate());

		// 设置AuditingHandler的父类AuditingHandlerSupport的dateTimeProvider
		if (StringUtils.hasText(configuration.getDateTimeProviderRef())) {
			builder.addPropertyReference(DATE_TIME_PROVIDER, configuration.getDateTimeProviderRef());
		} else {
			builder.addPropertyValue(DATE_TIME_PROVIDER, CurrentDateTimeProvider.INSTANCE);
		}

		builder.setRole(AbstractBeanDefinition.ROLE_INFRASTRUCTURE);

		return builder;
	}

	/**
	 * 返回一个AnnotationAuditingConfiguration对象,记录@EnableJpaAuditing的配置信息
	 */
	protected AuditingConfiguration getConfiguration(AnnotationMetadata annotationMetadata) {
		return new AnnotationAuditingConfiguration(annotationMetadata, getAnnotation());
	}

	/**
	 * 在JpaAuditingRegistrar中返回@EnableJpaAuditing
	 * @return
	 */
	protected abstract Class<? extends Annotation> getAnnotation();

	protected abstract void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
			BeanDefinitionRegistry registry);

	protected abstract String getAuditingHandlerBeanName();

	protected void registerInfrastructureBeanWithId(AbstractBeanDefinition definition, String id,
			BeanDefinitionRegistry registry) {

		definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(id, definition);
	}

	/**
	 * 创建一个懒加载的目标资源的auditorAwareRef的BeanDefinition对象
	 */
	private BeanDefinition createLazyInitTargetSourceBeanDefinition(String auditorAwareRef) {

		BeanDefinitionBuilder targetSourceBuilder = rootBeanDefinition(LazyInitTargetSource.class);
		targetSourceBuilder.addPropertyValue("targetBeanName", auditorAwareRef);

		BeanDefinitionBuilder builder = rootBeanDefinition(ProxyFactoryBean.class);
		builder.addPropertyValue("targetSource", targetSourceBuilder.getBeanDefinition());

		return builder.getBeanDefinition();
	}
}

在registerBeanDefinitions()方法中,执行如下:

1)执行registerAuditHandlerBeanDefinition(),通过@EnableJpaAuditing注解的中配置信息,传入AuditHandler中,注册Audit的处理器AuditHandler的bean到Spring容器;

2)执行registerAuditListenerBeanDefinition(),该方法在子类JpaAuditingRegistrar中实现。该方法注册Auditing的监听器AuditingEntityListener,并将1)中的AuditHandler设置给AuditingEntityListener;

3.5 AuditHandler

AuditHandler的源码如下:

package org.springframework.data.auditing;

public class AuditingHandler extends AuditingHandlerSupport implements InitializingBean {

	private static final Log logger = LogFactory.getLog(AuditingHandler.class);

	private Optional<AuditorAware<?>> auditorAware;

	public AuditingHandler(PersistentEntities entities) {

		super(entities);
		Assert.notNull(entities, "PersistentEntities must not be null");

		this.auditorAware = Optional.empty();
	}

	public static AuditingHandler from(MappingContext<?, ?> mappingContext) {
		return new AuditingHandler(PersistentEntities.of(mappingContext));
	}

	/**
	 * 在创建AuditingHandler时,设置了builder.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE),
	 * 所以会将Spring容器中的AuditorAware自动调用该方法进行设置。AuditorAware用户获取当前审计人信息
	 * @param auditorAware
	 */
	public void setAuditorAware(AuditorAware<?> auditorAware) {

		Assert.notNull(auditorAware, "AuditorAware must not be null");
		this.auditorAware = Optional.of(auditorAware);
	}

	/**
	 * 标记实体被创建
	 */
	public <T> T markCreated(T source) {

		Assert.notNull(source, "Entity must not be null");
		// 执行父类的方法
		return markCreated(getAuditor(), source);
	}

	/**
	 * 标记实体被更新
	 */
	public <T> T markModified(T source) {

		Assert.notNull(source, "Entity must not be null");
		// 执行父类的方法
		return markModified(getAuditor(), source);
	}

	/**
	 * 如果存在auditorAware,则执行auditorAware.getCurrentAuditor(),获取审计人;否则为Auditor.none()
	 */
	Auditor<?> getAuditor() {

		return auditorAware.map(AuditorAware::getCurrentAuditor).map(Auditor::ofOptional) //
				.orElse(Auditor.none());
	}

	public void afterPropertiesSet() {

		if (!auditorAware.isPresent()) {
			logger.debug("No AuditorAware set; Auditing will not be applied");
		}
	}
}

在AuditHandler中,核心实现在父类AuditingHandlerSupport。

3.6 AuditingHandlerSupport

AuditingHandlerSupport的源码如下:

package org.springframework.data.auditing;

public abstract class AuditingHandlerSupport {

	private static final Log logger = LogFactory.getLog(AuditingHandlerSupport.class);

	// 审计bean的包装工厂,该类的实现为DefaultAuditableBeanWrapperFactory
	private final AuditableBeanWrapperFactory factory;

	private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
	private boolean dateTimeForNow = true;
	private boolean modifyOnCreation = true;

	public AuditingHandlerSupport(PersistentEntities entities) {

		Assert.notNull(entities, "PersistentEntities must not be null");

		this.factory = new MappingAuditableBeanWrapperFactory(entities);
	}

	public void setDateTimeForNow(boolean dateTimeForNow) {
		this.dateTimeForNow = dateTimeForNow;
	}
	public void setModifyOnCreation(boolean modifyOnCreation) {
		this.modifyOnCreation = modifyOnCreation;
	}
	public void setDateTimeProvider(@Nullable DateTimeProvider dateTimeProvider) {
		this.dateTimeProvider = dateTimeProvider == null ? CurrentDateTimeProvider.INSTANCE : dateTimeProvider;
	}

	protected final boolean isAuditable(Object source) {

		Assert.notNull(source, "Source entity must not be null");

		return factory.getBeanWrapperFor(source).isPresent();
	}
	<T> T markCreated(Auditor<?> auditor, T source) {

		Assert.notNull(source, "Source entity must not be null");

		return touch(auditor, source, true);
	}

	<T> T markModified(Auditor<?> auditor, T source) {

		Assert.notNull(source, "Source entity must not be null");

		return touch(auditor, source, false);
	}

	/**
	 * 处理
	 * @param auditor:审计人信息
	 * @param target:目标实体,即实体类
	 * @param isNew:是否新增
	 */
	private <T> T touch(Auditor<?> auditor, T target, boolean isNew) {
		// 创建实体类的包装类,用于设置审计相关的属性值,
		// 在getBeanWrapperFor()方法中,解析实体类定义的审计相关的注解的元数据,
		// 包装成DefaultAuditableBeanWrapperFactory.ReflectionAuditingBeanWrapper
		Optional<AuditableBeanWrapper<T>> wrapper = factory.getBeanWrapperFor(target);
		// 对实体解包
		return wrapper.map(it -> {
			// 设置审计人信息。@createBy、@lastModifiedBy
			touchAuditor(auditor, it, isNew);
			// 设置日期
			Optional<TemporalAccessor> now = dateTimeForNow ? touchDate(it, isNew) : Optional.empty();

			if (logger.isDebugEnabled()) {

				Object defaultedNow = now.map(Object::toString).orElse("not set");
				Object defaultedAuditor = auditor.isPresent() ? auditor.toString() : "unknown";

				logger.debug(
						LogMessage.format("Touched %s - Last modification at %s by %s", target, defaultedNow, defaultedAuditor));
			}
			// 返回设置了审计信息的对象
			return it.getBean();
		}).orElse(target);
	}

	/**
	 * 设置修改和创建审核员。仅在新的可审核对象上设置创建审核器
	 */
	private void touchAuditor(Auditor<?> auditor, AuditableBeanWrapper<?> wrapper, boolean isNew) {

		if(!auditor.isPresent()) {
			return;
		}

		Assert.notNull(wrapper, "AuditableBeanWrapper must not be null");

		// 如果是新增,修改createBy
		if (isNew) {
			wrapper.setCreatedBy(auditor.getValue());
		}
		// 如果不是新增 || 配置了modifyOnCreation为true,修改lastModifiedBy
		if (!isNew || modifyOnCreation) {
			wrapper.setLastModifiedBy(auditor.getValue());
		}
	}

	/**
	 * 设置操作时间
	 */
	private Optional<TemporalAccessor> touchDate(AuditableBeanWrapper<?> wrapper, boolean isNew) {

		Assert.notNull(wrapper, "AuditableBeanWrapper must not be null");
		// 获取当前时间
		Optional<TemporalAccessor> now = dateTimeProvider.getNow();

		Assert.notNull(now, () -> String.format("Now must not be null Returned by: %s", dateTimeProvider.getClass()));
		// 设置创建时间
		now.filter(__ -> isNew).ifPresent(wrapper::setCreatedDate);
		// 设置修改时间
		now.filter(__ -> !isNew || modifyOnCreation).ifPresent(wrapper::setLastModifiedDate);

		return now;
	}
}

在AuditingHandlerSupport中,无论是新增还是编辑,最后都会调用touch()方法,该方法主要执行如下:

1)执行factory.getBeanWrapperFor(target),获取一个AuditableBeanWrapper对象,该对象为DefaultAuditableBeanWrapperFactory.ReflectionAuditingBeanWrapper;

该方法会执行AnnotationAuditingMetadata.getMetadata(it.getClass()),解析实体类中定义的审计相关注解的元数据,存放对应的注解的Field对象,并将信息添加到ReflectionAuditingBeanWrapper中。

2)执行touchAuditor(),根据新增或修改,调用ReflectionAuditingBeanWrapper的对应审计方法,为对应审计的审计人Field赋值;

3)执行touchDate(),根据新增或修改,调用ReflectionAuditingBeanWrapper的对应审计方法,为对应审计的时间Field赋值;

4)返回包装后的实体对象;

3.7 ReflectionAuditingBeanWrapper

ReflectionAuditingBeanWrapper的代码如下:

package org.springframework.data.auditing;

class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory {

	/**
	 * 保存实体类的审计相关注解的元数据
	 */
	static class ReflectionAuditingBeanWrapper<T> extends DateConvertingAuditableBeanWrapper<T> {

		private final AnnotationAuditingMetadata metadata;
		private final T target;

		/**
		 * 保存实体类的审计相关注解的元数据
		 */
		public ReflectionAuditingBeanWrapper(ConversionService conversionService, T target) {
			super(conversionService);

			Assert.notNull(target, "Target object must not be null");

			this.metadata = AnnotationAuditingMetadata.getMetadata(target.getClass());
			this.target = target;
		}

		/**
		 * 为属性Field赋值
		 */
		@Override
		public Object setCreatedBy(Object value) {
			return setField(metadata.getCreatedByField(), value);
		}

		@Override
		public TemporalAccessor setCreatedDate(TemporalAccessor value) {
			return setDateField(metadata.getCreatedDateField(), value);
		}

		@Override
		public Object setLastModifiedBy(Object value) {
			return setField(metadata.getLastModifiedByField(), value);
		}

		@Override
		public Optional<TemporalAccessor> getLastModifiedDate() {

			return getAsTemporalAccessor(metadata.getLastModifiedDateField().map(field -> {

				Object value = org.springframework.util.ReflectionUtils.getField(field, target);
				return value instanceof Optional ? ((Optional<?>) value).orElse(null) : value;

			}), TemporalAccessor.class);
		}

		@Override
		public TemporalAccessor setLastModifiedDate(TemporalAccessor value) {
			return setDateField(metadata.getLastModifiedDateField(), value);
		}

		@Override
		public T getBean() {
			return target;
		}

		private <S> S setField(Optional<Field> field, S value) {

			field.ifPresent(it -> ReflectionUtils.setField(it, target, value));

			return value;
		}

		private TemporalAccessor setDateField(Optional<Field> field, TemporalAccessor value) {

			field.ifPresent(it -> ReflectionUtils.setField(it, target, getDateValueToSet(value, it.getType(), it)));

			return value;
		}
	}
}

ReflectionAuditingBeanWrapper提供了setField的方法,为对应的审计字段赋值。

3.7 AnnotationAuditingMetadata

AnnotationAuditingMetadata的源码如下:

package org.springframework.data.auditing;

final class AnnotationAuditingMetadata {

	// 定义注解属性过滤器
	private static final AnnotationFieldFilter CREATED_BY_FILTER = new AnnotationFieldFilter(CreatedBy.class);
	private static final AnnotationFieldFilter CREATED_DATE_FILTER = new AnnotationFieldFilter(CreatedDate.class);
	private static final AnnotationFieldFilter LAST_MODIFIED_BY_FILTER = new AnnotationFieldFilter(LastModifiedBy.class);
	private static final AnnotationFieldFilter LAST_MODIFIED_DATE_FILTER = new AnnotationFieldFilter(
			LastModifiedDate.class);

	private static final Map<Class<?>, AnnotationAuditingMetadata> metadataCache = new ConcurrentHashMap<>();

	private final Optional<Field> createdByField;
	private final Optional<Field> createdDateField;
	private final Optional<Field> lastModifiedByField;
	private final Optional<Field> lastModifiedDateField;

	/**
	 * Creates a new {@link AnnotationAuditingMetadata} instance for the given type.
	 *
	 * @param type must not be {@literal null}.
	 */
	private AnnotationAuditingMetadata(Class<?> type) {

		Assert.notNull(type, "Given type must not be null");
		// 解析审计的注解,获取对应属性Field对象
		this.createdByField = Optional.ofNullable(ReflectionUtils.findField(type, CREATED_BY_FILTER));
		this.createdDateField = Optional.ofNullable(ReflectionUtils.findField(type, CREATED_DATE_FILTER));
		this.lastModifiedByField = Optional.ofNullable(ReflectionUtils.findField(type, LAST_MODIFIED_BY_FILTER));
		this.lastModifiedDateField = Optional.ofNullable(ReflectionUtils.findField(type, LAST_MODIFIED_DATE_FILTER));

		assertValidDateFieldType(createdDateField);
		assertValidDateFieldType(lastModifiedDateField);
	}

	/**
	 * 解析实体类的审计相关注解元数据,并保存到缓存中
	 */
	public static AnnotationAuditingMetadata getMetadata(Class<?> type) {
		return metadataCache.computeIfAbsent(type, AnnotationAuditingMetadata::new);
	}
	
	// 省略其他

}

AnnotationAuditingMetadata主要解析实体类中添加的审计相关注解的属性的Field。

小结

以上为本篇分享的全部内容,以下做一个小结:

1)JPA的提供了持久化的监听回调注解;

2)Spring解析实体类时,会解析实体类中添加的监听回调注解的监听器;或者实体类中的@EntityListeners注解中的监听器,并归类存放在FastSessionServices中,然后将FastSessionServices传给SessionImpl对象;

对于Auditing,监听器为AuditingEntityListener,添加了@PrePersist和@PreUpdate,在新增和修改之前回调。

3)Auditing审计功能通过添加@EnableJpaAuditing注解,自动为AuditingEntityListener添加AuditHandler对象;

4)AuditHandler对象提供了对当前执行持久化对象的审计相关注解的属性获取、审计人获取;

5)JPA通过SessionImpl执行新增或修改操作时,会调用FastSessionServices中对应操作类型的监听器,从而执行AuditingEntityListener的方法;

6)AuditingEntityListener通过AuditHandler及当前的实体类,通过反射,为实体类的审计属性赋值;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/751917.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue + Lodop 制作可视化设计页面 实现打印设计功能(二)

历史&#xff1a; vue2 Lodop 制作可视化设计页面 实现打印设计功能&#xff08;一&#xff09; 前言&#xff1a; 之前本来打算用直接拿之前做的vue2版本改改就发的&#xff0c;但考虑到现在主流都是vue3了&#xff0c;所以从这篇文章开始使用vue3来写&#xff0c;以及最后…

Spring相关面试题(三)

29 如何在所有的BeanDefinition注册完成后&#xff0c;进行扩展 Bean工厂的后置处理器&#xff0c;在所有的Bean注册完成后&#xff0c;就被执行。 public class A implements BeanFactoryPostProcessor {private String name "a class";private B b; ​public St…

项目分层--简单图书管理系统

分层情况 实体类Book代码 //实体类 public class Book {private int id;private String name;private int bsum;public Book() {}public Book(int id, String name, int bsum) {this.id id;this.name name;this.bsum bsum;}public int getId() {return id;}public void set…

2024上海初中生古诗文大会倒计时4个多月:真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个多月时间&#xff0c;我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是来自于历届真题&#xff0c;去重、合并后&#xff0c;每道题都有参考答案和解析。 为帮助孩子自测和练习&…

Midjourney 平替 Leonardo AI 国内版上线关键还免费

Leonardo AI 正式在国内上线&#xff0c;功能相对基础&#xff0c;计划在两周后推出新一轮的更新&#xff0c;届时将支持 Elements (Lora) 和一些新的 XL 模型&#xff0c;逐步会把国际服上的功能移植过来。 虽然界面是英文&#xff0c;但是不要慌&#xff0c;你可以在 fanbook…

Cloud Serpent

Cloud Serpent 风蛇&#xff0c;刷厄运北很好用的&#xff0c;不记得早前好像就是50级副本神庙带俯冲&#xff0c;加速&#xff0c;远距离攻击&#xff0c;这样就不容易被厄运北法师和恶魔的法术攻击打中&#xff0c;残废术&#xff0c;而减速&#xff0c;刷本拉怪超级好用 闪…

历史的加速度:智人何时会迎来下一个版本?人类的命运与挑战

在《人类简史》中&#xff0c;尤瓦尔赫拉利主要探讨了人类的过去和发展历程&#xff0c;重点关注的是智人&#xff08;Homo sapiens&#xff09;。在他的续作《未来简史》中&#xff0c;他进一步探讨了未来人类的发展&#xff0c;并引入了“神人”&#xff08;Homo deus&#x…

C++之迭代器分类与List容器的使用

目录 迭代器的分类 List容器 ​编辑 总结 在Vector容器中我们学习了迭代器&#xff0c;知道了迭代器的作用和使用方法&#xff0c;本期我们将进一步学习迭代器的概念以及list容器的使用。 迭代器的分类 以算法库中的两个算法为例&#xff1a; sort算法是用来排序的&#…

常用MQ消息中间件Kafka、ZeroMQ和RabbitMQ对比及RabbitMQ详解

1、概述 在现代的分布式系统和实时数据处理领域&#xff0c;消息中间件扮演着关键的角色&#xff0c;用于解决应用程序之间的通信和数据传递的挑战。在众多的消息中间件解决方案中&#xff0c;Kafka、ZeroMQ和RabbitMQ 是备受关注和广泛应用的代表性系统。它们各自具有独特的特…

O2OA(翱途) 开发平台之HTTP端口规划

O2OA(翱途) 开发平台[下称O2OA开发平台或者O2OA]采用相对灵活的系统架构&#xff0c;支持三种服务器运行的方式。本篇主要阐述合并服务运行独立服务运行代理端口运行三种服务器运行方式。 一、先决条件&#xff1a; 1、O2Server服务器正常运行&#xff0c;系统安装部署请参考文…

Java基于jjwt操作jwt

之前讲解了jwt的相关知识&#xff0c;有不了解的&#xff0c;可以查看相关的文章JWT简介-CSDN博客&#xff0c;本节不再介绍&#xff0c;主要讲解有关java中如何通过jjwt库产生jwt以及解析jwt的相关操作。 添加maven依赖 <dependency><groupId>io.jsonwebtoken&l…

统信桌面操作系统上使用命令行添加软件图标到任务栏

原文链接&#xff1a;统信桌面操作系统上使用命令行添加软件图标到任务栏 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇在统信桌面操作系统上使用命令行添加软件图标到任务栏的文章。通过命令行将常用软件的图标添加到任务栏&#xff0c;可以快速启动软件&#xf…

网络抓包分析工具

摘要 随着网络技术的快速发展&#xff0c;网络数据的传输和处理变得日益复杂。网络抓包分析工具作为网络故障排查、性能优化以及安全审计的重要工具&#xff0c;对于提升网络管理的效率和准确性具有重要意义。本文旨在设计并实现一款高效、易用的网络抓包分析工具&#xff0c;…

Python实现数据库与Excel文件之间的数据导入与导出

数据库和Excel文件是两种常见且重要的数据存储方式。数据库通常用于大规模数据的高效存储、管理和查询&#xff0c;而Excel则以其直观的界面和简单的操作方式广泛应用于数据分析、报告生成和可视化等领域。在实际工作中&#xff0c;可能需要在这两者之间进行数据的导入与导出。…

网上零食销售系统

摘 要 随着互联网的快速发展&#xff0c;网上销售已成为零售业的重要组成部分。在众多的线上购物品类中&#xff0c;零食销售因其受众广泛、购买频率高、消费金额适中等特点&#xff0c;一直备受关注。然而&#xff0c;传统的零食销售方式&#xff0c;如实体店铺销售&#xff…

Python湍流隐式模型耗散粘性方程和大涡流模拟

&#x1f3af;要点 &#x1f3af;达朗贝尔一维波动通解&#xff0c;二维变速模拟 | &#x1f3af;达朗贝尔算子解双曲波形微分方程 | &#x1f3af;耗散系统粘性伯格斯方程快速傅里叶变换算法 | &#x1f3af;二维线性和非线性对流扩散解和湍流隐式建模 &#x1f4dc;偏微分方…

网络研究观:网络犯罪简报

通过犯罪研究人员精选的新闻提要了解最新的全球网络犯罪威胁。 了解不同的数字欺诈以及如何保护自己。 1. 网络犯罪分子冒充 CBI 和 IB 官员&#xff1a;KP 加尔各答警察局警告公民&#xff0c;诈骗者通过发送虚假的 CBI 和 IB 通知来勒索钱财&#xff0c;指控他们在线观看儿…

Avue框架学习

Avue框架学习 我们的项目使用的框架是 Avue 在我看来这个框架最大的特点是可以基于JSON配置页面上的From,Table以及各种各样的输入框等,不需要懂前端就可以很快上手,前提是需要多查一下文档 开发环境搭建 由于我本地的环境全是用docker来搭建的,所以我依然选择用docker搭建我…

【第二周】基础语法学习

目录 前言初始化项目文件介绍基本介绍JSWXMLWXSS 常见组件基础组件视图容器match-mediamovable-area/viewpage-containerscroll-viewswiper 表单组件自定义组件 模板语法数据绑定单向数据绑定双向数据绑定 列表渲染条件渲染模板引用 事件系统事件类型事件绑定阻止冒泡互斥事件事…

【开源项目】自然语言处理领域的明星项目推荐:Hugging Face Transformers

在当今人工智能与大数据飞速发展的时代&#xff0c;自然语言处理&#xff08;NLP&#xff09;已成为推动科技进步的重要力量。而在NLP领域&#xff0c;Hugging Face Transformers无疑是一个备受瞩目的开源项目。本文将从项目介绍、代码解释以及技术特点等角度&#xff0c;为您深…