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:
@@ -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()
|
||||
*/
|
||||
|
||||
@@ -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!");
|
||||
|
||||
Reference in New Issue
Block a user