Browse Source

ResolvableType.forRawClass as a straight wrapper for Class.isAssignableFrom

Issue: SPR-12846
pull/759/head
Juergen Hoeller 11 years ago
parent
commit
09027f7972
  1. 2
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
  2. 5
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  3. 45
      spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java
  4. 278
      spring-core/src/main/java/org/springframework/core/ResolvableType.java
  5. 31
      spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java

2
spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java

@ -561,7 +561,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp @@ -561,7 +561,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
return isTypeMatch(name, ResolvableType.forClass(typeToMatch != null ? typeToMatch : Object.class));
return isTypeMatch(name, ResolvableType.forRawClass(typeToMatch));
}
@Override

5
spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

@ -412,8 +412,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -412,8 +412,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Override
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
return doGetBeanNamesForType(ResolvableType.forClass(type != null ? type : Object.class),
includeNonSingletons, allowEagerInit);
return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
}
Map<Class<?>, String[]> cache =
(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
@ -421,7 +420,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -421,7 +420,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (resolvedBeanNames != null) {
return resolvedBeanNames;
}
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forClass(type), includeNonSingletons, allowEagerInit);
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
cache.put(type, resolvedBeanNames);
}

45
spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package org.springframework.beans.factory;
import java.io.Closeable;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.security.AccessControlContext;
@ -32,6 +33,7 @@ import java.util.Locale; @@ -32,6 +33,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import javax.annotation.Priority;
import javax.security.auth.Subject;
@ -1669,13 +1671,21 @@ public class DefaultListableBeanFactoryTests { @@ -1669,13 +1671,21 @@ public class DefaultListableBeanFactoryTests {
@Test
public void testGetBeanNamesForTypeBeforeFactoryBeanCreation() {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
lbf.registerBeanDefinition("factoryBean", new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class.getName()));
lbf.registerBeanDefinition("factoryBean", new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class));
assertFalse(lbf.containsSingleton("factoryBean"));
String[] beanNames = lbf.getBeanNamesForType(Runnable.class, false, false);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = lbf.getBeanNamesForType(Callable.class, false, false);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = lbf.getBeanNamesForType(RepositoryFactoryInformation.class, false, false);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = lbf.getBeanNamesForType(FactoryBean.class, false, false);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
@ -1684,13 +1694,21 @@ public class DefaultListableBeanFactoryTests { @@ -1684,13 +1694,21 @@ public class DefaultListableBeanFactoryTests {
@Test
public void testGetBeanNamesForTypeAfterFactoryBeanCreation() {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
lbf.registerBeanDefinition("factoryBean", new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class.getName()));
lbf.registerBeanDefinition("factoryBean", new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class));
lbf.getBean("&factoryBean");
String[] beanNames = lbf.getBeanNamesForType(Runnable.class, false, false);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = lbf.getBeanNamesForType(Callable.class, false, false);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = lbf.getBeanNamesForType(RepositoryFactoryInformation.class, false, false);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = lbf.getBeanNamesForType(FactoryBean.class, false, false);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
@ -2892,10 +2910,24 @@ public class DefaultListableBeanFactoryTests { @@ -2892,10 +2910,24 @@ public class DefaultListableBeanFactoryTests {
}
public static class FactoryBeanThatShouldntBeCalled implements FactoryBean<Object>, Runnable {
public interface Repository<T, ID extends Serializable> {
}
public interface RepositoryFactoryInformation<T, ID extends Serializable> {
}
public static abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID extends Serializable>
implements RepositoryFactoryInformation<S, ID>, FactoryBean<T> {
}
public static class FactoryBeanThatShouldntBeCalled<T extends Repository<S, ID>, S, ID extends Serializable>
extends RepositoryFactoryBeanSupport<T, S, ID> implements Runnable, Callable<T> {
@Override
public Object getObject() {
public T getObject() {
throw new IllegalStateException();
}
@ -2913,6 +2945,11 @@ public class DefaultListableBeanFactoryTests { @@ -2913,6 +2945,11 @@ public class DefaultListableBeanFactoryTests {
public void run() {
throw new IllegalStateException();
}
@Override
public T call() throws Exception {
throw new IllegalStateException();
}
}

278
spring-core/src/main/java/org/springframework/core/ResolvableType.java

@ -76,7 +76,7 @@ import org.springframework.util.StringUtils; @@ -76,7 +76,7 @@ import org.springframework.util.StringUtils;
* @see #forType(Type)
*/
@SuppressWarnings("serial")
public final class ResolvableType implements Serializable {
public class ResolvableType implements Serializable {
/**
* {@code ResolvableType} returned when no value is available. {@code NONE} is used
@ -122,6 +122,17 @@ public final class ResolvableType implements Serializable { @@ -122,6 +122,17 @@ public final class ResolvableType implements Serializable {
private ResolvableType[] generics;
/**
* Private constructor used to create a new {@link ResolvableType} for cache key purposes.
*/
private ResolvableType(Type type, TypeProvider typeProvider, VariableResolver variableResolver) {
this.type = type;
this.typeProvider = typeProvider;
this.variableResolver = variableResolver;
this.componentType = null;
this.resolved = null;
}
/**
* Private constructor used to create a new {@link ResolvableType} for resolution purposes.
*/
@ -136,14 +147,16 @@ public final class ResolvableType implements Serializable { @@ -136,14 +147,16 @@ public final class ResolvableType implements Serializable {
}
/**
* Private constructor used to create a new {@link ResolvableType} for cache key purposes.
* Private constructor used to create a new {@link ResolvableType} on a {@link Class} basis.
* Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper.
* @since 4.2
*/
private ResolvableType(Type type, TypeProvider typeProvider, VariableResolver variableResolver) {
this.type = type;
this.typeProvider = typeProvider;
this.variableResolver = variableResolver;
private ResolvableType(Class<?> sourceClass) {
this.resolved = (sourceClass != null ? sourceClass : Object.class);
this.type = this.resolved;
this.typeProvider = null;
this.variableResolver = null;
this.componentType = null;
this.resolved = null;
}
@ -160,6 +173,9 @@ public final class ResolvableType implements Serializable { @@ -160,6 +173,9 @@ public final class ResolvableType implements Serializable {
* otherwise {@code null}.
*/
public Class<?> getRawClass() {
if (this.type == this.resolved) {
return this.resolved;
}
Type rawType = this.type;
if (rawType instanceof ParameterizedType) {
rawType = ((ParameterizedType) rawType).getRawType();
@ -619,7 +635,7 @@ public final class ResolvableType implements Serializable { @@ -619,7 +635,7 @@ public final class ResolvableType implements Serializable {
return EMPTY_TYPES_ARRAY;
}
if (this.generics == null) {
if (this.type instanceof Class<?>) {
if (this.type instanceof Class) {
Class<?> typeClass = (Class<?>) this.type;
this.generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver);
}
@ -712,7 +728,7 @@ public final class ResolvableType implements Serializable { @@ -712,7 +728,7 @@ public final class ResolvableType implements Serializable {
}
private Class<?> resolveClass() {
if (this.type instanceof Class<?> || this.type == null) {
if (this.type instanceof Class || this.type == null) {
return (Class<?>) this.type;
}
if (this.type instanceof GenericArrayType) {
@ -783,48 +799,20 @@ public final class ResolvableType implements Serializable { @@ -783,48 +799,20 @@ public final class ResolvableType implements Serializable {
return null;
}
/**
* Return a String representation of this type in its fully resolved form
* (including any generic parameters).
*/
@Override
public String toString() {
if (isArray()) {
return getComponentType() + "[]";
}
if (this.resolved == null) {
return "?";
}
if (this.type instanceof TypeVariable) {
TypeVariable<?> variable = (TypeVariable<?>) this.type;
if (this.variableResolver == null || this.variableResolver.resolveVariable(variable) == null) {
// Don't bother with variable boundaries for toString()...
// Can cause infinite recursions in case of self-references
return "?";
}
}
StringBuilder result = new StringBuilder(this.resolved.getName());
if (hasGenerics()) {
result.append('<');
result.append(StringUtils.arrayToDelimitedString(getGenerics(), ", "));
result.append('>');
}
return result.toString();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(obj instanceof ResolvableType)) {
if (!(other instanceof ResolvableType)) {
return false;
}
ResolvableType other = (ResolvableType) obj;
return (ObjectUtils.nullSafeEquals(this.type, other.type) &&
ObjectUtils.nullSafeEquals(getSource(), other.getSource()) &&
variableResolverSourceEquals(other.variableResolver) &&
ObjectUtils.nullSafeEquals(this.componentType, other.componentType));
ResolvableType otherType = (ResolvableType) other;
return (ObjectUtils.nullSafeEquals(this.type, otherType.type) &&
ObjectUtils.nullSafeEquals(getSource(), otherType.getSource()) &&
variableResolverSourceEquals(otherType.variableResolver) &&
ObjectUtils.nullSafeEquals(this.componentType, otherType.componentType));
}
@Override
@ -836,23 +824,6 @@ public final class ResolvableType implements Serializable { @@ -836,23 +824,6 @@ public final class ResolvableType implements Serializable {
return hashCode;
}
/**
* Custom serialization support for {@link #NONE}.
*/
private Object readResolve() throws ObjectStreamException {
return (this.type == null ? NONE : this);
}
/**
* Adapts this {@link ResolvableType} to a {@link VariableResolver}.
*/
VariableResolver asVariableResolver() {
if (this == NONE) {
return null;
}
return new DefaultVariableResolver();
}
private boolean variableResolverSourceEquals(VariableResolver other) {
if (this.variableResolver == null) {
return (other == null);
@ -871,31 +842,94 @@ public final class ResolvableType implements Serializable { @@ -871,31 +842,94 @@ public final class ResolvableType implements Serializable {
return hashCode;
}
private static ResolvableType[] forTypes(Type[] types, VariableResolver owner) {
ResolvableType[] result = new ResolvableType[types.length];
for (int i = 0; i < types.length; i++) {
result[i] = forType(types[i], owner);
/**
* Adapts this {@link ResolvableType} to a {@link VariableResolver}.
*/
VariableResolver asVariableResolver() {
if (this == NONE) {
return null;
}
return result;
return new DefaultVariableResolver();
}
/**
* Return a {@link ResolvableType} for the specified {@link Class}. For example
* {@code ResolvableType.forClass(MyArrayList.class)}.
* @param sourceClass the source class (must not be {@code null}
* Custom serialization support for {@link #NONE}.
*/
private Object readResolve() throws ObjectStreamException {
return (this.type == null ? NONE : this);
}
/**
* Return a String representation of this type in its fully resolved form
* (including any generic parameters).
*/
@Override
public String toString() {
if (isArray()) {
return getComponentType() + "[]";
}
if (this.resolved == null) {
return "?";
}
if (this.type instanceof TypeVariable) {
TypeVariable<?> variable = (TypeVariable<?>) this.type;
if (this.variableResolver == null || this.variableResolver.resolveVariable(variable) == null) {
// Don't bother with variable boundaries for toString()...
// Can cause infinite recursions in case of self-references
return "?";
}
}
StringBuilder result = new StringBuilder(this.resolved.getName());
if (hasGenerics()) {
result.append('<');
result.append(StringUtils.arrayToDelimitedString(getGenerics(), ", "));
result.append('>');
}
return result.toString();
}
// Factory methods
/**
* Return a {@link ResolvableType} for the specified {@link Class},
* using the full generic type information for assignability checks.
* For example: {@code ResolvableType.forClass(MyArrayList.class)}.
* @param sourceClass the source class ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here}
* @return a {@link ResolvableType} for the specified class
* @see #forClass(Class, Class)
* @see #forClassWithGenerics(Class, Class...)
*/
public static ResolvableType forClass(Class<?> sourceClass) {
Assert.notNull(sourceClass, "Source class must not be null");
return forType(sourceClass);
return new ResolvableType(sourceClass);
}
/**
* Return a {@link ResolvableType} for the specified {@link Class} with a given
* implementation. For example
* {@code ResolvableType.forClass(List.class, MyArrayList.class)}.
* Return a {@link ResolvableType} for the specified {@link Class}, doing
* assignability checks against the raw class only (analogous to
* {@link Class#isAssignableFrom}, which this serves as a wrapper for.
* For example: {@code ResolvableType.forClass(MyArrayList.class)}.
* @param sourceClass the source class ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here}
* @return a {@link ResolvableType} for the specified class
* @since 4.2
* @see #forClass(Class)
* @see #getRawClass()
*/
public static ResolvableType forRawClass(Class<?> sourceClass) {
return new ResolvableType(sourceClass) {
@Override
public boolean isAssignableFrom(Class<?> other) {
return ClassUtils.isAssignable(getRawClass(), other);
}
};
}
/**
* Return a {@link ResolvableType} for the specified {@link Class}
* with a given implementation.
* For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}.
* @param sourceClass the source class (must not be {@code null}
* @param implementationClass the implementation class
* @return a {@link ResolvableType} for the specified class backed by the given
@ -909,6 +943,47 @@ public final class ResolvableType implements Serializable { @@ -909,6 +943,47 @@ public final class ResolvableType implements Serializable {
return (asType == NONE ? forType(sourceClass) : asType);
}
/**
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param sourceClass the source class
* @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, ResolvableType...)
*/
public static ResolvableType forClassWithGenerics(Class<?> sourceClass, Class<?>... generics) {
Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(generics, "Generics must not be null");
ResolvableType[] resolvableGenerics = new ResolvableType[generics.length];
for (int i = 0; i < generics.length; i++) {
resolvableGenerics[i] = forClass(generics[i]);
}
return forClassWithGenerics(sourceClass, resolvableGenerics);
}
/**
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param sourceClass the source class
* @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, Class...)
*/
public static ResolvableType forClassWithGenerics(Class<?> sourceClass, ResolvableType... generics) {
Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(generics, "Generics must not be null");
TypeVariable<?>[] variables = sourceClass.getTypeParameters();
Assert.isTrue(variables.length == generics.length, "Mismatched number of generics specified");
Type[] arguments = new Type[generics.length];
for (int i = 0; i < generics.length; i++) {
ResolvableType generic = generics[i];
Type argument = (generic != null ? generic.getType() : null);
arguments[i] = (argument != null ? argument : variables[i]);
}
ParameterizedType syntheticType = new SyntheticParameterizedType(sourceClass, arguments);
return forType(syntheticType, new TypeVariablesVariableResolver(variables, generics));
}
/**
* Return a {@link ResolvableType} for the specified {@link Field}.
* @param field the source field
@ -1123,50 +1198,17 @@ public final class ResolvableType implements Serializable { @@ -1123,50 +1198,17 @@ public final class ResolvableType implements Serializable {
* @return a {@link ResolvableType} as an array of the specified component type
*/
public static ResolvableType forArrayComponent(ResolvableType componentType) {
Assert.notNull(componentType, "ComponentType must not be null");
Assert.notNull(componentType, "componentType must not be null");
Class<?> arrayClass = Array.newInstance(componentType.resolve(), 0).getClass();
return new ResolvableType(arrayClass, null, null, componentType);
}
/**
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param sourceClass the source class
* @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, ResolvableType...)
*/
public static ResolvableType forClassWithGenerics(Class<?> sourceClass, Class<?>... generics) {
Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(generics, "Generics must not be null");
ResolvableType[] resolvableGenerics = new ResolvableType[generics.length];
for (int i = 0; i < generics.length; i++) {
resolvableGenerics[i] = forClass(generics[i]);
}
return forClassWithGenerics(sourceClass, resolvableGenerics);
}
/**
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param sourceClass the source class
* @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, Class...)
*/
public static ResolvableType forClassWithGenerics(Class<?> sourceClass, ResolvableType... generics) {
Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(generics, "Generics must not be null");
TypeVariable<?>[] variables = sourceClass.getTypeParameters();
Assert.isTrue(variables.length == generics.length, "Mismatched number of generics specified");
Type[] arguments = new Type[generics.length];
for (int i = 0; i < generics.length; i++) {
ResolvableType generic = generics[i];
Type argument = (generic != null ? generic.getType() : null);
arguments[i] = (argument != null ? argument : variables[i]);
private static ResolvableType[] forTypes(Type[] types, VariableResolver owner) {
ResolvableType[] result = new ResolvableType[types.length];
for (int i = 0; i < types.length; i++) {
result[i] = forType(types[i], owner);
}
ParameterizedType syntheticType = new SyntheticParameterizedType(sourceClass, arguments);
return forType(syntheticType, new TypeVariablesVariableResolver(variables, generics));
return result;
}
/**
@ -1223,15 +1265,15 @@ public final class ResolvableType implements Serializable { @@ -1223,15 +1265,15 @@ public final class ResolvableType implements Serializable {
return NONE;
}
// Purge empty entries on access since we don't have a clean-up thread or the like.
cache.purgeUnreferencedEntries();
// For simple Class references, build the wrapper right away -
// no expensive resolution necessary, so not worth caching...
if (type instanceof Class<?>) {
if (type instanceof Class) {
return new ResolvableType(type, typeProvider, variableResolver, null);
}
// Purge empty entries on access since we don't have a clean-up thread or the like.
cache.purgeUnreferencedEntries();
// Check the cache - we may have a ResolvableType which has been resolved before...
ResolvableType key = new ResolvableType(type, typeProvider, variableResolver);
ResolvableType resolvableType = cache.get(key);

31
spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java

@ -101,13 +101,36 @@ public class ResolvableTypeTests { @@ -101,13 +101,36 @@ public class ResolvableTypeTests {
public void forClass() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
assertThat(type.getType(), equalTo((Type) ExtendsList.class));
assertThat(type.getRawClass(), equalTo(ExtendsList.class));
assertTrue(type.isAssignableFrom(ExtendsList.class));
assertFalse(type.isAssignableFrom(ArrayList.class));
}
@Test
public void forClassMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Source class must not be null");
ResolvableType.forClass(null);
public void forClassWithNull() throws Exception {
ResolvableType type = ResolvableType.forClass(null);
assertThat(type.getType(), equalTo((Type) Object.class));
assertThat(type.getRawClass(), equalTo(Object.class));
assertTrue(type.isAssignableFrom(Object.class));
assertTrue(type.isAssignableFrom(String.class));
}
@Test
public void forRawClass() throws Exception {
ResolvableType type = ResolvableType.forRawClass(ExtendsList.class);
assertThat(type.getType(), equalTo((Type) ExtendsList.class));
assertThat(type.getRawClass(), equalTo(ExtendsList.class));
assertTrue(type.isAssignableFrom(ExtendsList.class));
assertFalse(type.isAssignableFrom(ArrayList.class));
}
@Test
public void forRawClassWithNull() throws Exception {
ResolvableType type = ResolvableType.forRawClass(null);
assertThat(type.getType(), equalTo((Type) Object.class));
assertThat(type.getRawClass(), equalTo(Object.class));
assertTrue(type.isAssignableFrom(Object.class));
assertTrue(type.isAssignableFrom(String.class));
}
@Test

Loading…
Cancel
Save