DATACMNS-1676 - Create shared conversion service for AuditableBeanWrapper.

Prevent performance penalty in creating conversion service each time a domain object is being persisted.

Original pull request: #432.
This commit is contained in:
Pavel Horal
2020-02-28 16:42:41 +01:00
committed by Mark Paluch
parent d9a1f65f05
commit f2f7886105
3 changed files with 45 additions and 30 deletions

View File

@@ -21,7 +21,6 @@ import lombok.RequiredArgsConstructor;
import java.lang.reflect.Field;
import java.time.Instant;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.Optional;
import java.util.stream.Stream;
@@ -47,6 +46,12 @@ import org.springframework.util.Assert;
*/
class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory {
protected final ConversionService conversionService;
public DefaultAuditableBeanWrapperFactory() {
this.conversionService = getDateConversionService();
}
/**
* Returns an {@link AuditableBeanWrapper} if the given object is capable of being equipped with auditing information.
*
@@ -61,25 +66,39 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
return Optional.of(source).map(it -> {
if (it instanceof Auditable) {
return (AuditableBeanWrapper<T>) new AuditableInterfaceBeanWrapper((Auditable<Object, ?, TemporalAccessor>) it);
return (AuditableBeanWrapper<T>) new AuditableInterfaceBeanWrapper(conversionService, (Auditable<Object, ?, TemporalAccessor>) it);
}
AnnotationAuditingMetadata metadata = AnnotationAuditingMetadata.getMetadata(it.getClass());
if (metadata.isAuditable()) {
return new ReflectionAuditingBeanWrapper<T>(it);
return new ReflectionAuditingBeanWrapper<T>(conversionService, it);
}
return null;
});
}
/**
* Creates conversion service for conversions between {@link TemporalAccessor} values and other supported date type values.
*
* @return
*/
static ConversionService getDateConversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
JodaTimeConverters.getConvertersToRegister().forEach(conversionService::addConverter);
Jsr310Converters.getConvertersToRegister().forEach(conversionService::addConverter);
ThreeTenBackPortConverters.getConvertersToRegister().forEach(conversionService::addConverter);
return conversionService;
}
/**
* An {@link AuditableBeanWrapper} that works with objects implementing
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
static class AuditableInterfaceBeanWrapper
extends DateConvertingAuditableBeanWrapper<Auditable<Object, ?, TemporalAccessor>> {
@@ -87,7 +106,8 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
private final Class<? extends TemporalAccessor> type;
@SuppressWarnings("unchecked")
public AuditableInterfaceBeanWrapper(Auditable<Object, ?, TemporalAccessor> auditable) {
public AuditableInterfaceBeanWrapper(ConversionService conversionService, Auditable<Object, ?, TemporalAccessor> auditable) {
super(conversionService);
this.auditable = auditable;
this.type = (Class<? extends TemporalAccessor>) ResolvableType.forClass(Auditable.class, auditable.getClass())
@@ -151,7 +171,7 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
return value;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#getBean()
*/
@@ -162,32 +182,19 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
}
/**
* Base class for {@link AuditableBeanWrapper} implementations that might need to convert {@link Calendar} values into
* Base class for {@link AuditableBeanWrapper} implementations that might need to convert {@link TemporalAccessor} values into
* compatible types when setting date/time information.
*
* @author Oliver Gierke
* @since 1.8
*/
@RequiredArgsConstructor
abstract static class DateConvertingAuditableBeanWrapper<T> implements AuditableBeanWrapper<T> {
private final ConversionService conversionService;
/**
* Creates a new {@link DateConvertingAuditableBeanWrapper}.
*/
public DateConvertingAuditableBeanWrapper() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
JodaTimeConverters.getConvertersToRegister().forEach(conversionService::addConverter);
Jsr310Converters.getConvertersToRegister().forEach(conversionService::addConverter);
ThreeTenBackPortConverters.getConvertersToRegister().forEach(conversionService::addConverter);
this.conversionService = conversionService;
}
/**
* Returns the {@link Calendar} in a type, compatible to the given field.
* Returns the {@link TemporalAccessor} in a type, compatible to the given field.
*
* @param value can be {@literal null}.
* @param targetType must not be {@literal null}.
@@ -221,7 +228,7 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
}
/**
* Returns the given object as {@link Calendar}.
* Returns the given object as {@link TemporalAccessor}.
*
* @param source can be {@literal null}.
* @param target must not be {@literal null}.
@@ -266,9 +273,11 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
/**
* Creates a new {@link ReflectionAuditingBeanWrapper} to set auditing data on the given target object.
*
* @param conversionService conversion service for date value type conversions
* @param target must not be {@literal null}.
*/
public ReflectionAuditingBeanWrapper(T target) {
public ReflectionAuditingBeanWrapper(ConversionService conversionService, T target) {
super(conversionService);
Assert.notNull(target, "Target object must not be null!");
@@ -328,7 +337,7 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
return setDateField(metadata.getLastModifiedDateField(), value);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#getBean()
*/

View File

@@ -22,6 +22,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
@@ -87,7 +88,7 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
key -> new MappingAuditingMetadata(context, it.getClass()));
return Optional.<AuditableBeanWrapper<T>> ofNullable(metadata.isAuditable() //
? new MappingMetadataAuditableBeanWrapper<T>(entity.getPropertyPathAccessor(it), metadata)
? new MappingMetadataAuditableBeanWrapper<T>(conversionService, entity.getPropertyPathAccessor(it), metadata)
: null);
}).orElseGet(() -> super.getBeanWrapperFor(source));
@@ -177,8 +178,11 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
* @param accessor must not be {@literal null}.
* @param metadata must not be {@literal null}.
*/
public MappingMetadataAuditableBeanWrapper(PersistentPropertyPathAccessor<T> accessor,
public MappingMetadataAuditableBeanWrapper(
ConversionService conversionService,
PersistentPropertyPathAccessor<T> accessor,
MappingAuditingMetadata metadata) {
super(conversionService);
Assert.notNull(accessor, "PersistentPropertyAccessor must not be null!");
Assert.notNull(metadata, "Auditing metadata must not be null!");