Introduced BeanWrapper helper object to reduce static access through MappingBeanHelper.
Creating bean instances, getting and setting properties on a bean was done by using static methods on MappingBeanHelper so far. This required certain Spring components (ConversionService) being held statically as well. Beyond that various Spring components had set these static values which might cause strange behavior if multiple instances of these Spring components are created. Thus we now have a BeanWrapper that takes the formerly statically held instances and uses those for bean creation and property access. Moved SpEL awareness of a mapping into SpELAwareParameterValueProvider to hide it behind an ParameterValueProvider instance.
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeanInstantiationException;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.mapping.model.MappingInstantiationException;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.PreferredConstructor;
|
||||
import org.springframework.data.mapping.model.PreferredConstructor.Parameter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Value object to allow creation of objects using the metamodel, setting and getting properties.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BeanWrapper<E extends PersistentEntity<T, ?>, T> {
|
||||
|
||||
private final T bean;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BeanWrapper} for the given bean instance and {@link ConversionService}. If
|
||||
* {@link ConversionService} is {@literal null} no property type conversion will take place.
|
||||
*
|
||||
* @param <E>
|
||||
* @param <T>
|
||||
* @param bean must not be {@literal null}
|
||||
* @param conversionService
|
||||
* @return
|
||||
*/
|
||||
public static <E extends PersistentEntity<T, ?>, T> BeanWrapper<E, T> create(T bean,
|
||||
ConversionService conversionService) {
|
||||
return new BeanWrapper<E, T>(bean, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BeanWrapper} using the given {@link PersistentEntity} and {@link ParameterValueProvider}. Will
|
||||
* instantly create a bean instance using the {@link PreferredConstructor} of the {@link PersistentEntity}.
|
||||
*
|
||||
* @param <E>
|
||||
* @param <T>
|
||||
* @param entity
|
||||
* @param provider
|
||||
* @param conversionService
|
||||
* @return
|
||||
*/
|
||||
public static <E extends PersistentEntity<T, ?>, T> BeanWrapper<E, T> create(E entity,
|
||||
ParameterValueProvider provider, ConversionService conversionService) {
|
||||
return new BeanWrapper<E, T>(entity, provider, conversionService);
|
||||
}
|
||||
|
||||
private BeanWrapper(T bean, ConversionService conversionService) {
|
||||
Assert.notNull(bean);
|
||||
this.bean = bean;
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private BeanWrapper(E entity, ParameterValueProvider provider, ConversionService conversionService) {
|
||||
|
||||
this.conversionService = conversionService;
|
||||
|
||||
T bean = null;
|
||||
|
||||
PreferredConstructor<T> constructor = entity.getPreferredConstructor();
|
||||
if (null == constructor) {
|
||||
try {
|
||||
Class<T> clazz = entity.getType();
|
||||
if (clazz.isArray()) {
|
||||
Class<?> ctype = clazz;
|
||||
int dims = 0;
|
||||
while (ctype.isArray()) {
|
||||
ctype = ctype.getComponentType();
|
||||
dims++;
|
||||
}
|
||||
bean = (T) Array.newInstance(clazz, dims);
|
||||
} else {
|
||||
bean = BeanUtils.instantiateClass(entity.getType());
|
||||
}
|
||||
} catch (BeanInstantiationException e) {
|
||||
throw new MappingInstantiationException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
List<Object> params = new LinkedList<Object>();
|
||||
if (null != provider && constructor.hasParameters()) {
|
||||
for (Parameter<?> parameter : constructor.getParameters()) {
|
||||
params.add(provider.getParameterValue(parameter));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
bean = BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray());
|
||||
} catch (BeanInstantiationException e) {
|
||||
throw new MappingInstantiationException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given {@link PersistentProperty} to the given value. Will do type conversion if a
|
||||
* {@link ConversionService} is configured. Will use the accessor method of the given {@link PersistentProperty} if it
|
||||
* has one or field access otherwise.
|
||||
*
|
||||
* @param property
|
||||
* @param value
|
||||
* @throws IllegalAccessException
|
||||
* @throws InvocationTargetException
|
||||
*/
|
||||
public void setProperty(PersistentProperty<?> property, Object value) throws IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
setProperty(property, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given {@link PersistentProperty} to the given value. Will do type conversion if a
|
||||
* {@link ConversionService} is configured.
|
||||
*
|
||||
* @param property
|
||||
* @param value
|
||||
* @throws IllegalAccessException
|
||||
* @throws InvocationTargetException
|
||||
*/
|
||||
public void setProperty(PersistentProperty<?> property, Object value, boolean fieldAccessOnly)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
|
||||
Method setter = property.getPropertyDescriptor() != null ? property.getPropertyDescriptor().getWriteMethod() : null;
|
||||
|
||||
if (fieldAccessOnly || null == setter) {
|
||||
Object valueToSet = getPotentiallyConvertedValue(value, property.getType());
|
||||
ReflectionUtils.makeAccessible(property.getField());
|
||||
ReflectionUtils.setField(property.getField(), bean, valueToSet);
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?>[] paramTypes = setter.getParameterTypes();
|
||||
Object valueToSet = getPotentiallyConvertedValue(value, paramTypes[0]);
|
||||
ReflectionUtils.makeAccessible(setter);
|
||||
ReflectionUtils.invokeMethod(setter, bean, valueToSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the given {@link PersistentProperty} of the underlying bean instance.
|
||||
*
|
||||
* @param <S>
|
||||
* @param property
|
||||
* @return
|
||||
* @throws IllegalAccessException
|
||||
* @throws InvocationTargetException
|
||||
*/
|
||||
public Object getProperty(PersistentProperty<?> property)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
return getProperty(property, property.getType(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the given {@link PersistentProperty} potentially converted to the given type.
|
||||
*
|
||||
* @param <S>
|
||||
* @param property
|
||||
* @param type
|
||||
* @param fieldAccessOnly
|
||||
* @return
|
||||
* @throws IllegalAccessException
|
||||
* @throws InvocationTargetException
|
||||
*/
|
||||
public <S> S getProperty(PersistentProperty<?> property, Class<? extends S> type, boolean fieldAccessOnly)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
Object obj;
|
||||
Field field = property.getField();
|
||||
Method getter = (null != property.getPropertyDescriptor() ? property.getPropertyDescriptor().getReadMethod() : null);
|
||||
if (fieldAccessOnly || null == getter) {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
obj = ReflectionUtils.getField(field, bean);
|
||||
} else {
|
||||
ReflectionUtils.makeAccessible(getter);
|
||||
obj = ReflectionUtils.invokeMethod(getter, bean);
|
||||
}
|
||||
|
||||
return getPotentiallyConvertedValue(obj, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given source value if it is not assignable to the given target type.
|
||||
*
|
||||
* @param source
|
||||
* @param targetType
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <S> S getPotentiallyConvertedValue(Object source, Class<S> targetType) {
|
||||
|
||||
boolean conversionServiceAvailable = conversionService != null;
|
||||
boolean conversionNeeded = source == null || !source.getClass().isAssignableFrom(targetType);
|
||||
|
||||
if (conversionServiceAvailable && conversionNeeded) {
|
||||
return conversionService.convert(source, targetType);
|
||||
}
|
||||
|
||||
return (S) source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying bean instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public T getBean() {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,21 @@
|
||||
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.BeanInstantiationException;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.mapping.model.MappingInstantiationException;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.PreferredConstructor;
|
||||
import org.springframework.data.mapping.model.PreferredConstructor.Parameter;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Helper class to set and retrieve bean values.
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class MappingBeanHelper {
|
||||
|
||||
protected static GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
protected static SpelExpressionParser parser = new SpelExpressionParser();
|
||||
protected static Set<Class<?>> simpleTypes = Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>());
|
||||
private static final Set<Class<?>> simpleTypes = Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>());
|
||||
|
||||
static {
|
||||
simpleTypes.add(boolean.class);
|
||||
@@ -81,23 +58,26 @@ public abstract class MappingBeanHelper {
|
||||
simpleTypes.add(Double.class);
|
||||
simpleTypes.add(Character.class);
|
||||
simpleTypes.add(String.class);
|
||||
simpleTypes.add(java.util.Date.class);
|
||||
simpleTypes.add(Date.class);
|
||||
simpleTypes.add(Locale.class);
|
||||
simpleTypes.add(Class.class);
|
||||
}
|
||||
|
||||
public static GenericConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
public static void setConversionService(GenericConversionService conversionService) {
|
||||
MappingBeanHelper.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of types considered to be simple.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Set<Class<?>> getSimpleTypes() {
|
||||
return simpleTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given type is considered a simple one.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static boolean isSimpleType(Class<?> type) {
|
||||
for (Class<?> clazz : simpleTypes) {
|
||||
if (type == clazz || type.isAssignableFrom(clazz)) {
|
||||
@@ -106,122 +86,4 @@ public abstract class MappingBeanHelper {
|
||||
}
|
||||
return type.isEnum();
|
||||
}
|
||||
|
||||
public static <T, P extends PersistentProperty<P>> T constructInstance(PersistentEntity<T, P> entity, PreferredConstructor.ParameterValueProvider provider) {
|
||||
return constructInstance(entity, provider, new StandardEvaluationContext());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <T, P extends PersistentProperty<P>> T constructInstance(PersistentEntity<T, P> entity, PreferredConstructor.ParameterValueProvider provider, EvaluationContext spelCtx) {
|
||||
|
||||
PreferredConstructor<T> constructor = entity.getPreferredConstructor();
|
||||
if (null == constructor) {
|
||||
try {
|
||||
Class<T> clazz = entity.getType();
|
||||
if (clazz.isArray()) {
|
||||
Class<?> ctype = clazz;
|
||||
int dims = 0;
|
||||
while (ctype.isArray()) {
|
||||
ctype = ctype.getComponentType();
|
||||
dims++;
|
||||
}
|
||||
return (T) Array.newInstance(clazz, dims);
|
||||
} else {
|
||||
return BeanUtils.instantiateClass(entity.getType());
|
||||
}
|
||||
} catch (BeanInstantiationException e) {
|
||||
throw new MappingInstantiationException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
List<Object> params = new LinkedList<Object>();
|
||||
if (null != provider && constructor.getParameters().size() > 0) {
|
||||
for (Parameter<?> parameter : constructor.getParameters()) {
|
||||
String key = parameter.getKey();
|
||||
Object obj;
|
||||
if (null != key) {
|
||||
Expression x = parser.parseExpression(key);
|
||||
obj = x.getValue(spelCtx);
|
||||
} else {
|
||||
obj = provider.getParameterValue(parameter);
|
||||
}
|
||||
params.add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
T obj = null;
|
||||
try {
|
||||
obj = BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray());
|
||||
} catch (BeanInstantiationException e) {
|
||||
throw new MappingInstantiationException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static void setProperty(Object on,
|
||||
PersistentProperty<?> property,
|
||||
Object value)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
setProperty(on, property, value, false);
|
||||
}
|
||||
|
||||
public static void setProperty(Object on,
|
||||
PersistentProperty<?> property,
|
||||
Object value,
|
||||
boolean fieldAccessOnly)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
|
||||
Method setter = property.getPropertyDescriptor() != null ? property.getPropertyDescriptor().getWriteMethod() : null;
|
||||
|
||||
if (fieldAccessOnly || null == setter) {
|
||||
Object valueToSet = getPotentiallyConvertedValue(value, property.getType());
|
||||
ReflectionUtils.makeAccessible(property.getField());
|
||||
ReflectionUtils.setField(property.getField(), on, valueToSet);
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?>[] paramTypes = setter.getParameterTypes();
|
||||
Object valueToSet = getPotentiallyConvertedValue(value, paramTypes[0]);
|
||||
ReflectionUtils.makeAccessible(setter);
|
||||
ReflectionUtils.invokeMethod(setter, on, valueToSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given source value if it is not assignable to the given target type.
|
||||
*
|
||||
* @param source
|
||||
* @param targetType
|
||||
* @return
|
||||
*/
|
||||
private static Object getPotentiallyConvertedValue(Object source, Class<?> targetType) {
|
||||
if (source != null && source.getClass().isAssignableFrom(targetType)) {
|
||||
return source;
|
||||
}
|
||||
|
||||
return conversionService.convert(source, targetType);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <T> T getProperty(Object from,
|
||||
PersistentProperty<?> property,
|
||||
Class<T> type,
|
||||
boolean fieldAccessOnly)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
Object obj;
|
||||
Field field = property.getField();
|
||||
Method getter = (null != property.getPropertyDescriptor() ? property.getPropertyDescriptor().getReadMethod() : null);
|
||||
if (fieldAccessOnly || null == getter) {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
obj = ReflectionUtils.getField(field, from);
|
||||
} else {
|
||||
ReflectionUtils.makeAccessible(getter);
|
||||
obj = ReflectionUtils.invokeMethod(getter, from);
|
||||
}
|
||||
if (null != obj && !type.isAssignableFrom(obj.getClass())) {
|
||||
return conversionService.convert(obj, type);
|
||||
} else {
|
||||
return (T) obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
import org.springframework.data.mapping.model.PreferredConstructor.Parameter;
|
||||
|
||||
/**
|
||||
* Callback interface to lookup values for a given {@link Parameter}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface ParameterValueProvider {
|
||||
<T> T getParameterValue(PreferredConstructor.Parameter<T> parameter);
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -29,7 +28,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Value object to encapsulate the constructor to be used when mapping persistent data to objects.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -38,23 +37,54 @@ public class PreferredConstructor<T> {
|
||||
private final Constructor<T> constructor;
|
||||
private final List<Parameter<?>> parameters;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PreferredConstructor} from the given {@link Constructor} and {@link Parameter}s.
|
||||
*
|
||||
* @param constructor
|
||||
* @param parameters
|
||||
*/
|
||||
public PreferredConstructor(Constructor<T> constructor, Parameter<?>... parameters) {
|
||||
|
||||
Assert.notNull(constructor);
|
||||
Assert.notNull(parameters);
|
||||
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
this.constructor = constructor;
|
||||
this.parameters = Arrays.asList(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying {@link Constructor}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Constructor<T> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
public List<Parameter<?>> getParameters() {
|
||||
/**
|
||||
* Returns the {@link Parameter}s of the constructor.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Iterable<Parameter<?>> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the constructor has {@link Parameter}s.
|
||||
*
|
||||
* @see #isNoArgConstructor()
|
||||
* @return
|
||||
*/
|
||||
public boolean hasParameters() {
|
||||
return !parameters.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the constructor does not have any arguments.
|
||||
*
|
||||
*
|
||||
* @see #hasParameters()
|
||||
* @return
|
||||
*/
|
||||
public boolean isNoArgConstructor() {
|
||||
@@ -63,18 +93,35 @@ public class PreferredConstructor<T> {
|
||||
|
||||
/**
|
||||
* Returns whether the constructor was explicitly selected (by {@link PersistenceConstructor}).
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isExplicitlyAnnotated() {
|
||||
return constructor.isAnnotationPresent(PersistenceConstructor.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object to represent constructor parameters.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the paramter
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static class Parameter<T> {
|
||||
|
||||
private final String name;
|
||||
private final TypeInformation<T> type;
|
||||
private final String key;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Parameter} with the given name, {@link TypeInformation} as well as an array of
|
||||
* {@link Annotation}s. Will insprect the annotations for an {@link Value} annotation to lookup a key or an SpEL
|
||||
* expression to be evaluated.
|
||||
*
|
||||
* @param name the name of the parameter, can be {@literal null}
|
||||
* @param type must not be {@literal null}
|
||||
* @param annotations must not be {@literal null} but can be empty
|
||||
*/
|
||||
public Parameter(String name, TypeInformation<T> type, Annotation[] annotations) {
|
||||
|
||||
Assert.notNull(type);
|
||||
@@ -94,25 +141,40 @@ public class PreferredConstructor<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the parameter or {@literal null} if none was given.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link TypeInformation} of the parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TypeInformation<T> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw resolved type of the parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Class<T> getRawType() {
|
||||
return type.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key to be used when looking up a source data structure to populate the actual parameter value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface ParameterValueProvider {
|
||||
<T> T getParameterValue(Parameter<T> parameter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
import org.springframework.data.mapping.model.PreferredConstructor.Parameter;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ParameterValueProvider} implementation that evaluates the {@link Parameter}s key against
|
||||
* {@link SpelExpressionParser} and {@link EvaluationContext}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class SpELAwareParameterValueProvider implements ParameterValueProvider {
|
||||
|
||||
private final SpelExpressionParser parser;
|
||||
private final EvaluationContext context;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SpELAwareParameterValueProvider} from the given {@link SpelExpressionParser} and {@link EvaluationContext}.
|
||||
*
|
||||
* @param parser must not be {@literal null}
|
||||
* @param context must not be {@literal null}
|
||||
*/
|
||||
public SpELAwareParameterValueProvider(SpelExpressionParser parser, EvaluationContext context) {
|
||||
Assert.notNull(parser);
|
||||
Assert.notNull(context);
|
||||
this.parser = parser;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.PreferredConstructor.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.model.PreferredConstructor.Parameter)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getParameterValue(Parameter<T> parameter) {
|
||||
Expression expression = parser.parseExpression(parameter.getKey());
|
||||
return (T) expression.getValue(context);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.mapping;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -83,10 +84,12 @@ public class PreferredConstructorDiscovererUnitTests {
|
||||
assertThat(constructor.isNoArgConstructor(), is(false));
|
||||
assertThat(constructor.isExplicitlyAnnotated(), is(true));
|
||||
|
||||
List<Parameter<?>> parameters = constructor.getParameters();
|
||||
assertThat(constructor.hasParameters(), is(true));
|
||||
Iterator<Parameter<?>> parameters = constructor.getParameters().iterator();
|
||||
|
||||
assertThat(parameters.size(), is(1));
|
||||
assertThat(parameters.get(0).getType().getType(), typeCompatibleWith(Long.class));
|
||||
Parameter<?> parameter = parameters.next();
|
||||
assertThat(parameter.getType().getType(), typeCompatibleWith(Long.class));
|
||||
assertThat(parameters.hasNext(), is(false));
|
||||
}
|
||||
|
||||
static class EntityWithoutConstructor {
|
||||
|
||||
Reference in New Issue
Block a user