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

news2024/11/15 17:53:57

 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.coloradmin.cn/o/1859737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#的无边框窗体项目模板 - 开源研究系列文章

继续整理和编写代码及博文。 这次将笔者自己整理的C#的无边框窗体项目的基本模板进行总结&#xff0c;得出了基于C#的.net framework的Winform的4个项目模板&#xff0c;这些模板具有基本的功能&#xff0c;即已经初步将代码写了&#xff0c;直接在其基础上添加业务代码即可&am…

Maya 白膜渲染简单教程

零基础渲染小白&#xff0c;没关系&#xff0c;一篇超简单教程带你学会渲染白膜。 先打开Maya&#xff0c;看看面板有没有渲染器&#xff0c;这里以Arnold为主。 要是没有这个&#xff0c;就去找插件管理器&#xff0c; Arnold的是mtoa&#xff0c;在搜索栏搜&#xff0c;然后把…

uni-app 微信小程序开发到发布流程

1. uni-app 微信小程序开发到发布流程 1.1. 新建一个uni-app 项目 1.2. 发行微信小程序 1.3. 微信开发者平台的微信小程序appid 复制进来&#xff08;点击发行&#xff09; 1.4. IDE may already started at port xxxx, trying to connect &#xff08;1&#xff09;关闭微信…

【Golang - 90天从新手到大师】Day14 - 方法和接口

一&#xff0e; go方法 go方法&#xff1a;在函数的func和函数名间增加一个特殊的接收器类型&#xff0c;接收器可以是结构体类型或非结构体类型。接收器可以在方法内部访问。创建一个接收器类型为Type的methodName方法。 func (t Type) methodName(parameter list) {}go引入…

解析JavaScript中逻辑运算符和||的返回值机制

本文主要内容&#xff1a;了解逻辑运算符 &&&#xff08;逻辑与&#xff09;和 ||&#xff08;逻辑或&#xff09;的返回值。 在JavaScript中&#xff0c;逻辑运算符 &&&#xff08;逻辑与&#xff09;和 ||&#xff08;逻辑或&#xff09;的返回值可能并不总…

大模型之-Seq2Seq介绍

大模型之-Seq2Seq介绍 1. Seq2Seq 模型概述 Seq2Seq&#xff08;Sequence to Sequence&#xff09;模型是一种用于处理序列数据的深度学习模型&#xff0c;常用于机器翻译、文本摘要和对话系统等任务。它的核心思想是将一个输入序列转换成一个输出序列。 Seq2Seq模型由两个主…

【仿真建模-anylogic】MarkupSegment代码解析

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-25 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 类图 2. 代码解析 //***********************核心字段************************ // MarkupSegment起点坐标 double startX; double…

MySQL如何实现事务特性

目录 事务有那些特性 原子性如何实现 持久性如何实现 隔离性与一致性如何实现 事务有那些特性 事务是由MySQL的引擎 InnoDB 来实现的 事务的特性 : 原子性 不存在中间状态,要么完成,要么不完成 一致性 事务操作前和操作后,数据满足完整性的约束,数据库保持一致的状态…

mac鼠标和触摸屏单独设置滚动方向

引言&#xff1a;mac很好用&#xff0c;但是外接鼠标的滚动方向和win不一样&#xff0c;总有点不习惯。于是想要设置一下&#xff0c;当打开设置&#xff0c;搜索鼠标时&#xff0c;将“自然滚动”取消&#xff0c;就可以更改了。 问题&#xff1a;但触摸屏又不好用了。 原因&a…

VBA递归过程快速组合数据

实例需求&#xff1a;数据表包含的列数不固定&#xff0c;有的列&#xff08;数量和位置不固定&#xff09;包含组合数据&#xff0c;例如C2单元格为D,P&#xff0c;说明Unit Config有两种分别为D和P&#xff0c;如下图所示。 现在需要将所有的组合罗列出来&#xff0c;如下所示…

模块化沙箱结合零信任实现一机两用解决方案

为实现数据防泄露的各类型市场需求以及“一机两用”标准落地&#xff0c;依靠十几年的沙箱技术积累&#xff0c;研发出了支持多平台的模块化沙箱&#xff0c;配合零信任SDP网关&#xff0c;提高数据安全&#xff0c;实现“一机两用”安全解决方案。 产品特点 -自2008年开始已…

奇点临近:人类与智能时代的未来

在信息爆炸的时代&#xff0c;我们每天都被海量的信息所淹没&#xff0c;如何才能在这个嘈杂的世界中找到真正有价值的信息&#xff1f;如何才能利用信息的力量&#xff0c;提升我们的认知水平&#xff0c;重塑我们的未来&#xff1f; 这些问题的答案&#xff0c;或许都能在雷…

基于MindSpore Quantum的Grover搜索算法和龙算法

如果你听过量子计算&#xff0c;那么你一定听说过Grover搜索算法。1996年&#xff0c;Lov Grover [1] 提出了Grover搜索算法&#xff0c;它是一种利用量子状态的叠加性进行并行计算并实现加速的算法。Grover搜索算法被公认为是继Shor算法后的第二大量子算法&#xff0c;也是第一…

【仿真建模-解析几何】求有向线段上距指定点最近的坐标

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-25 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 问题描述&#xff1a; 有向线段起点A为&#xff08;x1&#xff0c;y1&#xff09;&#xff0c;终点B为&#xff08;x2&#xff0c;y2&a…

图片详解 —— 二维树状数组

\qquad 想要完全理解二维树状数组&#xff0c;要先完全理解一维树状数组。这里给大家推荐一个视频&#xff1a;五分钟丝滑动画讲解 | 树状数组。建议大家先看一遍视频再来看下面的讲解。 \qquad 在一维树状数组中&#xff0c; t r e [ i ] tre[i] tre[i] 可以理解为以 i i i 为…

MySQL集群高可用架构之双主双活+keepalived

该教程再linux系统下 从部署单台mysql -->到部署两台双主mysql-->再到安装keepalived-->整体测试 从而实现mysql双主双活高可用的目标。 改文档由本人亲自部署搭建一步一步编写而来&#xff0c;实属不易&#xff0c;如对您有所帮助&#xff0c;请收藏点个赞&#x…

装机必备一WinRAR安装使用以及常见问题

WinRAR是一款功能强大的压缩包管理器&#xff0c;支持多种压缩格式&#xff0c;如RAR、ZIP等。作为一款经典且广泛使用的压缩软件&#xff0c;WinRAR不仅在文件压缩率和速度方面表现出色&#xff0c;还提供了备份数据、缩减电子邮件附件大小以及解压缩网络下载文件等功能。 为…

瑜伽馆管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;教练管理&#xff0c;用户管理&#xff0c;瑜伽管理&#xff0c;套餐管理&#xff0c;体测报告管理&#xff0c;基础数据管理 前台账户功能包括&#xff1a;系统首页&#xff0…

计网入门还没到放弃

TCP报文段格式 源端口&#xff1a;标识报文的返回地址 目的端口&#xff1a;指明计算机上的应用程序接口 序号&#xff1a;通过SYN包传给接收端主机&#xff0c;每传送一次就1&#xff0c;用来解决网络包乱序的问题。 确认号&#xff1a;期望下一次收到的数据的序列号&#xff…

lvm,磁盘配额

文章目录 LVM概述Logical Volume Manager逻辑卷管理LVM机制的基本概念 LVM的管理命令LVM应用实例案例环境需求描述创建 LVM创建, 删除LVM 流程创建 LVM 流程删除 LVM 流程 磁盘配额概述实现磁盘限额的条件Linux 磁盘限额的特点 磁盘配额管理CentOS7设置磁盘配额步骤编辑用户和组…