diff --git a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java index 0474171c3af..007193722e1 100644 --- a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java +++ b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -51,6 +51,7 @@ import org.springframework.util.ReflectionUtils; * {@link GenericArrayType#getGenericComponentType()}) will be automatically wrapped. * * @author Phillip Webb + * @author Juergen Hoeller * @since 4.0 */ abstract class SerializableTypeWrapper { @@ -58,15 +59,10 @@ abstract class SerializableTypeWrapper { private static final Class[] SUPPORTED_SERIALIZABLE_TYPES = { GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class}; - private static final Method EQUALS_METHOD = ReflectionUtils.findMethod(Object.class, - "equals", Object.class); - - private static final Method GET_TYPE_PROVIDER_METHOD = ReflectionUtils.findMethod( - SerializableTypeProxy.class, "getTypeProvider"); - private static final ConcurrentReferenceHashMap cache = new ConcurrentReferenceHashMap(256); + /** * Return a {@link Serializable} variant of {@link Field#getGenericType()}. */ @@ -161,35 +157,33 @@ abstract class SerializableTypeWrapper { for (Class type : SUPPORTED_SERIALIZABLE_TYPES) { if (type.isAssignableFrom(provider.getType().getClass())) { ClassLoader classLoader = provider.getClass().getClassLoader(); - Class[] interfaces = new Class[] { type, - SerializableTypeProxy.class, Serializable.class }; + Class[] interfaces = new Class[] {type, SerializableTypeProxy.class, Serializable.class}; InvocationHandler handler = new TypeProxyInvocationHandler(provider); cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler); cache.put(provider.getType(), cached); return cached; } } - throw new IllegalArgumentException("Unsupported Type class " + provider.getType().getClass().getName()); + throw new IllegalArgumentException("Unsupported Type class: " + provider.getType().getClass().getName()); } /** * Additional interface implemented by the type proxy. */ - static interface SerializableTypeProxy { + interface SerializableTypeProxy { /** * Return the underlying type provider. */ TypeProvider getTypeProvider(); - } /** * A {@link Serializable} interface providing access to a {@link Type}. */ - static interface TypeProvider extends Serializable { + interface TypeProvider extends Serializable { /** * Return the (possibly non {@link Serializable}) {@link Type}. @@ -213,12 +207,11 @@ abstract class SerializableTypeWrapper { public Object getSource() { return null; } - } /** - * {@link Serializable} {@link InvocationHandler} used by the Proxied {@link Type}. + * {@link Serializable} {@link InvocationHandler} used by the proxied {@link Type}. * Provides serialization support and enhances any methods that return {@code Type} * or {@code Type[]}. */ @@ -233,10 +226,7 @@ abstract class SerializableTypeWrapper { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (GET_TYPE_PROVIDER_METHOD.equals(method)) { - return this.provider; - } - if (EQUALS_METHOD.equals(method)) { + if (method.getName().equals("equals")) { Object other = args[0]; // Unwrap proxies for speed if (other instanceof Type) { @@ -244,6 +234,13 @@ abstract class SerializableTypeWrapper { } return this.provider.getType().equals(other); } + else if (method.getName().equals("hashCode")) { + return this.provider.getType().hashCode(); + } + else if (method.getName().equals("getTypeProvider")) { + return this.provider; + } + if (Type.class.equals(method.getReturnType()) && args == null) { return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1)); } @@ -254,6 +251,7 @@ abstract class SerializableTypeWrapper { } return result; } + try { return method.invoke(this.provider.getType(), args); } @@ -376,21 +374,27 @@ abstract class SerializableTypeWrapper { private final int index; - private transient Object result; + private transient Method method; + + private transient volatile Object result; public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) { this.provider = provider; this.methodName = method.getName(); this.index = index; - this.result = ReflectionUtils.invokeMethod(method, provider.getType()); + this.method = method; } @Override public Type getType() { - if (this.result instanceof Type || this.result == null) { - return (Type) this.result; + Object result = this.result; + if (result == null) { + // Lazy invocation of the target method on the provided type + result = ReflectionUtils.invokeMethod(this.method, this.provider.getType()); + // Cache the result for further calls to getType() + this.result = result; } - return ((Type[])this.result)[this.index]; + return (result instanceof Type[] ? ((Type[]) result)[this.index] : (Type) result); } @Override @@ -400,8 +404,9 @@ abstract class SerializableTypeWrapper { private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { inputStream.defaultReadObject(); - Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName); - this.result = ReflectionUtils.invokeMethod(method, this.provider.getType()); + this.method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName); + Assert.state(Type.class.equals(this.method.getReturnType()) || + Type[].class.equals(this.method.getReturnType())); } }