DATACMNS-122 - Introduced further abstractions to improve entity instantiation.

PersistentEntity now has a isConstructorArgument(…) allowing to find out whether a PersistentProperty is referred to from a constructor argument. The PreferredConstructor abstraction now keeps track of the PersistentEntity it is built for and thus allows finding out whether a constructor Parameter maps a PersistentProperty.

Removed bean creation responsibility from BeanWrapper and introduced EntityInstantiator abstraction to allow the entity instantiation mechanism be short circuited via custom implementations. The reflection based implementation from BeanWrapper is now residing in ReflectionEntityInstantiator.
This commit is contained in:
Oliver Gierke
2012-01-23 11:25:18 +01:00
parent 59d8852921
commit 35697d639a
21 changed files with 1003 additions and 161 deletions

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.convert;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import java.util.Collections;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.mapping.PersistentEntity;
/**
* Unit tests for {@link EntityInstantiators}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class EntityInstantiatorsUnitTests {
@Mock
PersistentEntity<?, ?> entity;
@Mock
EntityInstantiator customInstantiator;
@Test(expected = IllegalArgumentException.class)
public void rejectsNullFallbackInstantiator() {
new EntityInstantiators((EntityInstantiator) null);
}
@Test
public void usesReflectionEntityInstantiatorAsDefaultFallback() {
EntityInstantiators instantiators = new EntityInstantiators();
assertThat(instantiators.getInstantiatorFor(entity), is((EntityInstantiator) ReflectionEntityInstantiator.INSTANCE));
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void returnsCustomInstantiatorForTypeIfRegistered() {
when(entity.getType()).thenReturn((Class) String.class);
Map<Class<?>, EntityInstantiator> customInstantiators = Collections.<Class<?>, EntityInstantiator> singletonMap(
String.class, customInstantiator);
EntityInstantiators instantiators = new EntityInstantiators(customInstantiators);
assertThat(instantiators.getInstantiatorFor(entity), is(customInstantiator));
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void usesCustomFallbackInstantiatorsIfConfigured() {
when(entity.getType()).thenReturn((Class) Object.class);
Map<Class<?>, EntityInstantiator> customInstantiators = Collections.<Class<?>, EntityInstantiator> singletonMap(
String.class, ReflectionEntityInstantiator.INSTANCE);
EntityInstantiators instantiators = new EntityInstantiators(customInstantiator, customInstantiators);
instantiators.getInstantiatorFor(entity);
assertThat(instantiators.getInstantiatorFor(entity), is(customInstantiator));
when(entity.getType()).thenReturn((Class) String.class);
assertThat(instantiators.getInstantiatorFor(entity), is((EntityInstantiator) ReflectionEntityInstantiator.INSTANCE));
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.convert;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.convert.ReflectionEntityInstantiator.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.PreferredConstructorDiscoverer;
/**
* Unit tests for {@link ReflectionEntityInstantiator}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class ReflectionEntityInstantiatorUnitTest<P extends PersistentProperty<P>> {
@Mock
PersistentEntity<?, P> entity;
@Mock
ParameterValueProvider<P> provider;
@Mock
PreferredConstructor<?, P> constructor;
@Mock
Parameter<?, P> parameter;
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void instantiatesSimpleObjectCorrectly() {
when(entity.getType()).thenReturn((Class) Object.class);
INSTANCE.createInstance(entity, provider);
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void instantiatesArrayCorrectly() {
when(entity.getType()).thenReturn((Class) String[][].class);
INSTANCE.createInstance(entity, provider);
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void instantiatesTypeWithPreferredConstructorUsingParameterValueProvider() {
PreferredConstructor constructor = new PreferredConstructorDiscoverer<Foo, P>(Foo.class).getConstructor();
when(entity.getType()).thenReturn((Class) Foo.class);
when(entity.getPersistenceConstructor()).thenReturn(constructor);
Object instance = INSTANCE.createInstance(entity, provider);
assertTrue(instance instanceof Foo);
verify(provider, times(1)).getParameterValue((Parameter) constructor.getParameters().iterator().next());
}
static class Foo {
Foo(String foo) {
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.convert;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
/**
*
* @author Oliver Gierke
*/
public class SimpleTypeInformationMapperUnitTests {
@Test
@SuppressWarnings({ "rawtypes" })
public void resolvesTypeByLoadingClass() {
TypeInformationMapper mapper = new SimpleTypeInformationMapper();
TypeInformation type = mapper.resolveTypeFrom("java.lang.String");
TypeInformation expected = ClassTypeInformation.from(String.class);
assertThat(type, is(expected));
}
@Test
public void returnsNullForNonStringKey() {
TypeInformationMapper mapper = new SimpleTypeInformationMapper();
assertThat(mapper.resolveTypeFrom(new Object()), is(nullValue()));
}
@Test
public void returnsNullForEmptyTypeKey() {
TypeInformationMapper mapper = new SimpleTypeInformationMapper();
assertThat(mapper.resolveTypeFrom(""), is(nullValue()));
}
@Test
public void returnsNullForUnloadableClass() {
TypeInformationMapper mapper = new SimpleTypeInformationMapper();
assertThat(mapper.resolveTypeFrom("Foo"), is(nullValue()));
}
@Test
public void usesFullyQualifiedClassNameAsTypeKey() {
TypeInformationMapper mapper = new SimpleTypeInformationMapper();
Object alias = mapper.createAliasFor(ClassTypeInformation.from(String.class));
assertTrue(alias instanceof String);
assertThat(alias, is((Object) String.class.getName()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2012 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.
@@ -25,48 +25,41 @@ import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.model.PreferredConstructorDiscoverer;
/**
* Unit tests for {@link PreferredConstructorDiscoverer}.
*
*
* @author Oliver Gierke
*/
public class PreferredConstructorDiscovererUnitTests {
public class PreferredConstructorDiscovererUnitTests<P extends PersistentProperty<P>> {
@Test
public void findsNoArgConstructorForClassWithoutExplicitConstructor() {
PreferredConstructorDiscoverer<EntityWithoutConstructor> discoverer =
new PreferredConstructorDiscoverer<EntityWithoutConstructor>(
EntityWithoutConstructor.class);
PreferredConstructor<EntityWithoutConstructor> constructor =
discoverer.getConstructor();
PreferredConstructorDiscoverer<EntityWithoutConstructor, P> discoverer = new PreferredConstructorDiscoverer<EntityWithoutConstructor, P>(
EntityWithoutConstructor.class);
PreferredConstructor<EntityWithoutConstructor, P> constructor = discoverer.getConstructor();
assertThat(constructor, is(notNullValue()));
assertThat(constructor.isNoArgConstructor(), is(true));
assertThat(constructor.isExplicitlyAnnotated(), is(false));
}
@Test
public void findsNoArgConstructorForClassWithMultipleConstructorsAndNoArgOne() {
PreferredConstructorDiscoverer<ClassWithEmptyConstructor> discoverer =
new PreferredConstructorDiscoverer<ClassWithEmptyConstructor>(
ClassWithEmptyConstructor.class);
PreferredConstructor<ClassWithEmptyConstructor> constructor =
discoverer.getConstructor();
PreferredConstructorDiscoverer<ClassWithEmptyConstructor, P> discoverer = new PreferredConstructorDiscoverer<ClassWithEmptyConstructor, P>(
ClassWithEmptyConstructor.class);
PreferredConstructor<ClassWithEmptyConstructor, P> constructor = discoverer.getConstructor();
assertThat(constructor, is(notNullValue()));
assertThat(constructor.isNoArgConstructor(), is(true));
assertThat(constructor.isExplicitlyAnnotated(), is(false));
}
@Test
public void doesNotThrowExceptionForMultipleConstructorsAndNoNoArgConstructorWithoutAnnotation() {
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne> discoverer = new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne>(
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne, P> discoverer = new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne, P>(
ClassWithMultipleConstructorsWithoutEmptyOne.class);
assertThat(discoverer.getConstructor(), is(nullValue()));
}
@@ -74,20 +67,18 @@ public class PreferredConstructorDiscovererUnitTests {
@Test
public void usesConstructorWithAnnotationOverEveryOther() {
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation> discoverer =
new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation>(
ClassWithMultipleConstructorsAndAnnotation.class);
PreferredConstructor<ClassWithMultipleConstructorsAndAnnotation> constructor =
discoverer.getConstructor();
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation, P> discoverer = new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation, P>(
ClassWithMultipleConstructorsAndAnnotation.class);
PreferredConstructor<ClassWithMultipleConstructorsAndAnnotation, P> constructor = discoverer.getConstructor();
assertThat(constructor, is(notNullValue()));
assertThat(constructor.isNoArgConstructor(), is(false));
assertThat(constructor.isExplicitlyAnnotated(), is(true));
assertThat(constructor.hasParameters(), is(true));
Iterator<Parameter<?>> parameters = constructor.getParameters().iterator();
Iterator<Parameter<?, P>> parameters = constructor.getParameters().iterator();
Parameter<?> parameter = parameters.next();
Parameter<?, P> parameter = parameters.next();
assertThat(parameter.getType().getType(), typeCompatibleWith(Long.class));
assertThat(parameters.hasNext(), is(false));
}
@@ -107,7 +98,6 @@ public class PreferredConstructorDiscovererUnitTests {
public ClassWithMultipleConstructorsAndEmptyOne(String value) {
}
public ClassWithMultipleConstructorsAndEmptyOne() {
}
}