diff --git a/gradle.properties b/gradle.properties
index af8519c3aff3..2edf644e9429 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1 +1 @@
-version=3.2.4.RELEASE
+version=3.2.5.RELEASE
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/Advised.java b/spring-aop/src/main/java/org/springframework/aop/framework/Advised.java
index 3c822cf2da51..232e2ebc3988 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/Advised.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/Advised.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -52,13 +52,13 @@ public interface Advised extends TargetClassAware {
* Return the interfaces proxied by the AOP proxy. Will not
* include the target class, which may also be proxied.
*/
- Class[] getProxiedInterfaces();
+ Class>[] getProxiedInterfaces();
/**
* Determine whether the given interface is proxied.
* @param intf the interface to check
*/
- boolean isInterfaceProxied(Class intf);
+ boolean isInterfaceProxied(Class> intf);
/**
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java
index e5f89177b603..2d6f4b7eb0ee 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -158,7 +158,7 @@ public TargetSource getTargetSource() {
* @see #setTargetSource
* @see #setTarget
*/
- public void setTargetClass(Class targetClass) {
+ public void setTargetClass(Class> targetClass) {
this.targetSource = EmptyTargetSource.forClass(targetClass);
}
@@ -194,7 +194,7 @@ public AdvisorChainFactory getAdvisorChainFactory() {
/**
* Set the interfaces to be proxied.
*/
- public void setInterfaces(Class[] interfaces) {
+ public void setInterfaces(Class>... interfaces) {
Assert.notNull(interfaces, "Interfaces must not be null");
this.interfaces.clear();
for (Class ifc : interfaces) {
@@ -206,7 +206,7 @@ public void setInterfaces(Class[] interfaces) {
* Add a new proxied interface.
* @param intf the additional interface to proxy
*/
- public void addInterface(Class intf) {
+ public void addInterface(Class> intf) {
Assert.notNull(intf, "Interface must not be null");
if (!intf.isInterface()) {
throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface");
@@ -224,15 +224,15 @@ public void addInterface(Class intf) {
* @return {@code true} if the interface was removed; {@code false}
* if the interface was not found and hence could not be removed
*/
- public boolean removeInterface(Class intf) {
+ public boolean removeInterface(Class> intf) {
return this.interfaces.remove(intf);
}
- public Class[] getProxiedInterfaces() {
+ public Class>[] getProxiedInterfaces() {
return this.interfaces.toArray(new Class[this.interfaces.size()]);
}
- public boolean isInterfaceProxied(Class intf) {
+ public boolean isInterfaceProxied(Class> intf) {
for (Class proxyIntf : this.interfaces) {
if (intf.isAssignableFrom(proxyIntf)) {
return true;
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java
index 67e89eeefd26..51f8d01f53c3 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -57,7 +57,7 @@ public ProxyFactory(Object target) {
*
No target, only interfaces. Must add interceptors.
* @param proxyInterfaces the interfaces that the proxy should implement
*/
- public ProxyFactory(Class[] proxyInterfaces) {
+ public ProxyFactory(Class>... proxyInterfaces) {
setInterfaces(proxyInterfaces);
}
@@ -69,7 +69,7 @@ public ProxyFactory(Class[] proxyInterfaces) {
* @param proxyInterface the interface that the proxy should implement
* @param interceptor the interceptor that the proxy should invoke
*/
- public ProxyFactory(Class proxyInterface, Interceptor interceptor) {
+ public ProxyFactory(Class> proxyInterface, Interceptor interceptor) {
addInterface(proxyInterface);
addAdvice(interceptor);
}
@@ -80,7 +80,7 @@ public ProxyFactory(Class proxyInterface, Interceptor interceptor) {
* @param proxyInterface the interface that the proxy should implement
* @param targetSource the TargetSource that the proxy should invoke
*/
- public ProxyFactory(Class proxyInterface, TargetSource targetSource) {
+ public ProxyFactory(Class> proxyInterface, TargetSource targetSource) {
addInterface(proxyInterface);
setTargetSource(targetSource);
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java
index e417f92df027..c484520b89be 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -81,24 +81,32 @@ public List findAdvisorBeans() {
List advisors = new LinkedList();
for (String name : advisorNames) {
- if (isEligibleBean(name) && !this.beanFactory.isCurrentlyInCreation(name)) {
- try {
- advisors.add(this.beanFactory.getBean(name, Advisor.class));
+ if (isEligibleBean(name)) {
+ if (this.beanFactory.isCurrentlyInCreation(name)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Skipping currently created advisor '" + name + "'");
+ }
}
- catch (BeanCreationException ex) {
- Throwable rootCause = ex.getMostSpecificCause();
- if (rootCause instanceof BeanCurrentlyInCreationException) {
- BeanCreationException bce = (BeanCreationException) rootCause;
- if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
- if (logger.isDebugEnabled()) {
- logger.debug("Ignoring currently created advisor '" + name + "': " + ex.getMessage());
+ else {
+ try {
+ advisors.add(this.beanFactory.getBean(name, Advisor.class));
+ }
+ catch (BeanCreationException ex) {
+ Throwable rootCause = ex.getMostSpecificCause();
+ if (rootCause instanceof BeanCurrentlyInCreationException) {
+ BeanCreationException bce = (BeanCreationException) rootCause;
+ if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Skipping advisor '" + name +
+ "' with dependency on currently created bean: " + ex.getMessage());
+ }
+ // Ignore: indicates a reference back to the bean we're trying to advise.
+ // We want to find advisors other than the currently created bean itself.
+ continue;
}
- // Ignore: indicates a reference back to the bean we're trying to advise.
- // We want to find advisors other than the currently created bean itself.
- continue;
}
+ throw ex;
}
- throw ex;
}
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java
index fdd0253d6ed0..0a3e5ba893b7 100644
--- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java
+++ b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -54,7 +54,7 @@ public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder defini
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
- proxyDefinition.setOriginatingBeanDefinition(definition.getBeanDefinition());
+ proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj
index 972a32485939..e8fae8d097f8 100644
--- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj
+++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj
@@ -97,36 +97,34 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth
public void verify() {
if (verified != calls.size()) {
- throw new IllegalStateException("Expected " + calls.size()
- + " calls, received " + verified);
+ throw new IllegalStateException("Expected " + calls.size() + " calls, received " + verified);
}
}
/**
- * Validate the call and provide the expected return value
- * @param lastSig
- * @param args
- * @return
+ * Validate the call and provide the expected return value.
*/
public Object respond(String lastSig, Object[] args) {
Call call = nextCall();
CallResponse responseType = call.responseType;
if (responseType == CallResponse.return_) {
return call.returnValue(lastSig, args);
- } else if(responseType == CallResponse.throw_) {
- return (RuntimeException)call.throwException(lastSig, args);
- } else if(responseType == CallResponse.nothing) {
+ }
+ else if (responseType == CallResponse.throw_) {
+ return call.throwException(lastSig, args);
+ }
+ else if (responseType == CallResponse.nothing) {
// do nothing
}
throw new IllegalStateException("Behavior of " + call + " not specified");
}
private Call nextCall() {
- if (verified > calls.size() - 1) {
- throw new IllegalStateException("Expected " + calls.size()
- + " calls, received " + verified);
+ verified++;
+ if (verified > calls.size()) {
+ throw new IllegalStateException("Expected " + calls.size() + " calls, received " + verified);
}
- return calls.get(verified++);
+ return calls.get(verified);
}
public void expectCall(String lastSig, Object lastArgs[]) {
@@ -171,7 +169,8 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth
expectations.expectCall(thisJoinPointStaticPart.toLongString(), thisJoinPoint.getArgs());
// Return value doesn't matter
return null;
- } else {
+ }
+ else {
return expectations.respond(thisJoinPointStaticPart.toLongString(), thisJoinPoint.getArgs());
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
index 7c8cd51cece1..f83f6acb4eb7 100644
--- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
+++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
@@ -101,15 +101,14 @@ public static void acceptClassLoader(ClassLoader classLoader) {
/**
* Clear the introspection cache for the given ClassLoader, removing the
- * introspection results for all classes underneath that ClassLoader,
- * and deregistering the ClassLoader (and any of its children) from the
- * acceptance list.
+ * introspection results for all classes underneath that ClassLoader, and
+ * removing the ClassLoader (and its children) from the acceptance list.
* @param classLoader the ClassLoader to clear the cache for
*/
public static void clearClassLoader(ClassLoader classLoader) {
synchronized (classCache) {
for (Iterator it = classCache.keySet().iterator(); it.hasNext();) {
- Class beanClass = it.next();
+ Class> beanClass = it.next();
if (isUnderneathClassLoader(beanClass.getClassLoader(), classLoader)) {
it.remove();
}
@@ -127,13 +126,11 @@ public static void clearClassLoader(ClassLoader classLoader) {
/**
* Create CachedIntrospectionResults for the given bean class.
- * We don't want to use synchronization here. Object references are atomic,
- * so we can live with doing the occasional unnecessary lookup at startup only.
* @param beanClass the bean class to analyze
* @return the corresponding CachedIntrospectionResults
* @throws BeansException in case of introspection failure
*/
- static CachedIntrospectionResults forClass(Class beanClass) throws BeansException {
+ static CachedIntrospectionResults forClass(Class> beanClass) throws BeansException {
CachedIntrospectionResults results;
Object value;
synchronized (classCache) {
@@ -225,7 +222,7 @@ private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoade
* @param beanClass the bean class to analyze
* @throws BeansException in case of introspection failure
*/
- private CachedIntrospectionResults(Class beanClass) throws BeansException {
+ private CachedIntrospectionResults(Class> beanClass) throws BeansException {
try {
if (logger.isTraceEnabled()) {
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
@@ -248,7 +245,7 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException {
// garbage collection on class loader shutdown - we cache it here anyway,
// in a GC-friendly manner. In contrast to CachedIntrospectionResults,
// Introspector does not use WeakReferences as values of its WeakHashMap!
- Class classToFlush = beanClass;
+ Class> classToFlush = beanClass;
do {
Introspector.flushFromCaches(classToFlush);
classToFlush = classToFlush.getSuperclass();
@@ -286,7 +283,7 @@ BeanInfo getBeanInfo() {
return this.beanInfo;
}
- Class getBeanClass() {
+ Class> getBeanClass() {
return this.beanInfo.getBeanDescriptor().getBeanClass();
}
@@ -314,7 +311,7 @@ PropertyDescriptor[] getPropertyDescriptors() {
return pds;
}
- private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class beanClass, PropertyDescriptor pd) {
+ private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class> beanClass, PropertyDescriptor pd) {
try {
return new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(), pd.getReadMethod(),
pd.getWriteMethod(), pd.getPropertyEditorClass());
diff --git a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
index 6606741ee4e0..971f009ac668 100644
--- a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
+++ b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
@@ -17,7 +17,6 @@
package org.springframework.beans;
import java.awt.Image;
-
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
@@ -26,10 +25,8 @@
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
-
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -84,8 +81,8 @@ class ExtendedBeanInfo implements BeanInfo {
/**
* Wrap the given {@link BeanInfo} instance; copy all its existing property descriptors
- * locally, wrapping each in a custom {@link SimpleIndexedPropertyDescriptor indexed} or
- * {@link SimpleNonIndexedPropertyDescriptor non-indexed} {@code PropertyDescriptor}
+ * locally, wrapping each in a custom {@link SimpleIndexedPropertyDescriptor indexed}
+ * or {@link SimplePropertyDescriptor non-indexed} {@code PropertyDescriptor}
* variant that bypasses default JDK weak/soft reference management; then search
* through its method descriptors to find any non-void returning write methods and
* update or create the corresponding {@link PropertyDescriptor} for each one found.
@@ -96,15 +93,16 @@ class ExtendedBeanInfo implements BeanInfo {
*/
public ExtendedBeanInfo(BeanInfo delegate) throws IntrospectionException {
this.delegate = delegate;
-
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
this.propertyDescriptors.add(pd instanceof IndexedPropertyDescriptor ?
new SimpleIndexedPropertyDescriptor((IndexedPropertyDescriptor) pd) :
- new SimpleNonIndexedPropertyDescriptor(pd));
+ new SimplePropertyDescriptor(pd));
}
-
- for (Method method : findCandidateWriteMethods(delegate.getMethodDescriptors())) {
- handleCandidateWriteMethod(method);
+ MethodDescriptor[] methodDescriptors = delegate.getMethodDescriptors();
+ if (methodDescriptors != null) {
+ for (Method method : findCandidateWriteMethods(methodDescriptors)) {
+ handleCandidateWriteMethod(method);
+ }
}
}
@@ -132,58 +130,44 @@ public static boolean isCandidateWriteMethod(Method method) {
String methodName = method.getName();
Class>[] parameterTypes = method.getParameterTypes();
int nParams = parameterTypes.length;
- if (methodName.length() > 3 && methodName.startsWith("set") &&
- Modifier.isPublic(method.getModifiers()) &&
- (
- !void.class.isAssignableFrom(method.getReturnType()) ||
- Modifier.isStatic(method.getModifiers())
- ) &&
- (nParams == 1 || (nParams == 2 && parameterTypes[0].equals(int.class)))) {
- return true;
- }
- return false;
+ return methodName.length() > 3 && methodName.startsWith("set") && Modifier.isPublic(method.getModifiers()) &&
+ (!void.class.isAssignableFrom(method.getReturnType()) || Modifier.isStatic(method.getModifiers())) &&
+ (nParams == 1 || (nParams == 2 && parameterTypes[0].equals(int.class)));
}
private void handleCandidateWriteMethod(Method method) throws IntrospectionException {
int nParams = method.getParameterTypes().length;
String propertyName = propertyNameFor(method);
Class> propertyType = method.getParameterTypes()[nParams-1];
- PropertyDescriptor existingPD = findExistingPropertyDescriptor(propertyName, propertyType);
+ PropertyDescriptor existingPd = findExistingPropertyDescriptor(propertyName, propertyType);
if (nParams == 1) {
- if (existingPD == null) {
- this.propertyDescriptors.add(
- new SimpleNonIndexedPropertyDescriptor(propertyName, null, method));
+ if (existingPd == null) {
+ this.propertyDescriptors.add(new SimplePropertyDescriptor(propertyName, null, method));
}
else {
- existingPD.setWriteMethod(method);
+ existingPd.setWriteMethod(method);
}
}
else if (nParams == 2) {
- if (existingPD == null) {
+ if (existingPd == null) {
this.propertyDescriptors.add(
- new SimpleIndexedPropertyDescriptor(
- propertyName, null, null, null, method));
+ new SimpleIndexedPropertyDescriptor(propertyName, null, null, null, method));
}
- else if (existingPD instanceof IndexedPropertyDescriptor) {
- ((IndexedPropertyDescriptor)existingPD).setIndexedWriteMethod(method);
+ else if (existingPd instanceof IndexedPropertyDescriptor) {
+ ((IndexedPropertyDescriptor) existingPd).setIndexedWriteMethod(method);
}
else {
- this.propertyDescriptors.remove(existingPD);
- this.propertyDescriptors.add(
- new SimpleIndexedPropertyDescriptor(
- propertyName, existingPD.getReadMethod(),
- existingPD.getWriteMethod(), null, method));
+ this.propertyDescriptors.remove(existingPd);
+ this.propertyDescriptors.add(new SimpleIndexedPropertyDescriptor(
+ propertyName, existingPd.getReadMethod(), existingPd.getWriteMethod(), null, method));
}
}
else {
- throw new IllegalArgumentException(
- "write method must have exactly 1 or 2 parameters: " + method);
+ throw new IllegalArgumentException("Write method must have exactly 1 or 2 parameters: " + method);
}
}
- private PropertyDescriptor findExistingPropertyDescriptor(
- String propertyName, Class> propertyType) {
-
+ private PropertyDescriptor findExistingPropertyDescriptor(String propertyName, Class> propertyType) {
for (PropertyDescriptor pd : this.propertyDescriptors) {
final Class> candidateType;
final String candidateName = pd.getName();
@@ -191,16 +175,14 @@ private PropertyDescriptor findExistingPropertyDescriptor(
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
candidateType = ipd.getIndexedPropertyType();
if (candidateName.equals(propertyName) &&
- (candidateType.equals(propertyType) ||
- candidateType.equals(propertyType.getComponentType()))) {
+ (candidateType.equals(propertyType) || candidateType.equals(propertyType.getComponentType()))) {
return pd;
}
}
else {
candidateType = pd.getPropertyType();
if (candidateName.equals(propertyName) &&
- (candidateType.equals(propertyType) ||
- propertyType.equals(candidateType.getComponentType()))) {
+ (candidateType.equals(propertyType) || propertyType.equals(candidateType.getComponentType()))) {
return pd;
}
}
@@ -209,8 +191,7 @@ private PropertyDescriptor findExistingPropertyDescriptor(
}
private String propertyNameFor(Method method) {
- return Introspector.decapitalize(
- method.getName().substring(3, method.getName().length()));
+ return Introspector.decapitalize(method.getName().substring(3, method.getName().length()));
}
@@ -221,8 +202,7 @@ private String propertyNameFor(Method method) {
* @see #ExtendedBeanInfo(BeanInfo)
*/
public PropertyDescriptor[] getPropertyDescriptors() {
- return this.propertyDescriptors.toArray(
- new PropertyDescriptor[this.propertyDescriptors.size()]);
+ return this.propertyDescriptors.toArray(new PropertyDescriptor[this.propertyDescriptors.size()]);
}
public BeanInfo[] getAdditionalBeanInfo() {
@@ -255,31 +235,28 @@ public MethodDescriptor[] getMethodDescriptors() {
}
-class SimpleNonIndexedPropertyDescriptor extends PropertyDescriptor {
+class SimplePropertyDescriptor extends PropertyDescriptor {
private Method readMethod;
+
private Method writeMethod;
- private Class> propertyType;
- private Class> propertyEditorClass;
+ private Class> propertyType;
- public SimpleNonIndexedPropertyDescriptor(PropertyDescriptor original)
- throws IntrospectionException {
+ private Class> propertyEditorClass;
+ public SimplePropertyDescriptor(PropertyDescriptor original) throws IntrospectionException {
this(original.getName(), original.getReadMethod(), original.getWriteMethod());
copyNonMethodProperties(original, this);
}
- public SimpleNonIndexedPropertyDescriptor(String propertyName,
- Method readMethod, Method writeMethod) throws IntrospectionException {
-
+ public SimplePropertyDescriptor(String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException {
super(propertyName, null, null);
- this.setReadMethod(readMethod);
- this.setWriteMethod(writeMethod);
+ this.readMethod = readMethod;
+ this.writeMethod = writeMethod;
this.propertyType = findPropertyType(readMethod, writeMethod);
}
-
@Override
public Method getReadMethod() {
return this.readMethod;
@@ -305,7 +282,8 @@ public Class> getPropertyType() {
if (this.propertyType == null) {
try {
this.propertyType = findPropertyType(this.readMethod, this.writeMethod);
- } catch (IntrospectionException ex) {
+ }
+ catch (IntrospectionException ex) {
// ignore, as does PropertyDescriptor#getPropertyType
}
}
@@ -322,7 +300,6 @@ public void setPropertyEditorClass(Class> propertyEditorClass) {
this.propertyEditorClass = propertyEditorClass;
}
-
@Override
public boolean equals(Object obj) {
return PropertyDescriptorUtils.equals(this, obj);
@@ -331,8 +308,7 @@ public boolean equals(Object obj) {
@Override
public String toString() {
return String.format("%s[name=%s, propertyType=%s, readMethod=%s, writeMethod=%s]",
- this.getClass().getSimpleName(), this.getName(), this.getPropertyType(),
- this.readMethod, this.writeMethod);
+ getClass().getSimpleName(), getName(), getPropertyType(), this.readMethod, this.writeMethod);
}
}
@@ -340,40 +316,37 @@ public String toString() {
class SimpleIndexedPropertyDescriptor extends IndexedPropertyDescriptor {
private Method readMethod;
+
private Method writeMethod;
+
private Class> propertyType;
- private Class> propertyEditorClass;
private Method indexedReadMethod;
+
private Method indexedWriteMethod;
- private Class> indexedPropertyType;
+ private Class> indexedPropertyType;
- public SimpleIndexedPropertyDescriptor(IndexedPropertyDescriptor original)
- throws IntrospectionException {
+ private Class> propertyEditorClass;
+ public SimpleIndexedPropertyDescriptor(IndexedPropertyDescriptor original) throws IntrospectionException {
this(original.getName(), original.getReadMethod(), original.getWriteMethod(),
original.getIndexedReadMethod(), original.getIndexedWriteMethod());
copyNonMethodProperties(original, this);
}
- public SimpleIndexedPropertyDescriptor(String propertyName,
- Method readMethod, Method writeMethod,
- Method indexedReadMethod, Method indexedWriteMethod)
- throws IntrospectionException {
+ public SimpleIndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod,
+ Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException {
super(propertyName, null, null, null, null);
- this.setReadMethod(readMethod);
- this.setWriteMethod(writeMethod);
+ this.readMethod = readMethod;
+ this.writeMethod = writeMethod;
this.propertyType = findPropertyType(readMethod, writeMethod);
-
- this.setIndexedReadMethod(indexedReadMethod);
- this.setIndexedWriteMethod(indexedWriteMethod);
- this.indexedPropertyType = findIndexedPropertyType(
- this.getName(), this.propertyType, indexedReadMethod, indexedWriteMethod);
+ this.indexedReadMethod = indexedReadMethod;
+ this.indexedWriteMethod = indexedWriteMethod;
+ this.indexedPropertyType = findIndexedPropertyType(propertyName, this.propertyType, indexedReadMethod, indexedWriteMethod);
}
-
@Override
public Method getReadMethod() {
return this.readMethod;
@@ -399,7 +372,8 @@ public Class> getPropertyType() {
if (this.propertyType == null) {
try {
this.propertyType = findPropertyType(this.readMethod, this.writeMethod);
- } catch (IntrospectionException ex) {
+ }
+ catch (IntrospectionException ex) {
// ignore, as does IndexedPropertyDescriptor#getPropertyType
}
}
@@ -431,9 +405,9 @@ public Class> getIndexedPropertyType() {
if (this.indexedPropertyType == null) {
try {
this.indexedPropertyType = findIndexedPropertyType(
- this.getName(), this.getPropertyType(),
- this.indexedReadMethod, this.indexedWriteMethod);
- } catch (IntrospectionException ex) {
+ getName(), getPropertyType(), this.indexedReadMethod, this.indexedWriteMethod);
+ }
+ catch (IntrospectionException ex) {
// ignore, as does IndexedPropertyDescriptor#getIndexedPropertyType
}
}
@@ -450,26 +424,22 @@ public void setPropertyEditorClass(Class> propertyEditorClass) {
this.propertyEditorClass = propertyEditorClass;
}
-
/*
- * @see java.beans.IndexedPropertyDescriptor#equals(java.lang.Object)
+ * See java.beans.IndexedPropertyDescriptor#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
-
if (obj != null && obj instanceof IndexedPropertyDescriptor) {
IndexedPropertyDescriptor other = (IndexedPropertyDescriptor) obj;
if (!compareMethods(getIndexedReadMethod(), other.getIndexedReadMethod())) {
return false;
}
-
if (!compareMethods(getIndexedWriteMethod(), other.getIndexedWriteMethod())) {
return false;
}
-
if (getIndexedPropertyType() != other.getIndexedPropertyType()) {
return false;
}
@@ -482,9 +452,8 @@ public boolean equals(Object obj) {
public String toString() {
return String.format("%s[name=%s, propertyType=%s, indexedPropertyType=%s, " +
"readMethod=%s, writeMethod=%s, indexedReadMethod=%s, indexedWriteMethod=%s]",
- this.getClass().getSimpleName(), this.getName(), this.getPropertyType(),
- this.getIndexedPropertyType(), this.readMethod, this.writeMethod,
- this.indexedReadMethod, this.indexedWriteMethod);
+ getClass().getSimpleName(), getName(), getPropertyType(), getIndexedPropertyType(),
+ this.readMethod, this.writeMethod, this.indexedReadMethod, this.indexedWriteMethod);
}
}
@@ -492,7 +461,7 @@ public String toString() {
class PropertyDescriptorUtils {
/*
- * see java.beans.FeatureDescriptor#FeatureDescriptor(FeatureDescriptor)
+ * See java.beans.FeatureDescriptor#FeatureDescriptor(FeatureDescriptor)
*/
public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDescriptor target)
throws IntrospectionException {
@@ -520,11 +489,8 @@ public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDe
/*
* See PropertyDescriptor#findPropertyType
*/
- public static Class> findPropertyType(Method readMethod, Method writeMethod)
- throws IntrospectionException {
-
+ public static Class> findPropertyType(Method readMethod, Method writeMethod) throws IntrospectionException {
Class> propertyType = null;
-
if (readMethod != null) {
Class>[] params = readMethod.getParameterTypes();
if (params.length != 0) {
@@ -554,8 +520,7 @@ public static Class> findPropertyType(Method readMethod, Method writeMethod)
* See IndexedPropertyDescriptor#findIndexedPropertyType
*/
public static Class> findIndexedPropertyType(String name, Class> propertyType,
- Method indexedReadMethod, Method indexedWriteMethod)
- throws IntrospectionException {
+ Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException {
Class> indexedPropertyType = null;
@@ -605,7 +570,6 @@ public static Class> findIndexedPropertyType(String name, Class> propertyTyp
* return {@code true} if they are objects are equivalent, i.e. both are {@code
* PropertyDescriptor}s whose read method, write method, property types, property
* editor and flags are equivalent.
- *
* @see PropertyDescriptor#equals(Object)
*/
public static boolean equals(PropertyDescriptor pd1, Object obj) {
@@ -633,13 +597,12 @@ public static boolean equals(PropertyDescriptor pd1, Object obj) {
}
/*
- * see PropertyDescriptor#compareMethods
+ * See PropertyDescriptor#compareMethods
*/
public static boolean compareMethods(Method a, Method b) {
if ((a == null) != (b == null)) {
return false;
}
-
if (a != null && b != null) {
if (!a.equals(b)) {
return false;
@@ -653,7 +616,6 @@ public static boolean compareMethods(Method a, Method b) {
/**
* Sorts PropertyDescriptor instances alpha-numerically to emulate the behavior of
* {@link java.beans.BeanInfo#getPropertyDescriptors()}.
- *
* @see ExtendedBeanInfo#propertyDescriptors
*/
class PropertyDescriptorComparator implements Comparator {
diff --git a/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java
index 0f0c5931627d..096bae5a6177 100644
--- a/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -31,32 +31,32 @@
import org.springframework.util.StringUtils;
/**
- * Extension of the standard JavaBeans PropertyDescriptor class,
- * overriding {@code getPropertyType()} such that a generically
- * declared type will be resolved against the containing bean class.
+ * Extension of the standard JavaBeans {@link PropertyDescriptor} class,
+ * overriding {@code getPropertyType()} such that a generically declared
+ * type variable will be resolved against the containing bean class.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
- private final Class beanClass;
+ private final Class> beanClass;
private final Method readMethod;
private final Method writeMethod;
- private final Class propertyEditorClass;
+ private final Class> propertyEditorClass;
private volatile Set ambiguousWriteMethods;
- private Class propertyType;
+ private Class> propertyType;
private MethodParameter writeMethodParameter;
- public GenericTypeAwarePropertyDescriptor(Class beanClass, String propertyName,
- Method readMethod, Method writeMethod, Class propertyEditorClass)
+ public GenericTypeAwarePropertyDescriptor(Class> beanClass, String propertyName,
+ Method readMethod, Method writeMethod, Class> propertyEditorClass)
throws IntrospectionException {
super(propertyName, null, null);
@@ -69,8 +69,11 @@ public GenericTypeAwarePropertyDescriptor(Class beanClass, String propertyName,
// Fallback: Original JavaBeans introspection might not have found matching setter
// method due to lack of bridge method resolution, in case of the getter using a
// covariant return type whereas the setter is defined for the concrete property type.
- writeMethodToUse = ClassUtils.getMethodIfAvailable(this.beanClass,
- "set" + StringUtils.capitalize(getName()), readMethodToUse.getReturnType());
+ Method candidate = ClassUtils.getMethodIfAvailable(
+ this.beanClass, "set" + StringUtils.capitalize(getName()), (Class>[]) null);
+ if (candidate != null && candidate.getParameterTypes().length == 1) {
+ writeMethodToUse = candidate;
+ }
}
this.readMethod = readMethodToUse;
this.writeMethod = writeMethodToUse;
@@ -118,12 +121,12 @@ public Method getWriteMethodForActualAccess() {
}
@Override
- public Class getPropertyEditorClass() {
+ public Class> getPropertyEditorClass() {
return this.propertyEditorClass;
}
@Override
- public synchronized Class getPropertyType() {
+ public synchronized Class> getPropertyType() {
if (this.propertyType == null) {
if (this.readMethod != null) {
this.propertyType = GenericTypeResolver.resolveReturnType(this.readMethod, this.beanClass);
diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
index 24bc57e9e325..1c59360b6a8c 100644
--- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
+++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
@@ -187,6 +187,9 @@ public T convertIfNecessary(String propertyName, Object oldValue, Object new
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
+ if (Object.class.equals(requiredType)) {
+ return (T) convertedValue;
+ }
if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java
index 657bb1ebd1dc..6952087521db 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java
@@ -139,7 +139,7 @@ public static String[] beanNamesIncludingAncestors(ListableBeanFactory lbf) {
* @param type the type that beans must match
* @return the array of matching bean names, or an empty array if none
*/
- public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class type) {
+ public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class> type) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForType(type);
if (lbf instanceof HierarchicalBeanFactory) {
@@ -181,7 +181,7 @@ public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lb
* @return the array of matching bean names, or an empty array if none
*/
public static String[] beanNamesForTypeIncludingAncestors(
- ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) {
+ ListableBeanFactory lbf, Class> type, boolean includeNonSingletons, boolean allowEagerInit) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
index 6775dd78edcd..ecf5044f04d8 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -59,6 +59,7 @@
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
@@ -121,8 +122,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
private final Map, Constructor>[]> candidateConstructorsCache =
new ConcurrentHashMap, Constructor>[]>(64);
- private final Map, InjectionMetadata> injectionMetadataCache =
- new ConcurrentHashMap, InjectionMetadata>(64);
+ private final Map injectionMetadataCache =
+ new ConcurrentHashMap(64);
/**
@@ -214,7 +215,7 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
if (beanType != null) {
- InjectionMetadata metadata = findAutowiringMetadata(beanType);
+ InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType);
metadata.checkConfigMembers(beanDefinition);
}
}
@@ -280,7 +281,7 @@ else if (candidate.getParameterTypes().length == 0) {
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
- InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
+ InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass());
try {
metadata.inject(bean, beanName, pvs);
}
@@ -298,7 +299,7 @@ public PropertyValues postProcessPropertyValues(
*/
public void processInjection(Object bean) throws BeansException {
Class> clazz = bean.getClass();
- InjectionMetadata metadata = findAutowiringMetadata(clazz);
+ InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz);
try {
metadata.inject(bean, null, null);
}
@@ -308,15 +309,17 @@ public void processInjection(Object bean) throws BeansException {
}
- private InjectionMetadata findAutowiringMetadata(Class> clazz) {
+ private InjectionMetadata findAutowiringMetadata(String beanName, Class> clazz) {
// Quick check on the concurrent map first, with minimal locking.
- InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
+ // Fall back to class name as cache key, for backwards compatibility with custom callers.
+ String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
+ InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (metadata == null) {
synchronized (this.injectionMetadataCache) {
- metadata = this.injectionMetadataCache.get(clazz);
+ metadata = this.injectionMetadataCache.get(cacheKey);
if (metadata == null) {
metadata = buildAutowiringMetadata(clazz);
- this.injectionMetadataCache.put(clazz, metadata);
+ this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java
index fe3e3b15efd7..26a0d0b7f749 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -48,14 +48,14 @@ public class InjectionMetadata {
private final Log logger = LogFactory.getLog(InjectionMetadata.class);
- private final Class targetClass;
+ private final Class> targetClass;
private final Collection injectedElements;
private volatile Set checkedElements;
- public InjectionMetadata(Class targetClass, Collection elements) {
+ public InjectionMetadata(Class> targetClass, Collection elements) {
this.targetClass = targetClass;
this.injectedElements = elements;
}
@@ -110,7 +110,7 @@ public final Member getMember() {
return this.member;
}
- protected final Class getResourceType() {
+ protected final Class> getResourceType() {
if (this.isField) {
return ((Field) this.member).getType();
}
@@ -122,16 +122,16 @@ else if (this.pd != null) {
}
}
- protected final void checkResourceType(Class resourceType) {
+ protected final void checkResourceType(Class> resourceType) {
if (this.isField) {
- Class fieldType = ((Field) this.member).getType();
+ Class> fieldType = ((Field) this.member).getType();
if (!(resourceType.isAssignableFrom(fieldType) || fieldType.isAssignableFrom(resourceType))) {
throw new IllegalStateException("Specified field type [" + fieldType +
"] is incompatible with resource type [" + resourceType.getName() + "]");
}
}
else {
- Class paramType =
+ Class> paramType =
(this.pd != null ? this.pd.getPropertyType() : ((Method) this.member).getParameterTypes()[0]);
if (!(resourceType.isAssignableFrom(paramType) || paramType.isAssignableFrom(resourceType))) {
throw new IllegalStateException("Specified parameter type [" + paramType +
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
index 657ee22666b0..76c36efbba96 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -233,8 +233,9 @@ protected boolean checkQualifier(
}
if (qualifier == null) {
Annotation targetAnnotation = null;
- if (bd.getResolvedFactoryMethod() != null) {
- targetAnnotation = AnnotationUtils.getAnnotation(bd.getResolvedFactoryMethod(), type);
+ Method resolvedFactoryMethod = bd.getResolvedFactoryMethod();
+ if (resolvedFactoryMethod != null) {
+ targetAnnotation = AnnotationUtils.getAnnotation(resolvedFactoryMethod, type);
}
if (targetAnnotation == null) {
// look for matching annotation on the target class
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java
index 1e59e7d134c7..5bf2fffcdf98 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -146,7 +146,7 @@ public boolean hasIndexedArgumentValue(int index) {
* untyped values only)
* @return the ValueHolder for the argument, or {@code null} if none set
*/
- public ValueHolder getIndexedArgumentValue(int index, Class requiredType) {
+ public ValueHolder getIndexedArgumentValue(int index, Class> requiredType) {
return getIndexedArgumentValue(index, requiredType, null);
}
@@ -159,7 +159,7 @@ public ValueHolder getIndexedArgumentValue(int index, Class requiredType) {
* unnamed values only)
* @return the ValueHolder for the argument, or {@code null} if none set
*/
- public ValueHolder getIndexedArgumentValue(int index, Class requiredType, String requiredName) {
+ public ValueHolder getIndexedArgumentValue(int index, Class> requiredType, String requiredName) {
Assert.isTrue(index >= 0, "Index must not be negative");
ValueHolder valueHolder = this.indexedArgumentValues.get(index);
if (valueHolder != null &&
@@ -247,7 +247,7 @@ private void addOrMergeGenericArgumentValue(ValueHolder newValue) {
* @param requiredType the type to match
* @return the ValueHolder for the argument, or {@code null} if none set
*/
- public ValueHolder getGenericArgumentValue(Class requiredType) {
+ public ValueHolder getGenericArgumentValue(Class> requiredType) {
return getGenericArgumentValue(requiredType, null, null);
}
@@ -257,7 +257,7 @@ public ValueHolder getGenericArgumentValue(Class requiredType) {
* @param requiredName the name to match
* @return the ValueHolder for the argument, or {@code null} if none set
*/
- public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName) {
+ public ValueHolder getGenericArgumentValue(Class> requiredType, String requiredName) {
return getGenericArgumentValue(requiredType, requiredName, null);
}
@@ -273,7 +273,7 @@ public ValueHolder getGenericArgumentValue(Class requiredType, String requiredNa
* in the current resolution process and should therefore not be returned again
* @return the ValueHolder for the argument, or {@code null} if none found
*/
- public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName, Set usedValueHolders) {
+ public ValueHolder getGenericArgumentValue(Class> requiredType, String requiredName, Set usedValueHolders) {
for (ValueHolder valueHolder : this.genericArgumentValues) {
if (usedValueHolders != null && usedValueHolders.contains(valueHolder)) {
continue;
@@ -309,10 +309,10 @@ public List getGenericArgumentValues() {
* Look for an argument value that either corresponds to the given index
* in the constructor argument list or generically matches by type.
* @param index the index in the constructor argument list
- * @param requiredType the type to match
+ * @param requiredType the parameter type to match
* @return the ValueHolder for the argument, or {@code null} if none set
*/
- public ValueHolder getArgumentValue(int index, Class requiredType) {
+ public ValueHolder getArgumentValue(int index, Class> requiredType) {
return getArgumentValue(index, requiredType, null, null);
}
@@ -320,11 +320,11 @@ public ValueHolder getArgumentValue(int index, Class requiredType) {
* Look for an argument value that either corresponds to the given index
* in the constructor argument list or generically matches by type.
* @param index the index in the constructor argument list
- * @param requiredType the type to match
- * @param requiredName the name to match
+ * @param requiredType the parameter type to match
+ * @param requiredName the parameter name to match
* @return the ValueHolder for the argument, or {@code null} if none set
*/
- public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName) {
+ public ValueHolder getArgumentValue(int index, Class> requiredType, String requiredName) {
return getArgumentValue(index, requiredType, requiredName, null);
}
@@ -332,15 +332,17 @@ public ValueHolder getArgumentValue(int index, Class requiredType, String requir
* Look for an argument value that either corresponds to the given index
* in the constructor argument list or generically matches by type.
* @param index the index in the constructor argument list
- * @param requiredType the type to match (can be {@code null} to find
- * an untyped argument value)
+ * @param requiredType the parameter type to match (can be {@code null}
+ * to find an untyped argument value)
+ * @param requiredName the parameter name to match (can be {@code null}
+ * to find an unnamed argument value)
* @param usedValueHolders a Set of ValueHolder objects that have already
* been used in the current resolution process and should therefore not
* be returned again (allowing to return the next generic argument match
* in case of multiple generic argument values of the same type)
* @return the ValueHolder for the argument, or {@code null} if none set
*/
- public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName, Set usedValueHolders) {
+ public ValueHolder getArgumentValue(int index, Class> requiredType, String requiredName, Set usedValueHolders) {
Assert.isTrue(index >= 0, "Index must not be negative");
ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType, requiredName);
if (valueHolder == null) {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
index 2693f8246c50..35bdfd904b23 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -44,7 +44,7 @@ public class DependencyDescriptor implements Serializable {
private transient Field field;
- private Class declaringClass;
+ private Class> declaringClass;
private String methodName;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java
index e4f27fec8b2e..40f7e3f24e9b 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -61,7 +61,7 @@ public TypedStringValue(String value) {
* @param value the String value
* @param targetType the type to convert to
*/
- public TypedStringValue(String value, Class targetType) {
+ public TypedStringValue(String value, Class> targetType) {
setValue(value);
setTargetType(targetType);
}
@@ -101,7 +101,7 @@ public String getValue() {
* for example in BeanFactoryPostProcessors.
* @see PropertyPlaceholderConfigurer
*/
- public void setTargetType(Class targetType) {
+ public void setTargetType(Class> targetType) {
Assert.notNull(targetType, "'targetType' must not be null");
this.targetType = targetType;
}
@@ -109,7 +109,7 @@ public void setTargetType(Class targetType) {
/**
* Return the type to convert to.
*/
- public Class getTargetType() {
+ public Class> getTargetType() {
Object targetTypeValue = this.targetType;
if (!(targetTypeValue instanceof Class)) {
throw new IllegalStateException("Typed String value does not carry a resolved target type");
@@ -153,11 +153,11 @@ public boolean hasTargetType() {
* @return the resolved type to convert to
* @throws ClassNotFoundException if the type cannot be resolved
*/
- public Class resolveTargetType(ClassLoader classLoader) throws ClassNotFoundException {
+ public Class> resolveTargetType(ClassLoader classLoader) throws ClassNotFoundException {
if (this.targetType == null) {
return null;
}
- Class resolvedClass = ClassUtils.forName(getTargetTypeName(), classLoader);
+ Class> resolvedClass = ClassUtils.forName(getTargetTypeName(), classLoader);
this.targetType = resolvedClass;
return resolvedClass;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
index 600524d4da0f..74e0ce73ed06 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
@@ -63,7 +63,7 @@
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
@@ -569,7 +569,7 @@ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
}
@Override
- protected Class> predictBeanType(String beanName, RootBeanDefinition mbd, Class... typesToMatch) {
+ protected Class> predictBeanType(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
Class> targetType = mbd.getTargetType();
if (targetType == null) {
targetType = (mbd.getFactoryMethodName() != null ? getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
@@ -632,12 +632,6 @@ protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
return null;
}
- List argumentValues = mbd.getConstructorArgumentValues().getGenericArgumentValues();
- Object[] args = new Object[argumentValues.size()];
- for (int i = 0; i < args.length; i++) {
- args[i] = argumentValues.get(i).getValue();
- }
-
// If all factory methods have the same return type, return that type.
// Can't clearly figure out exact method due to type converting / autowiring!
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
@@ -647,9 +641,45 @@ protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
factoryMethod.getName().equals(mbd.getFactoryMethodName()) &&
factoryMethod.getParameterTypes().length >= minNrOfArgs) {
- Class> returnType = GenericTypeResolver.resolveReturnTypeForGenericMethod(factoryMethod, args);
- if (returnType != null) {
- returnTypes.add(returnType);
+ // No declared type variables to inspect, so just process the standard return type.
+ if (factoryMethod.getTypeParameters().length > 0) {
+ try {
+ // Fully resolve parameter names and argument values.
+ Class>[] paramTypes = factoryMethod.getParameterTypes();
+ String[] paramNames = null;
+ ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
+ if (pnd != null) {
+ paramNames = pnd.getParameterNames(factoryMethod);
+ }
+ ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
+ Set usedValueHolders =
+ new HashSet(paramTypes.length);
+ Object[] args = new Object[paramTypes.length];
+ for (int i = 0; i < args.length; i++) {
+ ConstructorArgumentValues.ValueHolder valueHolder = cav.getArgumentValue(
+ i, paramTypes[i], (paramNames != null ? paramNames[i] : null), usedValueHolders);
+ if (valueHolder == null) {
+ valueHolder = cav.getGenericArgumentValue(null, null, usedValueHolders);
+ }
+ if (valueHolder != null) {
+ args[i] = valueHolder.getValue();
+ usedValueHolders.add(valueHolder);
+ }
+ }
+ Class> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
+ factoryMethod, args, getBeanClassLoader());
+ if (returnType != null) {
+ returnTypes.add(returnType);
+ }
+ }
+ catch (Throwable ex) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to resolve generic return type for factory method: " + ex);
+ }
+ }
+ }
+ else {
+ returnTypes.add(factoryMethod.getReturnType());
}
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
index 34ce6b59b121..aae92fd8fee5 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -275,77 +275,83 @@ protected T doGetBean(
markBeanAsCreated(beanName);
}
- final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- checkMergedBeanDefinition(mbd, beanName, args);
-
- // Guarantee initialization of beans that the current bean depends on.
- String[] dependsOn = mbd.getDependsOn();
- if (dependsOn != null) {
- for (String dependsOnBean : dependsOn) {
- getBean(dependsOnBean);
- registerDependentBean(dependsOnBean, beanName);
- }
- }
-
- // Create bean instance.
- if (mbd.isSingleton()) {
- sharedInstance = getSingleton(beanName, new ObjectFactory() {
- public Object getObject() throws BeansException {
- try {
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
+ try {
+ final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ checkMergedBeanDefinition(mbd, beanName, args);
+
+ // Guarantee initialization of beans that the current bean depends on.
+ String[] dependsOn = mbd.getDependsOn();
+ if (dependsOn != null) {
+ for (String dependsOnBean : dependsOn) {
+ getBean(dependsOnBean);
+ registerDependentBean(dependsOnBean, beanName);
}
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
-
- else if (mbd.isPrototype()) {
- // It's a prototype -> create a new instance.
- Object prototypeInstance = null;
- try {
- beforePrototypeCreation(beanName);
- prototypeInstance = createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
}
- bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
- }
- else {
- String scopeName = mbd.getScope();
- final Scope scope = this.scopes.get(scopeName);
- if (scope == null) {
- throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
- }
- try {
- Object scopedInstance = scope.get(beanName, new ObjectFactory() {
+ // Create bean instance.
+ if (mbd.isSingleton()) {
+ sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
- beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
- finally {
- afterPrototypeCreation(beanName);
+ catch (BeansException ex) {
+ // Explicitly remove instance from singleton cache: It might have been put there
+ // eagerly by the creation process, to allow for circular reference resolution.
+ // Also remove any beans that received a temporary reference to the bean.
+ destroySingleton(beanName);
+ throw ex;
}
}
});
- bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
+ bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
+ }
+
+ else if (mbd.isPrototype()) {
+ // It's a prototype -> create a new instance.
+ Object prototypeInstance = null;
+ try {
+ beforePrototypeCreation(beanName);
+ prototypeInstance = createBean(beanName, mbd, args);
+ }
+ finally {
+ afterPrototypeCreation(beanName);
+ }
+ bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
- catch (IllegalStateException ex) {
- throw new BeanCreationException(beanName,
- "Scope '" + scopeName + "' is not active for the current thread; " +
- "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
- ex);
+
+ else {
+ String scopeName = mbd.getScope();
+ final Scope scope = this.scopes.get(scopeName);
+ if (scope == null) {
+ throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
+ }
+ try {
+ Object scopedInstance = scope.get(beanName, new ObjectFactory() {
+ public Object getObject() throws BeansException {
+ beforePrototypeCreation(beanName);
+ try {
+ return createBean(beanName, mbd, args);
+ }
+ finally {
+ afterPrototypeCreation(beanName);
+ }
+ }
+ });
+ bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
+ }
+ catch (IllegalStateException ex) {
+ throw new BeanCreationException(beanName,
+ "Scope '" + scopeName + "' is not active for the current thread; " +
+ "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
+ ex);
+ }
}
}
+ catch (BeansException ex) {
+ cleanupAfterBeanCreationFailure(beanName);
+ throw ex;
+ }
}
// Check if required type matches the type of the actual bean instance.
@@ -1388,6 +1394,14 @@ protected void markBeanAsCreated(String beanName) {
this.alreadyCreated.put(beanName, Boolean.TRUE);
}
+ /**
+ * Perform appropriate cleanup of cached metadata after bean creation failed.
+ * @param beanName the name of the bean
+ */
+ protected void cleanupAfterBeanCreationFailure(String beanName) {
+ this.alreadyCreated.remove(beanName);
+ }
+
/**
* Determine whether the specified bean is eligible for having
* its bean definition metadata cached.
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
index f8ffd5798b52..81358b094309 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -23,20 +23,27 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
+import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
- * Utility class that contains various methods useful for
- * the implementation of autowire-capable bean factories.
+ * Utility class that contains various methods useful for the implementation of
+ * autowire-capable bean factories.
*
* @author Juergen Hoeller
* @author Mark Fisher
+ * @author Sam Brannen
* @since 1.1.2
* @see AbstractAutowireCapableBeanFactory
*/
@@ -117,8 +124,8 @@ public static boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set interfaces) {
Method setter = pd.getWriteMethod();
if (setter != null) {
- Class targetClass = setter.getDeclaringClass();
- for (Class ifc : interfaces) {
+ Class> targetClass = setter.getDeclaringClass();
+ for (Class> ifc : interfaces) {
if (ifc.isAssignableFrom(targetClass) &&
ClassUtils.hasMethod(ifc, setter.getName(), setter.getParameterTypes())) {
return true;
@@ -135,7 +142,7 @@ public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set requiredType) {
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory factory = (ObjectFactory) autowiringValue;
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
@@ -149,6 +156,124 @@ public static Object resolveAutowiringValue(Object autowiringValue, Class requir
return autowiringValue;
}
+ /**
+ * Determine the target type for the generic return type of the given
+ * generic factory method , where formal type variables are declared
+ * on the given method itself.
+ * For example, given a factory method with the following signature,
+ * if {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected
+ * method for {@code creatProxy()} and an {@code Object[]} array containing
+ * {@code MyService.class}, {@code resolveReturnTypeForFactoryMethod()} will
+ * infer that the target return type is {@code MyService}.
+ *
{@code public static T createProxy(Class clazz)}
+ * Possible Return Values
+ *
+ * the target return type, if it can be inferred
+ * the {@linkplain Method#getReturnType() standard return type}, if
+ * the given {@code method} does not declare any {@linkplain
+ * Method#getTypeParameters() formal type variables}
+ * the {@linkplain Method#getReturnType() standard return type}, if the
+ * target return type cannot be inferred (e.g., due to type erasure)
+ * {@code null}, if the length of the given arguments array is shorter
+ * than the length of the {@linkplain
+ * Method#getGenericParameterTypes() formal argument list} for the given
+ * method
+ *
+ * @param method the method to introspect (never {@code null})
+ * @param args the arguments that will be supplied to the method when it is
+ * invoked (never {@code null})
+ * @param classLoader the ClassLoader to resolve class names against, if necessary
+ * (never {@code null})
+ * @return the resolved target return type, the standard return type, or {@code null}
+ * @since 3.2.5
+ */
+ public static Class> resolveReturnTypeForFactoryMethod(Method method, Object[] args, ClassLoader classLoader) {
+ Assert.notNull(method, "Method must not be null");
+ Assert.notNull(args, "Argument array must not be null");
+ Assert.notNull(classLoader, "ClassLoader must not be null");
+
+ TypeVariable[] declaredTypeVariables = method.getTypeParameters();
+ Type genericReturnType = method.getGenericReturnType();
+ Type[] methodParameterTypes = method.getGenericParameterTypes();
+ Assert.isTrue(args.length == methodParameterTypes.length, "Argument array does not match parameter count");
+
+ // Ensure that the type variable (e.g., T) is declared directly on the method
+ // itself (e.g., via ), not on the enclosing class or interface.
+ boolean locallyDeclaredTypeVariableMatchesReturnType = false;
+ for (TypeVariable currentTypeVariable : declaredTypeVariables) {
+ if (currentTypeVariable.equals(genericReturnType)) {
+ locallyDeclaredTypeVariableMatchesReturnType = true;
+ break;
+ }
+ }
+
+ if (locallyDeclaredTypeVariableMatchesReturnType) {
+ for (int i = 0; i < methodParameterTypes.length; i++) {
+ Type methodParameterType = methodParameterTypes[i];
+ Object arg = args[i];
+ if (methodParameterType.equals(genericReturnType)) {
+ if (arg instanceof TypedStringValue) {
+ TypedStringValue typedValue = ((TypedStringValue) arg);
+ if (typedValue.hasTargetType()) {
+ return typedValue.getTargetType();
+ }
+ try {
+ return typedValue.resolveTargetType(classLoader);
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Failed to resolve value type [" +
+ typedValue.getTargetTypeName() + "] for factory method argument", ex);
+ }
+ }
+ // Only consider argument type if it is a simple value...
+ if (arg != null && !(arg instanceof BeanMetadataElement)) {
+ return arg.getClass();
+ }
+ return method.getReturnType();
+ }
+ else if (methodParameterType instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) methodParameterType;
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+ for (Type typeArg : actualTypeArguments) {
+ if (typeArg.equals(genericReturnType)) {
+ if (arg instanceof Class) {
+ return (Class>) arg;
+ }
+ else {
+ String className = null;
+ if (arg instanceof String) {
+ className = (String) arg;
+ }
+ else if (arg instanceof TypedStringValue) {
+ TypedStringValue typedValue = ((TypedStringValue) arg);
+ String targetTypeName = typedValue.getTargetTypeName();
+ if (targetTypeName == null || Class.class.getName().equals(targetTypeName)) {
+ className = typedValue.getValue();
+ }
+ }
+ if (className != null) {
+ try {
+ return ClassUtils.forName(className, classLoader);
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Could not resolve class name [" + arg +
+ "] for factory method argument", ex);
+ }
+ }
+ // Consider adding logic to determine the class of the typeArg, if possible.
+ // For now, just fall back...
+ return method.getReturnType();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Fall back...
+ return method.getReturnType();
+ }
+
/**
* Reflective InvocationHandler for lazy access to the current target object.
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReader.java
index f5d1e7778123..bd8b8f51b506 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReader.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -22,7 +22,7 @@
/**
* Simple interface for bean definition readers.
- * Specifies load methods with Resource parameters.
+ * Specifies load methods with Resource and String location parameters.
*
* Concrete bean definition readers can of course add additional
* load and register methods for bean definitions, specific to
@@ -45,23 +45,23 @@ public interface BeanDefinitionReader {
*/
BeanDefinitionRegistry getRegistry();
- /**
- * Return the resource loader to use for resource locations.
- * Can be checked for the ResourcePatternResolver interface and cast
- * accordingly, for loading multiple resources for a given resource pattern.
- *
Null suggests that absolute resource loading is not available
- * for this bean definition reader.
- *
This is mainly meant to be used for importing further resources
- * from within a bean definition resource, for example via the "import"
- * tag in XML bean definitions. It is recommended, however, to apply
- * such imports relative to the defining resource; only explicit full
- * resource locations will trigger absolute resource loading.
- *
There is also a {@code loadBeanDefinitions(String)} method available,
- * for loading bean definitions from a resource location (or location pattern).
- * This is a convenience to avoid explicit ResourceLoader handling.
- * @see #loadBeanDefinitions(String)
- * @see org.springframework.core.io.support.ResourcePatternResolver
- */
+ /**
+ * Return the resource loader to use for resource locations.
+ * Can be checked for the ResourcePatternResolver interface and cast
+ * accordingly, for loading multiple resources for a given resource pattern.
+ *
Null suggests that absolute resource loading is not available
+ * for this bean definition reader.
+ *
This is mainly meant to be used for importing further resources
+ * from within a bean definition resource, for example via the "import"
+ * tag in XML bean definitions. It is recommended, however, to apply
+ * such imports relative to the defining resource; only explicit full
+ * resource locations will trigger absolute resource loading.
+ *
There is also a {@code loadBeanDefinitions(String)} method available,
+ * for loading bean definitions from a resource location (or location pattern).
+ * This is a convenience to avoid explicit ResourceLoader handling.
+ * @see #loadBeanDefinitions(String)
+ * @see org.springframework.core.io.support.ResourcePatternResolver
+ */
ResourceLoader getResourceLoader();
/**
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java
index 4ee6d218fa61..96400677848f 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -128,7 +128,7 @@ else if (value instanceof BeanDefinition) {
else if (value instanceof ManagedArray) {
// May need to resolve contained runtime references.
ManagedArray array = (ManagedArray) value;
- Class elementType = array.resolvedElementType;
+ Class> elementType = array.resolvedElementType;
if (elementType == null) {
String elementTypeName = array.getElementTypeName();
if (StringUtils.hasText(elementTypeName)) {
@@ -271,7 +271,7 @@ private Object resolveInnerBean(Object argName, String innerBeanName, BeanDefini
Object innerBean = this.beanFactory.createBean(actualInnerBeanName, mbd, null);
this.beanFactory.registerContainedBean(actualInnerBeanName, this.beanName);
if (innerBean instanceof FactoryBean) {
- boolean synthetic = (mbd != null && mbd.isSynthetic());
+ boolean synthetic = mbd.isSynthetic();
return this.beanFactory.getObjectFromFactoryBean((FactoryBean) innerBean, actualInnerBeanName, !synthetic);
}
else {
@@ -335,7 +335,7 @@ private Object resolveReference(Object argName, RuntimeBeanReference ref) {
/**
* For each element in the managed array, resolve reference if necessary.
*/
- private Object resolveManagedArray(Object argName, List> ml, Class elementType) {
+ private Object resolveManagedArray(Object argName, List> ml, Class> elementType) {
Object resolved = Array.newInstance(elementType, ml.size());
for (int i = 0; i < ml.size(); i++) {
Array.set(resolved, i,
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
index 384866ffdd78..f290d51b1869 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -151,7 +151,7 @@ public BeanWrapper autowireConstructor(
// Take specified constructors, if any.
Constructor[] candidates = chosenCtors;
if (candidates == null) {
- Class beanClass = mbd.getBeanClass();
+ Class> beanClass = mbd.getBeanClass();
try {
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
@@ -169,7 +169,7 @@ public BeanWrapper autowireConstructor(
for (int i = 0; i < candidates.length; i++) {
Constructor> candidate = candidates[i];
- Class[] paramTypes = candidate.getParameterTypes();
+ Class>[] paramTypes = candidate.getParameterTypes();
if (constructorToUse != null && argsToUse.length > paramTypes.length) {
// Already found greedy constructor that can be satisfied ->
@@ -341,7 +341,7 @@ public BeanWrapper instantiateUsingFactoryMethod(final String beanName, final Ro
this.beanFactory.initBeanWrapper(bw);
Object factoryBean;
- Class factoryClass;
+ Class> factoryClass;
boolean isStatic;
String factoryBeanName = mbd.getFactoryBeanName();
@@ -399,7 +399,7 @@ public BeanWrapper instantiateUsingFactoryMethod(final String beanName, final Ro
factoryClass = ClassUtils.getUserClass(factoryClass);
Method[] rawCandidates;
- final Class factoryClazz = factoryClass;
+ final Class> factoryClazz = factoryClass;
if (System.getSecurityManager() != null) {
rawCandidates = AccessController.doPrivileged(new PrivilegedAction() {
public Method[] run() {
@@ -445,7 +445,7 @@ public Method[] run() {
for (int i = 0; i < candidates.length; i++) {
Method candidate = candidates[i];
- Class[] paramTypes = candidate.getParameterTypes();
+ Class>[] paramTypes = candidate.getParameterTypes();
if (paramTypes.length >= minNrOfArgs) {
ArgumentsHolder argsHolder;
@@ -503,7 +503,15 @@ public Method[] run() {
minTypeDiffWeight = typeDiffWeight;
ambiguousFactoryMethods = null;
}
- else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight) {
+ // Find out about ambiguity: In case of the same type difference weight
+ // for methods with the same number of parameters, collect such candidates
+ // and eventually raise an ambiguity exception.
+ // However, only perform that check in non-lenient constructor resolution mode,
+ // and explicitly ignore overridden methods (with the same parameter signature).
+ else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
+ !mbd.isLenientConstructorResolution() &&
+ paramTypes.length == factoryMethodToUse.getParameterTypes().length &&
+ !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
if (ambiguousFactoryMethods == null) {
ambiguousFactoryMethods = new LinkedHashSet();
ambiguousFactoryMethods.add(factoryMethodToUse);
@@ -540,7 +548,7 @@ else if (void.class.equals(factoryMethodToUse.getReturnType())) {
"Invalid factory method '" + mbd.getFactoryMethodName() +
"': needs to have a non-void return type!");
}
- else if (ambiguousFactoryMethods != null && !mbd.isLenientConstructorResolution()) {
+ else if (ambiguousFactoryMethods != null) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Ambiguous factory method matches found in bean '" + beanName + "' " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
@@ -644,7 +652,7 @@ private int resolveConstructorArguments(
*/
private ArgumentsHolder createArgumentArray(
String beanName, RootBeanDefinition mbd, ConstructorArgumentValues resolvedValues,
- BeanWrapper bw, Class[] paramTypes, String[] paramNames, Object methodOrCtor,
+ BeanWrapper bw, Class>[] paramTypes, String[] paramNames, Object methodOrCtor,
boolean autowiring) throws UnsatisfiedDependencyException {
String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method");
@@ -750,7 +758,7 @@ private ArgumentsHolder createArgumentArray(
private Object[] resolvePreparedArguments(
String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor, Object[] argsToResolve) {
- Class[] paramTypes = (methodOrCtor instanceof Method ?
+ Class>[] paramTypes = (methodOrCtor instanceof Method ?
((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes());
TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ?
this.beanFactory.getCustomTypeConverter() : bw);
@@ -822,7 +830,7 @@ public ArgumentsHolder(Object[] args) {
this.preparedArguments = args;
}
- public int getTypeDifferenceWeight(Class[] paramTypes) {
+ public int getTypeDifferenceWeight(Class>[] paramTypes) {
// If valid arguments found, determine type difference weight.
// Try type difference weight on both the converted arguments and
// the raw arguments. If the raw weight is better, use it.
@@ -832,7 +840,7 @@ public int getTypeDifferenceWeight(Class[] paramTypes) {
return (rawTypeDiffWeight < typeDiffWeight ? rawTypeDiffWeight : typeDiffWeight);
}
- public int getAssignabilityWeight(Class[] paramTypes) {
+ public int getAssignabilityWeight(Class>[] paramTypes) {
for (int i = 0; i < paramTypes.length; i++) {
if (!ClassUtils.isAssignableValue(paramTypes[i], this.arguments[i])) {
return Integer.MAX_VALUE;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java
index a422d5b4fcd4..0be926af27ed 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java
@@ -133,7 +133,7 @@ protected void doRegisterBeanDefinitions(Element root) {
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
- this.delegate = createHelper(this.readerContext, root, parent);
+ this.delegate = createDelegate(this.readerContext, root, parent);
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
@@ -142,14 +142,24 @@ protected void doRegisterBeanDefinitions(Element root) {
this.delegate = parent;
}
- protected BeanDefinitionParserDelegate createHelper(
+ protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
- BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext, environment);
- delegate.initDefaults(root, parentDelegate);
+ BeanDefinitionParserDelegate delegate = createHelper(readerContext, root, parentDelegate);
+ if (delegate == null) {
+ delegate = new BeanDefinitionParserDelegate(readerContext, this.environment);
+ delegate.initDefaults(root, parentDelegate);
+ }
return delegate;
}
+ @Deprecated
+ protected BeanDefinitionParserDelegate createHelper(
+ XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
+
+ return null;
+ }
+
/**
* Return the descriptor for the XML resource that this parser works on.
*/
diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java
index 9ec9af033b8d..17dcfd653455 100644
--- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java
@@ -63,6 +63,10 @@
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
+import static org.hamcrest.Matchers.*;
+
+import static org.junit.Assert.*;
+
/**
* @author Rod Johnson
@@ -1559,6 +1563,20 @@ public void cornerSpr10115() {
assertEquals("val1", Spr10115Bean.prop1);
}
+ @Test
+ public void testArrayToObject() throws Exception {
+ ArrayToObject foo = new ArrayToObject();
+ BeanWrapperImpl bwi = new BeanWrapperImpl();
+ bwi.setWrappedInstance(foo);
+
+ Object[] array = new Object[] {"1","2"};
+ bwi.setPropertyValue("object", array );
+ assertThat(foo.getObject(), equalTo((Object) array));
+
+ array = new Object[] {"1"};
+ bwi.setPropertyValue("object", array );
+ assertThat(foo.getObject(), equalTo((Object) array));
+}
static class Spr10115Bean {
private static String prop1;
@@ -1944,4 +1962,19 @@ public enum TestEnum {
TEST_VALUE
}
+
+ static class ArrayToObject {
+
+ private Object object;
+
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ }
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/SimplePropertyDescriptorTests.java b/spring-beans/src/test/java/org/springframework/beans/SimplePropertyDescriptorTests.java
index 8627083279ef..b5a9f14e4f2e 100644
--- a/spring-beans/src/test/java/org/springframework/beans/SimplePropertyDescriptorTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/SimplePropertyDescriptorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -28,7 +28,7 @@
import static org.junit.Assert.*;
/**
- * Unit tests for {@link SimpleNonIndexedPropertyDescriptor} and
+ * Unit tests for {@link SimplePropertyDescriptor} and
* {@link SimpleIndexedPropertyDescriptor}.
*
* @author Chris Beams
@@ -39,7 +39,7 @@ public class SimplePropertyDescriptorTests {
@Test
public void toStringOutput() throws IntrospectionException, SecurityException, NoSuchMethodException {
{
- Object pd = new SimpleNonIndexedPropertyDescriptor("foo", null, null);
+ Object pd = new SimplePropertyDescriptor("foo", null, null);
assertThat(pd.toString(), containsString(
"PropertyDescriptor[name=foo, propertyType=null, readMethod=null"));
}
@@ -49,7 +49,7 @@ class C {
public Object setFoo(String foo) { return null; }
}
Method m = C.class.getMethod("setFoo", String.class);
- Object pd = new SimpleNonIndexedPropertyDescriptor("foo", null, m);
+ Object pd = new SimplePropertyDescriptor("foo", null, m);
assertThat(pd.toString(), allOf(
containsString("PropertyDescriptor[name=foo"),
containsString("propertyType=class java.lang.String"),
@@ -76,10 +76,10 @@ class C {
@Test
public void nonIndexedEquality() throws IntrospectionException, SecurityException, NoSuchMethodException {
- Object pd1 = new SimpleNonIndexedPropertyDescriptor("foo", null, null);
+ Object pd1 = new SimplePropertyDescriptor("foo", null, null);
assertThat(pd1, equalTo(pd1));
- Object pd2 = new SimpleNonIndexedPropertyDescriptor("foo", null, null);
+ Object pd2 = new SimplePropertyDescriptor("foo", null, null);
assertThat(pd1, equalTo(pd2));
assertThat(pd2, equalTo(pd1));
@@ -89,12 +89,12 @@ class C {
public String getFoo() { return null; }
}
Method wm1 = C.class.getMethod("setFoo", String.class);
- Object pd3 = new SimpleNonIndexedPropertyDescriptor("foo", null, wm1);
+ Object pd3 = new SimplePropertyDescriptor("foo", null, wm1);
assertThat(pd1, not(equalTo(pd3)));
assertThat(pd3, not(equalTo(pd1)));
Method rm1 = C.class.getMethod("getFoo");
- Object pd4 = new SimpleNonIndexedPropertyDescriptor("foo", rm1, null);
+ Object pd4 = new SimplePropertyDescriptor("foo", rm1, null);
assertThat(pd1, not(equalTo(pd4)));
assertThat(pd4, not(equalTo(pd1)));
@@ -147,4 +147,5 @@ class C {
assertThat(pd1, not(equalTo(pd7)));
assertThat(pd7, not(equalTo(pd1)));
}
+
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/AutowireUtilsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/AutowireUtilsTests.java
new file mode 100644
index 000000000000..56fcde6ec9cf
--- /dev/null
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/AutowireUtilsTests.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2002-2013 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.beans.factory.support;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import org.springframework.util.ReflectionUtils;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ */
+public class AutowireUtilsTests {
+
+ @Test
+ public void genericMethodReturnTypes() {
+ Method notParameterized = ReflectionUtils.findMethod(MyTypeWithMethods.class, "notParameterized", new Class[]{});
+ assertEquals(String.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(notParameterized, new Object[]{}, getClass().getClassLoader()));
+
+ Method notParameterizedWithArguments = ReflectionUtils.findMethod(MyTypeWithMethods.class, "notParameterizedWithArguments",
+ new Class[] { Integer.class, Boolean.class });
+ assertEquals(String.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(notParameterizedWithArguments, new Object[] { 99, true }, getClass().getClassLoader()));
+
+ Method createProxy = ReflectionUtils.findMethod(MyTypeWithMethods.class, "createProxy", new Class[] { Object.class });
+ assertEquals(String.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(createProxy, new Object[] { "foo" }, getClass().getClassLoader()));
+
+ Method createNamedProxyWithDifferentTypes = ReflectionUtils.findMethod(MyTypeWithMethods.class, "createNamedProxy",
+ new Class[] { String.class, Object.class });
+ assertEquals(Long.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(createNamedProxyWithDifferentTypes, new Object[] { "enigma", 99L }, getClass().getClassLoader()));
+
+ Method createNamedProxyWithDuplicateTypes = ReflectionUtils.findMethod(MyTypeWithMethods.class, "createNamedProxy",
+ new Class[] { String.class, Object.class });
+ assertEquals(String.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(createNamedProxyWithDuplicateTypes, new Object[] { "enigma", "foo" }, getClass().getClassLoader()));
+
+ Method createMock = ReflectionUtils.findMethod(MyTypeWithMethods.class, "createMock", new Class[] { Class.class });
+ assertEquals(Runnable.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(createMock, new Object[] { Runnable.class }, getClass().getClassLoader()));
+ assertEquals(Runnable.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(createMock, new Object[] { Runnable.class.getName() }, getClass().getClassLoader()));
+
+ Method createNamedMock = ReflectionUtils.findMethod(MyTypeWithMethods.class, "createNamedMock", new Class[] { String.class,
+ Class.class });
+ assertEquals(Runnable.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(createNamedMock, new Object[] { "foo", Runnable.class }, getClass().getClassLoader()));
+
+ Method createVMock = ReflectionUtils.findMethod(MyTypeWithMethods.class, "createVMock",
+ new Class[] { Object.class, Class.class });
+ assertEquals(Runnable.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(createVMock, new Object[] { "foo", Runnable.class }, getClass().getClassLoader()));
+
+ // Ideally we would expect String.class instead of Object.class, but
+ // resolveReturnTypeForFactoryMethod() does not currently support this form of
+ // look-up.
+ Method extractValueFrom = ReflectionUtils.findMethod(MyTypeWithMethods.class, "extractValueFrom",
+ new Class[] { MyInterfaceType.class });
+ assertEquals(Object.class,
+ AutowireUtils.resolveReturnTypeForFactoryMethod(extractValueFrom, new Object[] { new MySimpleInterfaceType() }, getClass().getClassLoader()));
+
+ // Ideally we would expect Boolean.class instead of Object.class, but this
+ // information is not available at run-time due to type erasure.
+ Map map = new HashMap();
+ map.put(0, false);
+ map.put(1, true);
+ Method extractMagicValue = ReflectionUtils.findMethod(MyTypeWithMethods.class, "extractMagicValue", new Class[] { Map.class });
+ assertEquals(Object.class, AutowireUtils.resolveReturnTypeForFactoryMethod(extractMagicValue, new Object[] { map }, getClass().getClassLoader()));
+ }
+
+
+ public interface MyInterfaceType {
+ }
+
+ public class MySimpleInterfaceType implements MyInterfaceType {
+ }
+
+ public static class MyTypeWithMethods {
+
+ public MyInterfaceType integer() {
+ return null;
+ }
+
+ public MySimpleInterfaceType string() {
+ return null;
+ }
+
+ public Object object() {
+ return null;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public MyInterfaceType raw() {
+ return null;
+ }
+
+ public String notParameterized() {
+ return null;
+ }
+
+ public String notParameterizedWithArguments(Integer x, Boolean b) {
+ return null;
+ }
+
+ /**
+ * Simulates a factory method that wraps the supplied object in a proxy of the
+ * same type.
+ */
+ public static T createProxy(T object) {
+ return null;
+ }
+
+ /**
+ * Similar to {@link #createProxy(Object)} but adds an additional argument before
+ * the argument of type {@code T}. Note that they may potentially be of the same
+ * time when invoked!
+ */
+ public static T createNamedProxy(String name, T object) {
+ return null;
+ }
+
+ /**
+ * Simulates factory methods found in libraries such as Mockito and EasyMock.
+ */
+ public static MOCK createMock(Class toMock) {
+ return null;
+ }
+
+ /**
+ * Similar to {@link #createMock(Class)} but adds an additional method argument
+ * before the parameterized argument.
+ */
+ public static T createNamedMock(String name, Class toMock) {
+ return null;
+ }
+
+ /**
+ * Similar to {@link #createNamedMock(String, Class)} but adds an additional
+ * parameterized type.
+ */
+ public static T createVMock(V name, Class toMock) {
+ return null;
+ }
+
+ /**
+ * Extract some value of the type supported by the interface (i.e., by a concrete,
+ * non-generic implementation of the interface).
+ */
+ public static T extractValueFrom(MyInterfaceType myInterfaceType) {
+ return null;
+ }
+
+ /**
+ * Extract some magic value from the supplied map.
+ */
+ public static V extractMagicValue(Map map) {
+ return null;
+ }
+
+ public void readIntegerInputMessage(MyInterfaceType message) {
+ }
+
+ public void readIntegerArrayInputMessage(MyInterfaceType[] message) {
+ }
+
+ public void readGenericArrayInputMessage(T[] message) {
+ }
+ }
+
+}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java
index 2480f64fac22..f2b33f22dc87 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java
@@ -16,13 +16,9 @@
package org.springframework.beans.factory.support;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
@@ -37,21 +33,23 @@
import org.junit.Test;
import org.mockito.Mockito;
+
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.UrlResource;
import org.springframework.tests.Assume;
import org.springframework.tests.TestGroup;
-
import org.springframework.tests.sample.beans.GenericBean;
import org.springframework.tests.sample.beans.GenericIntegerBean;
import org.springframework.tests.sample.beans.GenericSetOfIntegerBean;
import org.springframework.tests.sample.beans.TestBean;
+import static org.junit.Assert.*;
/**
* @author Juergen Hoeller
@@ -115,7 +113,7 @@ public void testGenericListPropertyWithInvalidElementType() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
RootBeanDefinition rbd = new RootBeanDefinition(GenericIntegerBean.class);
- List input = new ArrayList();
+ List input = new ArrayList();
input.add(1);
rbd.getPropertyValues().add("testBeanList", input);
@@ -655,18 +653,17 @@ public void testSetBean() throws Exception {
}
/**
- * Tests support for parameterized {@code factory-method} declarations such
- * as Mockito {@code mock()} method which has the following signature.
- *
- * {@code
+ * Tests support for parameterized static {@code factory-method} declarations such as
+ * Mockito's {@code mock()} method which has the following signature.
+ *
+ * {@code
* public static T mock(Class classToMock)
- * }
- *
- * See SPR-9493
- * @since 3.2
+ * }
+ *
+ * See SPR-9493
*/
@Test
- public void parameterizedFactoryMethod() {
+ public void parameterizedStaticFactoryMethod() {
RootBeanDefinition rbd = new RootBeanDefinition(Mockito.class);
rbd.setFactoryMethodName("mock");
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
@@ -678,6 +675,100 @@ public void parameterizedFactoryMethod() {
assertEquals(1, beans.size());
}
+ /**
+ * Tests support for parameterized instance {@code factory-method} declarations such
+ * as EasyMock's {@code IMocksControl.createMock()} method which has the following
+ * signature.
+ *
+ * {@code
+ * public T createMock(Class toMock)
+ * }
+ *
+ * See SPR-10411
+ */
+ @Test
+ public void parameterizedInstanceFactoryMethod() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+
+ RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
+ bf.registerBeanDefinition("mocksControl", rbd);
+
+ rbd = new RootBeanDefinition();
+ rbd.setFactoryBeanName("mocksControl");
+ rbd.setFactoryMethodName("createMock");
+ rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
+ bf.registerBeanDefinition("mock", rbd);
+
+ Map beans = bf.getBeansOfType(Runnable.class);
+ assertEquals(1, beans.size());
+ }
+
+ @Test
+ public void parameterizedInstanceFactoryMethodWithNonResolvedClassName() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+
+ RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
+ bf.registerBeanDefinition("mocksControl", rbd);
+
+ rbd = new RootBeanDefinition();
+ rbd.setFactoryBeanName("mocksControl");
+ rbd.setFactoryMethodName("createMock");
+ rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class.getName());
+ bf.registerBeanDefinition("mock", rbd);
+
+ Map beans = bf.getBeansOfType(Runnable.class);
+ assertEquals(1, beans.size());
+ }
+
+ @Test
+ public void parameterizedInstanceFactoryMethodWithWrappedClassName() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+
+ RootBeanDefinition rbd = new RootBeanDefinition();
+ rbd.setBeanClassName(Mockito.class.getName());
+ rbd.setFactoryMethodName("mock");
+ // TypedStringValue used to be equivalent to an XML-defined argument String
+ rbd.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue(Runnable.class.getName()));
+ bf.registerBeanDefinition("mock", rbd);
+
+ Map beans = bf.getBeansOfType(Runnable.class);
+ assertEquals(1, beans.size());
+ }
+
+ @Test
+ public void parameterizedInstanceFactoryMethodWithInvalidClassName() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+
+ RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
+ bf.registerBeanDefinition("mocksControl", rbd);
+
+ rbd = new RootBeanDefinition();
+ rbd.setFactoryBeanName("mocksControl");
+ rbd.setFactoryMethodName("createMock");
+ rbd.getConstructorArgumentValues().addGenericArgumentValue("x");
+ bf.registerBeanDefinition("mock", rbd);
+
+ Map beans = bf.getBeansOfType(Runnable.class);
+ assertEquals(0, beans.size());
+ }
+
+ @Test
+ public void parameterizedInstanceFactoryMethodWithIndexedArgument() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+
+ RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
+ bf.registerBeanDefinition("mocksControl", rbd);
+
+ rbd = new RootBeanDefinition();
+ rbd.setFactoryBeanName("mocksControl");
+ rbd.setFactoryMethodName("createMock");
+ rbd.getConstructorArgumentValues().addIndexedArgumentValue(0, Runnable.class);
+ bf.registerBeanDefinition("mock", rbd);
+
+ Map beans = bf.getBeansOfType(Runnable.class);
+ assertEquals(1, beans.size());
+ }
+
@SuppressWarnings("serial")
public static class NamedUrlList extends LinkedList {
@@ -722,4 +813,22 @@ public void setUrlNames(Set urlNames) throws MalformedURLException {
}
}
+
+ /**
+ * Pseudo-implementation of EasyMock's {@code MocksControl} class.
+ */
+ public static class MocksControl {
+
+ @SuppressWarnings("unchecked")
+ public T createMock(Class toMock) {
+ return (T) Proxy.newProxyInstance(BeanFactoryGenericsTests.class.getClassLoader(), new Class>[] {toMock},
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ throw new UnsupportedOperationException("mocked!");
+ }
+ });
+ }
+ }
+
}
diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DerivedTestBean.java b/spring-beans/src/test/java/org/springframework/tests/sample/beans/DerivedTestBean.java
index 91416208a2b1..d12db5b7adf1 100644
--- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DerivedTestBean.java
+++ b/spring-beans/src/test/java/org/springframework/tests/sample/beans/DerivedTestBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -71,6 +71,11 @@ public void setSpouseRef(String name) {
setSpouse(new TestBean(name));
}
+ @Override
+ public TestBean getSpouse() {
+ return (TestBean) super.getSpouse();
+ }
+
public void initialize() {
this.initialized = true;
diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml
index 974ae6c3d9ca..73294dfbaede 100644
--- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml
+++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml
@@ -35,11 +35,10 @@
-
+
Jenny
30
-
diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java
index dcec4047eeec..02f7f38396a2 100644
--- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java
+++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java
@@ -319,7 +319,8 @@ public void afterPropertiesSet() throws CacheException, IOException {
// Fetch cache region: If none with the given name exists,
// create one on the fly.
Ehcache rawCache;
- if (this.cacheManager.cacheExists(this.cacheName)) {
+ boolean cacheExists = this.cacheManager.cacheExists(cacheName);
+ if (cacheExists) {
if (logger.isDebugEnabled()) {
logger.debug("Using existing EhCache cache region '" + this.cacheName + "'");
}
@@ -330,7 +331,6 @@ public void afterPropertiesSet() throws CacheException, IOException {
logger.debug("Creating new EhCache cache region '" + this.cacheName + "'");
}
rawCache = createCache();
- this.cacheManager.addCache(rawCache);
}
if (this.cacheEventListeners != null) {
@@ -348,7 +348,9 @@ public void afterPropertiesSet() throws CacheException, IOException {
rawCache.setDisabled(true);
}
- // Decorate cache if necessary.
+ if (!cacheExists) {
+ this.cacheManager.addCache(rawCache);
+ }
Ehcache decoratedCache = decorateCache(rawCache);
if (decoratedCache != rawCache) {
this.cacheManager.replaceCacheWithDecoratedCache(rawCache, decoratedCache);
diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java
index 4d1518dfa9a9..499575210955 100644
--- a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java
+++ b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -17,10 +17,9 @@
package org.springframework.scheduling.commonj;
import java.util.Collection;
+import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
-import java.util.concurrent.Callable;
-
import javax.naming.NamingException;
import commonj.work.Work;
@@ -54,10 +53,8 @@
* server's JNDI environment, as defined in the server's management console.
*
* Note: At the time of this writing, the CommonJ WorkManager facility
- * is only supported on IBM WebSphere 6.0+ and BEA WebLogic 9.0+,
+ * is only supported on IBM WebSphere 6.1+ and BEA WebLogic 9.0+,
* despite being such a crucial API for an application server.
- * (There is a similar facility available on WebSphere 5.1 Enterprise,
- * though, which we will discuss below.)
*
*
On JBoss and GlassFish, a similar facility is available through
* the JCA WorkManager. See the
@@ -80,8 +77,7 @@ public class WorkManagerTaskExecutor extends JndiLocatorSupport
/**
* Specify the CommonJ WorkManager to delegate to.
- *
Alternatively, you can also specify the JNDI name
- * of the target WorkManager.
+ *
Alternatively, you can also specify the JNDI name of the target WorkManager.
* @see #setWorkManagerName
*/
public void setWorkManager(WorkManager workManager) {
@@ -90,9 +86,8 @@ public void setWorkManager(WorkManager workManager) {
/**
* Set the JNDI name of the CommonJ WorkManager.
- *
This can either be a fully qualified JNDI name,
- * or the JNDI name relative to the current environment
- * naming context if "resourceRef" is set to "true".
+ *
This can either be a fully qualified JNDI name, or the JNDI name relative
+ * to the current environment naming context if "resourceRef" is set to "true".
* @see #setWorkManager
* @see #setResourceRef
*/
@@ -170,27 +165,19 @@ public boolean prefersShortLivedTasks() {
// Implementation of the CommonJ WorkManager interface
//-------------------------------------------------------------------------
- public WorkItem schedule(Work work)
- throws WorkException, IllegalArgumentException {
-
+ public WorkItem schedule(Work work) throws WorkException, IllegalArgumentException {
return this.workManager.schedule(work);
}
- public WorkItem schedule(Work work, WorkListener workListener)
- throws WorkException, IllegalArgumentException {
-
+ public WorkItem schedule(Work work, WorkListener workListener) throws WorkException {
return this.workManager.schedule(work, workListener);
}
- public boolean waitForAll(Collection workItems, long timeout)
- throws InterruptedException, IllegalArgumentException {
-
+ public boolean waitForAll(Collection workItems, long timeout) throws InterruptedException {
return this.workManager.waitForAll(workItems, timeout);
}
- public Collection waitForAny(Collection workItems, long timeout)
- throws InterruptedException, IllegalArgumentException {
-
+ public Collection waitForAny(Collection workItems, long timeout) throws InterruptedException {
return this.workManager.waitForAny(workItems, timeout);
}
diff --git a/spring-context/src/main/java/org/springframework/cache/Cache.java b/spring-context/src/main/java/org/springframework/cache/Cache.java
index dbfa1cba84ba..8a9109afc92d 100644
--- a/spring-context/src/main/java/org/springframework/cache/Cache.java
+++ b/spring-context/src/main/java/org/springframework/cache/Cache.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -39,11 +39,15 @@ public interface Cache {
Object getNativeCache();
/**
- * Return the value to which this cache maps the specified key. Returns
- * {@code null} if the cache contains no mapping for this key.
- * @param key key whose associated value is to be returned.
+ * Return the value to which this cache maps the specified key.
+ *
Returns {@code null} if the cache contains no mapping for this key;
+ * otherwise, the cached value (which may be {@code null} itself) will
+ * be returned in a {@link ValueWrapper}.
+ * @param key the key whose associated value is to be returned
* @return the value to which this cache maps the specified key,
- * or {@code null} if the cache contains no mapping for this key
+ * contained within a {@link ValueWrapper} which may also hold
+ * a cached {@code null} value. A straight {@code null} being
+ * returned means that the cache contains no mapping for this key.
*/
ValueWrapper get(Object key);
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
index 4576ab3bba16..9110189f738a 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
@@ -109,7 +109,7 @@ public void setEnvironment(ConfigurableEnvironment environment) {
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.reader.setBeanNameGenerator(beanNameGenerator);
this.scanner.setBeanNameGenerator(beanNameGenerator);
- this.getBeanFactory().registerSingleton(
+ getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
}
@@ -126,9 +126,9 @@ public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver
/**
* Register one or more annotated classes to be processed.
- * Note that {@link #refresh()} must be called in order for the context to fully
- * process the new class.
- *
Calls to {@link #register} are idempotent; adding the same
+ * Note that {@link #refresh()} must be called in order for the context
+ * to fully process the new class.
+ *
Calls to {@code register} are idempotent; adding the same
* annotated class more than once has no additional effect.
* @param annotatedClasses one or more annotated classes,
* e.g. {@link Configuration @Configuration} classes
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java
index f283bb6d2c0f..39560ef53116 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -230,7 +230,7 @@ private static BeanDefinitionHolder registerPostProcessor(
return new BeanDefinitionHolder(definition, beanName);
}
- static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
+ public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
AnnotationMetadata metadata = abd.getMetadata();
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
@@ -260,5 +260,4 @@ static BeanDefinitionHolder applyScopedProxyMode(
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
-
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
index b3f9450f983d..944a5a5ed166 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
@@ -317,8 +317,8 @@ protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition)
/**
* Determine whether the given new bean definition is compatible with
* the given existing bean definition.
- *
The default implementation simply considers them as compatible
- * when the bean class name matches.
+ *
The default implementation considers them as compatible when the existing
+ * bean definition comes from the same source or from a non-scanning source.
* @param newDefinition the new bean definition, originated from scanning
* @param existingDefinition the existing bean definition, potentially an
* explicitly defined one or a previously generated one from scanning
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
index b775c6e64084..9280c65e1b6c 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
@@ -228,7 +228,7 @@ protected void registerDefaultFilters() {
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false));
- logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
+ logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
@@ -236,7 +236,7 @@ protected void registerDefaultFilters() {
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class extends Annotation>) cl.loadClass("javax.inject.Named")), false));
- logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
+ logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
index 93a71336a0fb..cbb1ee2110eb 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -177,8 +177,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
private transient BeanFactory beanFactory;
- private transient final Map, InjectionMetadata> injectionMetadataCache =
- new ConcurrentHashMap, InjectionMetadata>(64);
+ private transient final Map injectionMetadataCache =
+ new ConcurrentHashMap(64);
/**
@@ -282,7 +282,7 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
if (beanType != null) {
- InjectionMetadata metadata = findResourceMetadata(beanType);
+ InjectionMetadata metadata = findResourceMetadata(beanName, beanType);
metadata.checkConfigMembers(beanDefinition);
}
}
@@ -298,7 +298,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
- InjectionMetadata metadata = findResourceMetadata(bean.getClass());
+ InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass());
try {
metadata.inject(bean, beanName, pvs);
}
@@ -309,12 +309,14 @@ public PropertyValues postProcessPropertyValues(
}
- private InjectionMetadata findResourceMetadata(final Class> clazz) {
+ private InjectionMetadata findResourceMetadata(String beanName, final Class> clazz) {
// Quick check on the concurrent map first, with minimal locking.
- InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
+ // Fall back to class name as cache key, for backwards compatibility with custom callers.
+ String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
+ InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (metadata == null) {
synchronized (this.injectionMetadataCache) {
- metadata = this.injectionMetadataCache.get(clazz);
+ metadata = this.injectionMetadataCache.get(cacheKey);
if (metadata == null) {
LinkedList elements = new LinkedList();
Class> targetClass = clazz;
@@ -388,7 +390,7 @@ else if (method.isAnnotationPresent(Resource.class)) {
while (targetClass != null && targetClass != Object.class);
metadata = new InjectionMetadata(clazz, elements);
- this.injectionMetadataCache.put(clazz, metadata);
+ this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
@@ -472,11 +474,8 @@ protected abstract class LookupElement extends InjectionMetadata.InjectedElement
public LookupElement(Member member, PropertyDescriptor pd) {
super(member, pd);
- initAnnotation((AnnotatedElement) member);
}
- protected abstract void initAnnotation(AnnotatedElement ae);
-
/**
* Return the resource name for the lookup.
*/
@@ -511,14 +510,11 @@ public final DependencyDescriptor getDependencyDescriptor() {
*/
private class ResourceElement extends LookupElement {
- protected boolean shareable = true;
+ protected final boolean shareable;
public ResourceElement(Member member, PropertyDescriptor pd) {
super(member, pd);
- }
-
- @Override
- protected void initAnnotation(AnnotatedElement ae) {
+ AnnotatedElement ae = (AnnotatedElement) member;
Resource resource = ae.getAnnotation(Resource.class);
String resourceName = resource.name();
Class> resourceType = resource.type();
@@ -558,16 +554,13 @@ protected Object getResourceToInject(Object target, String requestingBeanName) {
*/
private class WebServiceRefElement extends LookupElement {
- private Class> elementType;
+ private final Class> elementType;
- private String wsdlLocation;
+ private final String wsdlLocation;
public WebServiceRefElement(Member member, PropertyDescriptor pd) {
super(member, pd);
- }
-
- @Override
- protected void initAnnotation(AnnotatedElement ae) {
+ AnnotatedElement ae = (AnnotatedElement) member;
WebServiceRef resource = ae.getAnnotation(WebServiceRef.class);
String resourceName = resource.name();
Class> resourceType = resource.type();
@@ -647,14 +640,11 @@ protected Object getResourceToInject(Object target, String requestingBeanName) {
*/
private class EjbRefElement extends LookupElement {
- private String beanName;
+ private final String beanName;
public EjbRefElement(Member member, PropertyDescriptor pd) {
super(member, pd);
- }
-
- @Override
- protected void initAnnotation(AnnotatedElement ae) {
+ AnnotatedElement ae = (AnnotatedElement) member;
EJB resource = ae.getAnnotation(EJB.class);
String resourceBeanName = resource.beanName();
String resourceName = resource.name();
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java
index eaab654ec702..f90f4d98588b 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -42,7 +42,7 @@
* always registered, meaning that any attempt to disable them at the
* {@code @ComponentScan} level would be ignored.
*
- * See @{@link Configuration} Javadoc for usage examples.
+ *
See @{@link Configuration}'s javadoc for usage examples.
*
* @author Chris Beams
* @since 3.1
@@ -139,24 +139,18 @@
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
+
/**
- * The type of filter to use.
- *
Note that the filter types available are limited to those that may
- * be expressed as a {@code Class} in the {@link #value()} attribute. This is
- * in contrast to {@code }, which allows for
- * expression-based (i.e., string-based) filters such as AspectJ pointcuts.
- * These filter types are intentionally not supported here, and not available
- * in the {@link FilterType} enum.
- * @see FilterType
+ * The type of filter to use. Default is {@link FilterType#ANNOTATION}.
*/
FilterType type() default FilterType.ANNOTATION;
/**
* The class or classes to use as the filter. In the case of
- * {@link FilterType#ANNOTATION}, the class will be the annotation itself. In the
- * case of {@link FilterType#ASSIGNABLE_TYPE}, the class will be the type that
- * detected components should be assignable to. And in the case of
- * {@link FilterType#CUSTOM}, the class will be an implementation of
+ * {@link FilterType#ANNOTATION}, the class will be the annotation itself.
+ * In the case of {@link FilterType#ASSIGNABLE_TYPE}, the class will be the
+ * type that detected components should be assignable to. And in the case
+ * of {@link FilterType#CUSTOM}, the class will be an implementation of
* {@link TypeFilter}.
*
When multiple classes are specified, OR logic is applied, e.g. "include
* types annotated with {@code @Foo} OR {@code @Bar}".
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java
index 48af4ed5a0e0..7519495de093 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -28,6 +28,7 @@
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
@@ -64,7 +65,7 @@ public ComponentScanAnnotationParser(ResourceLoader resourceLoader, Environment
}
- public Set parse(AnnotationAttributes componentScan, String declaringClass) {
+ public Set parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner =
new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"));
@@ -120,6 +121,12 @@ public Set parse(AnnotationAttributes componentScan, Strin
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
+ scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
+ @Override
+ protected boolean matchClassName(String className) {
+ return declaringClass.equals(className);
+ }
+ });
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
index a89be5597701..644065e5dc23 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
@@ -165,7 +165,6 @@ public Map> getImportedResources()
return this.importedResources;
}
-
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation)
if (getMetadata().isAnnotated(Configuration.class.getName())) {
@@ -196,7 +195,6 @@ public void validate(ProblemReporter problemReporter) {
}
}
-
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof ConfigurationClass &&
@@ -210,7 +208,7 @@ public int hashCode() {
@Override
public String toString() {
- return String.format("[ConfigurationClass:beanName=%s,resource=%s]", this.beanName, this.resource);
+ return "ConfigurationClass:beanName=" + this.beanName + ",resource=" + this.resource;
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
index 27ae171955cc..5250f5e34c64 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
@@ -156,7 +156,7 @@ private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
- RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass);
+ ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass);
beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
if (metadata.isStatic()) {
@@ -188,9 +188,18 @@ private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
// has this already been overridden (e.g. via XML)?
if (this.registry.containsBeanDefinition(beanName)) {
- BeanDefinition existingBeanDef = registry.getBeanDefinition(beanName);
- // is the existing bean definition one that was created from a configuration class?
- if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
+ BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);
+ // Is the existing bean definition one that was created from a configuration class?
+ // -> allow the current bean method to override, since both are at second-pass level.
+ // However, if the bean method is an overloaded case on the same configuration class,
+ // preserve the existing bean definition.
+ if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
+ ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
+ if (ccbd.getMetadata().getClassName().equals(beanMethod.getConfigurationClass().getMetadata().getClassName())) {
+ return;
+ }
+ }
+ else {
// no -> then it's an external override, probably XML
// overriding is legal, return immediately
if (logger.isDebugEnabled()) {
@@ -238,7 +247,7 @@ else if (configClass.getMetadata().isAnnotated(Lazy.class.getName())){
beanDef.setDestroyMethodName(destroyMethodName);
}
- // consider scoping
+ // Consider scoping
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
AnnotationAttributes scope = attributesFor(metadata, Scope.class);
if (scope != null) {
@@ -249,7 +258,7 @@ else if (configClass.getMetadata().isAnnotated(Lazy.class.getName())){
}
}
- // replace the original bean definition with the target one, if necessary
+ // Replace the original bean definition with the target one, if necessary
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
@@ -259,7 +268,8 @@ else if (configClass.getMetadata().isAnnotated(Lazy.class.getName())){
}
if (logger.isDebugEnabled()) {
- logger.debug(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getMetadata().getClassName(), beanName));
+ logger.debug(String.format("Registering bean definition for @Bean method %s.%s()",
+ configClass.getMetadata().getClassName(), beanName));
}
registry.registerBeanDefinition(beanName, beanDefToRegister);
@@ -310,6 +320,7 @@ private static class ConfigurationClassBeanDefinition extends RootBeanDefinition
public ConfigurationClassBeanDefinition(ConfigurationClass configClass) {
this.annotationMetadata = configClass.getMetadata();
+ setLenientConstructorResolution(false);
}
public ConfigurationClassBeanDefinition(RootBeanDefinition original, ConfigurationClass configClass) {
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
index c1748314bc89..29a1ea0357f4 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
@@ -287,11 +287,11 @@ private void processMemberClasses(AnnotationMetadata metadata) throws IOExceptio
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
String[] locations = propertySource.getStringArray("value");
- int nLocations = locations.length;
- if (nLocations == 0) {
+ int locationCount = locations.length;
+ if (locationCount == 0) {
throw new IllegalArgumentException("At least one @PropertySource(value) location is required");
}
- for (int i = 0; i < nLocations; i++) {
+ for (int i = 0; i < locationCount; i++) {
locations[i] = this.environment.resolveRequiredPlaceholders(locations[i]);
}
ClassLoader classLoader = this.resourceLoader.getClassLoader();
@@ -301,13 +301,13 @@ private void processPropertySource(AnnotationAttributes propertySource) throws I
}
}
else {
- if (nLocations == 1) {
+ if (locationCount == 1) {
this.propertySources.push(new ResourcePropertySource(name, locations[0], classLoader));
}
else {
CompositePropertySource ps = new CompositePropertySource(name);
- for (String location : locations) {
- ps.addPropertySource(new ResourcePropertySource(location, classLoader));
+ for (int i = locations.length - 1; i >= 0; i--) {
+ ps.addPropertySource(new ResourcePropertySource(locations[i], classLoader));
}
this.propertySources.push(ps);
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
index ddc7000b7f1d..6242ffef7561 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -27,7 +27,6 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
@@ -85,7 +84,7 @@
* @since 3.0
*/
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
- ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware, Ordered {
+ Ordered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
private static final String IMPORT_AWARE_PROCESSOR_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importAwareProcessor";
@@ -130,6 +129,10 @@ protected String buildDefaultBeanName(BeanDefinition definition) {
};
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE;
+ }
+
/**
* Set the {@link SourceExtractor} to use for generated bean definitions
* that correspond to {@link Bean} factory methods.
@@ -368,21 +371,21 @@ public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFact
}
}
- @Override
- public int getOrder() {
- return Ordered.HIGHEST_PRECEDENCE;
- }
-
- private static class ImportAwareBeanPostProcessor implements PriorityOrdered, BeanFactoryAware, BeanPostProcessor {
+ private static class ImportAwareBeanPostProcessor implements BeanPostProcessor, PriorityOrdered, BeanFactoryAware {
private BeanFactory beanFactory;
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof ImportAware) {
ImportRegistry importRegistry = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
String importingClass = importRegistry.getImportingClassFor(bean.getClass().getSuperclass().getName());
@@ -404,13 +407,9 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro
return bean;
}
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
-
- public int getOrder() {
- return Ordered.HIGHEST_PRECEDENCE;
- }
}
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/FilterType.java b/spring-context/src/main/java/org/springframework/context/annotation/FilterType.java
index 26bd302d8e8f..746424007dfe 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/FilterType.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/FilterType.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -16,9 +16,6 @@
package org.springframework.context.annotation;
-import org.springframework.core.type.filter.AssignableTypeFilter;
-
-
/**
* Enumeration of the type filters that may be used in conjunction with
* {@link ComponentScan @ComponentScan}.
@@ -42,12 +39,12 @@ public enum FilterType {
/**
* Filter candidates assignable to a given type.
- * @see AssignableTypeFilter
+ * @see org.springframework.core.type.filter.AssignableTypeFilter
*/
ASSIGNABLE_TYPE,
/** Filter candidates using a given custom
- * {@link org.springframework.core.type.filter.TypeFilter} implementation
+ * {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Profile.java b/spring-context/src/main/java/org/springframework/context/annotation/Profile.java
index ab793599c017..4d9c603910ae 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/Profile.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/Profile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -16,6 +16,7 @@
package org.springframework.context.annotation;
+import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -44,11 +45,11 @@
*
* If a {@code @Configuration} class is marked with {@code @Profile}, all of the
* {@code @Bean} methods and {@link Import @Import} annotations associated with that class
- * will be bypassed unless one or more the specified profiles are active. This is very
+ * will be bypassed unless one or more of the specified profiles are active. This is very
* similar to the behavior in Spring XML: if the {@code profile} attribute of the
* {@code beans} element is supplied e.g., {@code }, the
* {@code beans} element will not be parsed unless profiles 'p1' and/or 'p2' have been
- * activated. Likewise, if a {@code @Component} or {@code @Configuration} class is marked
+ * activated. Likewise, if a {@code @Component} or {@code @Configuration} class is marked
* with {@code @Profile({"p1", "p2"})}, that class will not be registered/processed unless
* profiles 'p1' and/or 'p2' have been activated.
*
@@ -73,10 +74,11 @@
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
+@Documented
public @interface Profile {
/**
- * The set of profiles for which this component should be registered.
+ * The set of profiles for which the annotated component should be registered.
*/
String[] value();
diff --git a/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java
index 055b5e59db84..f14f164ce6f7 100644
--- a/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java
+++ b/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -28,6 +28,7 @@
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.OrderComparator;
+import org.springframework.util.ObjectUtils;
/**
* Abstract implementation of the {@link ApplicationEventMulticaster} interface,
@@ -128,7 +129,8 @@ protected Collection getApplicationListeners() {
*/
protected Collection getApplicationListeners(ApplicationEvent event) {
Class extends ApplicationEvent> eventType = event.getClass();
- Class sourceType = event.getSource().getClass();
+ Object source = event.getSource();
+ Class> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
@@ -191,11 +193,11 @@ protected boolean supportsEvent(
*/
private static class ListenerCacheKey {
- private final Class eventType;
+ private final Class> eventType;
- private final Class sourceType;
+ private final Class> sourceType;
- public ListenerCacheKey(Class eventType, Class sourceType) {
+ public ListenerCacheKey(Class> eventType, Class> sourceType) {
this.eventType = eventType;
this.sourceType = sourceType;
}
@@ -206,12 +208,13 @@ public boolean equals(Object other) {
return true;
}
ListenerCacheKey otherKey = (ListenerCacheKey) other;
- return (this.eventType.equals(otherKey.eventType) && this.sourceType.equals(otherKey.sourceType));
+ return ObjectUtils.nullSafeEquals(this.eventType, otherKey.eventType) &&
+ ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType);
}
@Override
public int hashCode() {
- return this.eventType.hashCode() * 29 + this.sourceType.hashCode();
+ return ObjectUtils.nullSafeHashCode(this.eventType) * 29 + ObjectUtils.nullSafeHashCode(this.sourceType);
}
}
diff --git a/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java b/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
index a9326c5b168a..2fef497de283 100644
--- a/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
+++ b/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -74,7 +74,7 @@ public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
}
public boolean supportsSourceType(Class> sourceType) {
- return sourceType.isInstance(this.source);
+ return (sourceType != null && sourceType.isInstance(this.source));
}
public int getOrder() {
diff --git a/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java b/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java
index 2ca6ea2f85ca..c1d716793a26 100644
--- a/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java
+++ b/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -59,7 +59,8 @@ public static void resetLocaleContext() {
/**
* Associate the given LocaleContext with the current thread,
* not exposing it as inheritable for child threads.
- * @param localeContext the current LocaleContext
+ * @param localeContext the current LocaleContext,
+ * or {@code null} to reset the thread-bound context
*/
public static void setLocaleContext(LocaleContext localeContext) {
setLocaleContext(localeContext, false);
diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
index 6bfc380aa5db..bbc7841acbc5 100644
--- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -217,7 +217,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
* Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
- this(null);
+ this.resourcePatternResolver = getResourcePatternResolver();
}
/**
@@ -225,8 +225,8 @@ public AbstractApplicationContext() {
* @param parent the parent context
*/
public AbstractApplicationContext(ApplicationContext parent) {
- this.parent = parent;
- this.resourcePatternResolver = getResourcePatternResolver();
+ this();
+ setParent(parent);
}
diff --git a/spring-context/src/main/java/org/springframework/context/support/GenericXmlApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/GenericXmlApplicationContext.java
index e120f667fb59..eef4d80ba412 100644
--- a/spring-context/src/main/java/org/springframework/context/support/GenericXmlApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/support/GenericXmlApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -45,10 +45,9 @@ public class GenericXmlApplicationContext extends GenericApplicationContext {
/**
* Create a new GenericXmlApplicationContext that needs to be
- * {@linkplain #load loaded} and then manually {@link #refresh refreshed}.
+ * {@link #load loaded} and then manually {@link #refresh refreshed}.
*/
public GenericXmlApplicationContext() {
- reader.setEnvironment(this.getEnvironment());
}
/**
@@ -91,14 +90,13 @@ public void setValidating(boolean validating) {
}
/**
- * {@inheritDoc}
- * Delegates the given environment to underlying {@link XmlBeanDefinitionReader}.
- * Should be called before any call to {@link #load}.
+ * Delegates the given environment to underlying {@link XmlBeanDefinitionReader}.
+ * Should be called before any call to {@code #load}.
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
- this.reader.setEnvironment(this.getEnvironment());
+ this.reader.setEnvironment(getEnvironment());
}
/**
diff --git a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java
index b5c660f0236e..b67ab82505c7 100644
--- a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java
+++ b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java
@@ -29,7 +29,6 @@
import org.springframework.format.Formatter;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
-import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -45,6 +44,7 @@
public class DateFormatter implements Formatter {
private static final Map ISO_PATTERNS;
+
static {
Map formats = new HashMap(4);
formats.put(ISO.DATE, "yyyy-MM-dd");
@@ -168,34 +168,36 @@ private DateFormat createDateFormat(Locale locale) {
if (StringUtils.hasLength(this.pattern)) {
return new SimpleDateFormat(this.pattern, locale);
}
- if (iso != null && iso != ISO.NONE) {
- String pattern = ISO_PATTERNS.get(iso);
- Assert.state(pattern != null, "Unsupported ISO format " + iso);
+ if (this.iso != null && this.iso != ISO.NONE) {
+ String pattern = ISO_PATTERNS.get(this.iso);
+ if (pattern == null) {
+ throw new IllegalStateException("Unsupported ISO format " + this.iso);
+ }
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
return format;
}
- if(StringUtils.hasLength(stylePattern)) {
+ if (StringUtils.hasLength(this.stylePattern)) {
int dateStyle = getStylePatternForChar(0);
int timeStyle = getStylePatternForChar(1);
- if(dateStyle != -1 && timeStyle != -1) {
+ if (dateStyle != -1 && timeStyle != -1) {
return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
}
- if(dateStyle != -1) {
+ if (dateStyle != -1) {
return DateFormat.getDateInstance(dateStyle, locale);
}
- if(timeStyle != -1) {
+ if (timeStyle != -1) {
return DateFormat.getTimeInstance(timeStyle, locale);
}
- throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");
+ throw new IllegalStateException("Unsupported style pattern '"+ this.stylePattern+ "'");
}
return DateFormat.getDateInstance(this.style, locale);
}
private int getStylePatternForChar(int index) {
- if(stylePattern != null && stylePattern.length() > index) {
- switch (stylePattern.charAt(index)) {
+ if (this.stylePattern != null && this.stylePattern.length() > index) {
+ switch (this.stylePattern.charAt(index)) {
case 'S': return DateFormat.SHORT;
case 'M': return DateFormat.MEDIUM;
case 'L': return DateFormat.LONG;
@@ -203,7 +205,7 @@ private int getStylePatternForChar(int index) {
case '-': return -1;
}
}
- throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");
+ throw new IllegalStateException("Unsupported style pattern '" + this.stylePattern + "'");
}
}
diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeContext.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeContext.java
index e2db64c6bff8..e4db4a2557ad 100644
--- a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeContext.java
+++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeContext.java
@@ -38,31 +38,31 @@ public class JodaTimeContext {
/**
- * Set the user's chronology.
+ * Set the user's chronology (calendar system).
*/
public void setChronology(Chronology chronology) {
this.chronology = chronology;
}
/**
- * The user's chronology (calendar system), if any.
+ * Return the user's chronology (calendar system), if any.
*/
public Chronology getChronology() {
return this.chronology;
}
/**
- * Set the user's timezone.
+ * Set the user's time zone.
*/
public void setTimeZone(DateTimeZone timeZone) {
this.timeZone = timeZone;
}
/**
- * The user's timezone, if any.
+ * Return the user's time zone, if any.
*/
public DateTimeZone getTimeZone() {
- return timeZone;
+ return this.timeZone;
}
diff --git a/spring-context/src/main/java/org/springframework/jndi/JndiObjectFactoryBean.java b/spring-context/src/main/java/org/springframework/jndi/JndiObjectFactoryBean.java
index 504a96ad8c64..a906dab947bb 100644
--- a/spring-context/src/main/java/org/springframework/jndi/JndiObjectFactoryBean.java
+++ b/spring-context/src/main/java/org/springframework/jndi/JndiObjectFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -25,8 +25,14 @@
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.SimpleTypeConverter;
+import org.springframework.beans.TypeConverter;
+import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.util.ClassUtils;
/**
@@ -61,9 +67,10 @@
* @see #setCache
* @see JndiObjectTargetSource
*/
-public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryBean, BeanClassLoaderAware {
+public class JndiObjectFactoryBean extends JndiObjectLocator
+ implements FactoryBean, BeanFactoryAware, BeanClassLoaderAware {
- private Class[] proxyInterfaces;
+ private Class>[] proxyInterfaces;
private boolean lookupOnStartup = true;
@@ -73,6 +80,8 @@ public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryB
private Object defaultObject;
+ private ConfigurableBeanFactory beanFactory;
+
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private Object jndiObject;
@@ -87,8 +96,8 @@ public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryB
* @see #setLookupOnStartup
* @see #setCache
*/
- public void setProxyInterface(Class proxyInterface) {
- this.proxyInterfaces = new Class[] {proxyInterface};
+ public void setProxyInterface(Class> proxyInterface) {
+ this.proxyInterfaces = new Class>[] {proxyInterface};
}
/**
@@ -100,7 +109,7 @@ public void setProxyInterface(Class proxyInterface) {
* @see #setLookupOnStartup
* @see #setCache
*/
- public void setProxyInterfaces(Class[] proxyInterfaces) {
+ public void setProxyInterfaces(Class>... proxyInterfaces) {
this.proxyInterfaces = proxyInterfaces;
}
@@ -149,12 +158,26 @@ public void setExposeAccessContext(boolean exposeAccessContext) {
* It is typically used for literal values in scenarios where the JNDI environment
* might define specific config settings but those are not required to be present.
* Note: This is only supported for lookup on startup.
+ * If specified together with {@link #setExpectedType}, the specified value
+ * needs to be either of that type or convertible to it.
* @see #setLookupOnStartup
+ * @see ConfigurableBeanFactory#getTypeConverter()
+ * @see SimpleTypeConverter
*/
public void setDefaultObject(Object defaultObject) {
this.defaultObject = defaultObject;
}
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) {
+ if (beanFactory instanceof ConfigurableBeanFactory) {
+ // Just optional - for getting a specifically configured TypeConverter if needed.
+ // We'll simply fall back to a SimpleTypeConverter if no specific one available.
+ this.beanFactory = (ConfigurableBeanFactory) beanFactory;
+ }
+ }
+
+ @Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
@@ -179,9 +202,16 @@ public void afterPropertiesSet() throws IllegalArgumentException, NamingExceptio
else {
if (this.defaultObject != null && getExpectedType() != null &&
!getExpectedType().isInstance(this.defaultObject)) {
- throw new IllegalArgumentException("Default object [" + this.defaultObject +
- "] of type [" + this.defaultObject.getClass().getName() +
- "] is not of expected type [" + getExpectedType().getName() + "]");
+ TypeConverter converter = (this.beanFactory != null ?
+ this.beanFactory.getTypeConverter() : new SimpleTypeConverter());
+ try {
+ this.defaultObject = converter.convertIfNecessary(this.defaultObject, getExpectedType());
+ }
+ catch (TypeMismatchException ex) {
+ throw new IllegalArgumentException("Default object [" + this.defaultObject + "] of type [" +
+ this.defaultObject.getClass().getName() + "] is not of expected type [" +
+ getExpectedType().getName() + "] and cannot be converted either", ex);
+ }
}
// Locate specified JNDI object.
this.jndiObject = lookupWithFallback();
@@ -263,7 +293,7 @@ public boolean isSingleton() {
* @return the merged interface as Class
* @see java.lang.reflect.Proxy#getProxyClass
*/
- protected Class createCompositeInterface(Class[] interfaces) {
+ protected Class> createCompositeInterface(Class>[] interfaces) {
return ClassUtils.createCompositeInterface(interfaces, this.beanClassLoader);
}
@@ -290,13 +320,13 @@ private static Object createJndiObjectProxy(JndiObjectFactoryBean jof) throws Na
proxyFactory.setInterfaces(jof.proxyInterfaces);
}
else {
- Class targetClass = targetSource.getTargetClass();
+ Class> targetClass = targetSource.getTargetClass();
if (targetClass == null) {
throw new IllegalStateException(
"Cannot deactivate 'lookupOnStartup' without specifying a 'proxyInterface' or 'expectedType'");
}
- Class[] ifcs = ClassUtils.getAllInterfacesForClass(targetClass, jof.beanClassLoader);
- for (Class ifc : ifcs) {
+ Class>[] ifcs = ClassUtils.getAllInterfacesForClass(targetClass, jof.beanClassLoader);
+ for (Class> ifc : ifcs) {
if (Modifier.isPublic(ifc.getModifiers())) {
proxyFactory.addInterface(ifc);
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java
index 91764b332865..d13a2f616239 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java
@@ -63,8 +63,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
- * Set the ThreadFactory to use for the ThreadPoolExecutor's thread pool.
- * Default is the ThreadPoolExecutor's default thread factory.
+ * Set the ThreadFactory to use for the ExecutorService's thread pool.
+ * Default is the underlying ExecutorService's default thread factory.
* @see java.util.concurrent.Executors#defaultThreadFactory()
*/
public void setThreadFactory(ThreadFactory threadFactory) {
@@ -78,8 +78,8 @@ public void setThreadNamePrefix(String threadNamePrefix) {
}
/**
- * Set the RejectedExecutionHandler to use for the ThreadPoolExecutor.
- * Default is the ThreadPoolExecutor's default abort policy.
+ * Set the RejectedExecutionHandler to use for the ExecutorService.
+ * Default is the ExecutorService's default abort policy.
* @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy
*/
public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) {
@@ -181,7 +181,7 @@ public void destroy() {
}
/**
- * Perform a shutdown on the ThreadPoolExecutor.
+ * Perform a shutdown on the underlying ExecutorService.
* @see java.util.concurrent.ExecutorService#shutdown()
* @see java.util.concurrent.ExecutorService#shutdownNow()
* @see #awaitTerminationIfNecessary()
diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java
index 40d48ad29554..41707680206d 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -37,9 +37,10 @@
*
*
Allows for registration of {@link ScheduledExecutorTask ScheduledExecutorTasks},
* automatically starting the {@link ScheduledExecutorService} on initialization and
- * cancelling it on destruction of the context. In scenarios that just require static
+ * cancelling it on destruction of the context. In scenarios that only require static
* registration of tasks at startup, there is no need to access the
- * {@link ScheduledExecutorService} instance itself in application code.
+ * {@link ScheduledExecutorService} instance itself in application code at all;
+ * ScheduledExecutorFactoryBean is then just being used for lifecycle integration.
*
*
Note that {@link java.util.concurrent.ScheduledExecutorService}
* uses a {@link Runnable} instance that is shared between repeated executions,
@@ -92,7 +93,7 @@ public void setPoolSize(int poolSize) {
* @see java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)
* @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)
*/
- public void setScheduledExecutorTasks(ScheduledExecutorTask[] scheduledExecutorTasks) {
+ public void setScheduledExecutorTasks(ScheduledExecutorTask... scheduledExecutorTasks) {
this.scheduledExecutorTasks = scheduledExecutorTasks;
}
@@ -192,9 +193,9 @@ protected void registerTasks(ScheduledExecutorTask[] tasks, ScheduledExecutorSer
* @return the actual Runnable to schedule (may be a decorator)
*/
protected Runnable getRunnableToSchedule(ScheduledExecutorTask task) {
- return this.continueScheduledExecutionAfterException
- ? new DelegatingErrorHandlingRunnable(task.getRunnable(), TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER)
- : new DelegatingErrorHandlingRunnable(task.getRunnable(), TaskUtils.LOG_AND_PROPAGATE_ERROR_HANDLER);
+ return (this.continueScheduledExecutionAfterException ?
+ new DelegatingErrorHandlingRunnable(task.getRunnable(), TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER) :
+ new DelegatingErrorHandlingRunnable(task.getRunnable(), TaskUtils.LOG_AND_PROPAGATE_ERROR_HANDLER));
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java
index 3e04248ea868..3f912aa27f24 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java
@@ -71,7 +71,6 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport
/**
* Set the ThreadPoolExecutor's core pool size.
* Default is 1.
- *
This setting can be modified at runtime, for example through JMX.
*/
public void setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
@@ -80,7 +79,6 @@ public void setCorePoolSize(int corePoolSize) {
/**
* Set the ThreadPoolExecutor's maximum pool size.
* Default is {@code Integer.MAX_VALUE}.
- *
This setting can be modified at runtime, for example through JMX.
*/
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
@@ -89,7 +87,6 @@ public void setMaxPoolSize(int maxPoolSize) {
/**
* Set the ThreadPoolExecutor's keep-alive seconds.
* Default is 60.
- *
This setting can be modified at runtime, for example through JMX.
*/
public void setKeepAliveSeconds(int keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
diff --git a/spring-context/src/main/java/org/springframework/scripting/support/ResourceScriptSource.java b/spring-context/src/main/java/org/springframework/scripting/support/ResourceScriptSource.java
index c0de42493f40..9934edc0ea5b 100644
--- a/spring-context/src/main/java/org/springframework/scripting/support/ResourceScriptSource.java
+++ b/spring-context/src/main/java/org/springframework/scripting/support/ResourceScriptSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -23,6 +23,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.springframework.core.io.Resource;
import org.springframework.scripting.ScriptSource;
import org.springframework.util.Assert;
@@ -51,11 +52,12 @@ public class ResourceScriptSource implements ScriptSource {
private final Resource resource;
+ private String encoding = "UTF-8";
+
private long lastModified = -1;
private final Object lastModifiedMonitor = new Object();
- private String encoding = "UTF-8";
/**
* Create a new ResourceScriptSource for the given resource.
@@ -74,15 +76,23 @@ public final Resource getResource() {
return this.resource;
}
+ /**
+ * Set the encoding used for reading the script resource.
+ *
The default value for regular Resources is "UTF-8".
+ * A {@code null} value implies the platform default.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+
public String getScriptAsString() throws IOException {
synchronized (this.lastModifiedMonitor) {
this.lastModified = retrieveLastModifiedTime();
}
-
InputStream stream = this.resource.getInputStream();
- Reader reader = (StringUtils.hasText(encoding) ? new InputStreamReader(stream, encoding)
- : new InputStreamReader(stream));
-
+ Reader reader = (StringUtils.hasText(this.encoding) ? new InputStreamReader(stream, this.encoding) :
+ new InputStreamReader(stream));
return FileCopyUtils.copyToString(reader);
}
@@ -99,10 +109,11 @@ public boolean isModified() {
protected long retrieveLastModifiedTime() {
try {
return getResource().lastModified();
- } catch (IOException ex) {
+ }
+ catch (IOException ex) {
if (logger.isDebugEnabled()) {
- logger.debug(getResource() + " could not be resolved in the file system - "
- + "current timestamp not available for script modification check", ex);
+ logger.debug(getResource() + " could not be resolved in the file system - " +
+ "current timestamp not available for script modification check", ex);
}
return 0;
}
@@ -112,18 +123,9 @@ public String suggestedClassName() {
return StringUtils.stripFilenameExtension(getResource().getFilename());
}
- /**
- * Sets the encoding used for reading the script resource. The default value is "UTF-8".
- * A null value, implies the platform default.
- *
- * @param encoding charset encoding used for reading the script.
- */
- public void setEncoding(String encoding) {
- this.encoding = encoding;
- }
-
@Override
public String toString() {
return this.resource.toString();
}
+
}
diff --git a/spring-context/src/main/resources/org/springframework/context/config/spring-context-3.1.xsd b/spring-context/src/main/resources/org/springframework/context/config/spring-context-3.1.xsd
index b2132e4e1c2e..34dca16031a3 100644
--- a/spring-context/src/main/resources/org/springframework/context/config/spring-context-3.1.xsd
+++ b/spring-context/src/main/resources/org/springframework/context/config/spring-context-3.1.xsd
@@ -1,13 +1,15 @@
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:tool="http://www.springframework.org/schema/tool"
+ targetNamespace="http://www.springframework.org/schema/context"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
-
-
+
+
+ type="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
@@ -158,14 +160,13 @@
]]>
-
+
-
+
@@ -273,9 +274,8 @@
]]>
-
-
+
+
@@ -288,9 +288,8 @@
]]>
-
-
+
+
@@ -304,9 +303,9 @@
-
-
-
+
+
+
@@ -342,8 +341,7 @@
]]>
-
+
@@ -355,9 +353,8 @@
]]>
-
-
+
+
@@ -396,23 +393,20 @@
-
-
-
+
-
-
+
@@ -455,9 +448,9 @@
-
-
-
+
+
+
@@ -466,8 +459,7 @@
-
-
+
@@ -503,8 +495,8 @@
"annotation" indicates an annotation to be present at the type level in target components;
"assignable" indicates a class (or interface) that the target components are assignable to (extend/implement);
- "aspectj" indicates an AspectJ type expression to be matched by the target components;
- "regex" indicates a regex expression to be matched by the target components' class names;
+ "aspectj" indicates an AspectJ type pattern expression to be matched by the target components;
+ "regex" indicates a regex pattern to be matched by the target components' class names;
"custom" indicates a custom implementation of the org.springframework.core.type.TypeFilter interface.
Note: This attribute will not be inherited by child bean definitions.
@@ -513,11 +505,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/spring-context/src/main/resources/org/springframework/context/config/spring-context-3.2.xsd b/spring-context/src/main/resources/org/springframework/context/config/spring-context-3.2.xsd
index f63ecc1aafdf..d650f0713566 100644
--- a/spring-context/src/main/resources/org/springframework/context/config/spring-context-3.2.xsd
+++ b/spring-context/src/main/resources/org/springframework/context/config/spring-context-3.2.xsd
@@ -1,13 +1,15 @@
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:tool="http://www.springframework.org/schema/tool"
+ targetNamespace="http://www.springframework.org/schema/context"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
-
-
+
+
+ type="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
@@ -158,14 +160,13 @@
]]>
-
+
-
+
@@ -273,9 +274,8 @@
]]>
-
-
+
+
@@ -288,9 +288,8 @@
]]>
-
-
+
+
@@ -304,9 +303,9 @@
-
-
-
+
+
+
@@ -342,8 +341,7 @@
]]>
-
+
@@ -355,9 +353,8 @@
]]>
-
-
+
+
@@ -396,23 +393,20 @@
-
-
-
+
-
-
+
@@ -455,9 +448,9 @@
-
-
-
+
+
+
@@ -466,8 +459,7 @@
-
-
+
@@ -503,8 +495,8 @@
"annotation" indicates an annotation to be present at the type level in target components;
"assignable" indicates a class (or interface) that the target components are assignable to (extend/implement);
- "aspectj" indicates an AspectJ type expression to be matched by the target components;
- "regex" indicates a regex expression to be matched by the target components' class names;
+ "aspectj" indicates an AspectJ type pattern expression to be matched by the target components;
+ "regex" indicates a regex pattern to be matched by the target components' class names;
"custom" indicates a custom implementation of the org.springframework.core.type.TypeFilter interface.
Note: This attribute will not be inherited by child bean definitions.
@@ -513,11 +505,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/spring-context/src/test/java/org/springframework/beans/factory/xml/QualifierAnnotationTests-context.xml b/spring-context/src/test/java/org/springframework/beans/factory/xml/QualifierAnnotationTests-context.xml
index bdc524aa8bac..2f7db2105691 100644
--- a/spring-context/src/test/java/org/springframework/beans/factory/xml/QualifierAnnotationTests-context.xml
+++ b/spring-context/src/test/java/org/springframework/beans/factory/xml/QualifierAnnotationTests-context.xml
@@ -7,11 +7,19 @@
+
+
+
+ larry
+
+
+
+
-
+
diff --git a/spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java
index 6f407fb24e07..d92309186e29 100644
--- a/spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java
+++ b/spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java
@@ -16,8 +16,13 @@
package org.springframework.context;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.util.Locale;
+import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.AbstractListableBeanFactoryTests;
import org.springframework.tests.sample.beans.LifecycleBean;
@@ -129,11 +134,29 @@ public void testMessageSource() throws NoSuchMessageException {
}
public void testEvents() throws Exception {
+ doTestEvents(this.listener, this.parentListener, new MyEvent(this));
+ }
+
+ @Test
+ public void testEventsWithNoSource() throws Exception {
+ // See SPR-10945 Serialized events result in a null source
+ MyEvent event = new MyEvent(this);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ oos.writeObject(event);
+ oos.close();
+ event = (MyEvent) new ObjectInputStream(new ByteArrayInputStream(
+ bos.toByteArray())).readObject();
+ doTestEvents(this.listener, this.parentListener, event);
+ }
+
+ protected void doTestEvents(TestListener listener, TestListener parentListener,
+ MyEvent event) {
listener.zeroCounter();
parentListener.zeroCounter();
assertTrue("0 events before publication", listener.getEventCount() == 0);
assertTrue("0 parent events before publication", parentListener.getEventCount() == 0);
- this.applicationContext.publishEvent(new MyEvent(this));
+ this.applicationContext.publishEvent(event);
assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1);
assertTrue("1 parent events after publication", parentListener.getEventCount() == 1);
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java b/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java
index 1d573540f8a2..ebdc073b9b7b 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java
@@ -16,61 +16,83 @@
package org.springframework.context.annotation;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.lang.annotation.Inherited;
+import java.util.List;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.beans.factory.support.RootBeanDefinition;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
/**
* Tests regarding overloading and overriding of bean methods.
* Related to SPR-6618.
*
- * Bean-annotated methods should be able to be overridden, just as any regular
- * method. This is straightforward.
- *
- * Bean-annotated methods should be able to be overloaded, though supporting this
- * is more subtle. Essentially, it must be unambiguous to the container which bean
- * method to call. A simple way to think about this is that no one Configuration
- * class may declare two bean methods with the same name. In the case of inheritance,
- * the most specific subclass bean method will always be the one that is invoked.
- *
* @author Chris Beams
+ * @author Phillip Webb
+ * @author Juergen Hoeller
*/
public class BeanMethodPolymorphismTests {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+
+ @Test
+ public void beanMethodDetectedOnSuperClass() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
+ ctx.getBean("testBean", TestBean.class);
+ }
+
+ @Test
+ public void beanMethodOverriding() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.register(OverridingConfig.class);
+ ctx.setAllowBeanDefinitionOverriding(false);
+ ctx.refresh();
+ assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
+ assertEquals("overridden", ctx.getBean("testBean", TestBean.class).toString());
+ assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("testBean"));
+ }
+
@Test
public void beanMethodOverloadingWithoutInheritance() {
+
@SuppressWarnings({ "hiding" })
@Configuration class Config {
@Bean String aString() { return "na"; }
@Bean String aString(Integer dependency) { return "na"; }
}
- try {
- new AnnotationConfigApplicationContext(Config.class);
- fail("expected bean method overloading exception");
- } catch (BeanDefinitionParsingException ex) {
- assertTrue(ex.getMessage(), ex.getMessage().contains("2 overloaded @Bean methods named 'aString'"));
- }
+
+ this.thrown.expect(BeanDefinitionParsingException.class);
+ this.thrown.expectMessage("overloaded @Bean methods named 'aString'");
+ new AnnotationConfigApplicationContext(Config.class);
}
@Test
public void beanMethodOverloadingWithInheritance() {
- AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SubConfig.class);
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.register(SubConfig.class);
+ ctx.setAllowBeanDefinitionOverriding(false);
+ ctx.refresh();
+ assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
assertThat(ctx.getBean(String.class), equalTo("overloaded5"));
+ assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
}
- static @Configuration class SuperConfig {
- @Bean String aString() { return "super"; }
- }
- static @Configuration class SubConfig {
- @Bean Integer anInt() { return 5; }
- @Bean String aString(Integer dependency) { return "overloaded"+dependency; }
+
+ // SPR-11025
+ @Test
+ public void beanMethodOverloadingWithInheritanceAndList() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.register(SubConfigWithList.class);
+ ctx.setAllowBeanDefinitionOverriding(false);
+ ctx.refresh();
+ assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
+ assertThat(ctx.getBean(String.class), equalTo("overloaded5"));
+ assertTrue(ctx.getDefaultListableBeanFactory().containsSingleton("aString"));
}
/**
@@ -83,24 +105,6 @@ public void beanMethodShadowing() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ShadowConfig.class);
assertThat(ctx.getBean(String.class), equalTo("shadow"));
}
- @Import(SubConfig.class)
- static @Configuration class ShadowConfig {
- @Bean String aString() { return "shadow"; }
- }
-
- /**
- * Tests that polymorphic Configuration classes need not explicitly redeclare the
- * {@link Configuration} annotation. This respects the {@link Inherited} nature
- * of the Configuration annotation, even though it's being detected via ASM.
- */
- @Test
- public void beanMethodsDetectedOnSuperClass() {
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
- ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
- pp.postProcessBeanFactory(beanFactory);
- beanFactory.getBean("testBean", TestBean.class);
- }
@Configuration
@@ -117,4 +121,71 @@ public TestBean testBean() {
static class Config extends BaseConfig {
}
+
+ @Configuration
+ static class OverridingConfig extends BaseConfig {
+
+ @Bean @Lazy
+ @Override
+ public TestBean testBean() {
+ return new TestBean() {
+ @Override
+ public String toString() {
+ return "overridden";
+ }
+ };
+ }
+ }
+
+
+ @Configuration
+ static class SuperConfig {
+
+ @Bean
+ String aString() {
+ return "super";
+ }
+ }
+
+
+ @Configuration
+ static class SubConfig extends SuperConfig {
+
+ @Bean
+ Integer anInt() {
+ return 5;
+ }
+
+ @Bean @Lazy
+ String aString(Integer dependency) {
+ return "overloaded" + dependency;
+ }
+ }
+
+
+ @Configuration
+ static class SubConfigWithList extends SuperConfig {
+
+ @Bean
+ Integer anInt() {
+ return 5;
+ }
+
+ @Bean @Lazy
+ String aString(List dependency) {
+ return "overloaded" + dependency.get(0);
+ }
+ }
+
+
+ @Configuration
+ @Import(SubConfig.class)
+ static class ShadowConfig {
+
+ @Bean
+ String aString() {
+ return "shadow";
+ }
+ }
+
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java
index aaf2a82c18f7..45673cfd654d 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java
@@ -130,6 +130,18 @@ public void withNameAndMultipleResourceLocations() {
assertThat(ctx.getEnvironment().containsProperty("from.p2"), is(true));
}
+ /**
+ * SPR-10820
+ */
+ @Test
+ public void orderingWithAndWithoutNameAndMultipleResourceLocations() {
+ // p2 should 'win' as it was registered last
+ AnnotationConfigApplicationContext ctxWithName = new AnnotationConfigApplicationContext(ConfigWithNameAndMultipleResourceLocations.class);
+ AnnotationConfigApplicationContext ctxWithoutName = new AnnotationConfigApplicationContext(ConfigWithMultipleResourceLocations.class);
+ assertThat(ctxWithoutName.getEnvironment().getProperty("testbean.name"), equalTo("p2TestBean"));
+ assertThat(ctxWithName.getEnvironment().getProperty("testbean.name"), equalTo("p2TestBean"));
+ }
+
@Test(expected=IllegalArgumentException.class)
public void withEmptyResourceLocations() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@@ -209,6 +221,15 @@ static class P2Config {
static class ConfigWithNameAndMultipleResourceLocations {
}
+ @Configuration
+ @PropertySource(
+ value = {
+ "classpath:org/springframework/context/annotation/p1.properties",
+ "classpath:org/springframework/context/annotation/p2.properties"
+ })
+ static class ConfigWithMultipleResourceLocations {
+ }
+
@Configuration
@PropertySource(value = {})
diff --git a/spring-context/src/test/java/org/springframework/jndi/JndiObjectFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/jndi/JndiObjectFactoryBeanTests.java
index d9d7e60ac6b5..5d14e715a376 100644
--- a/spring-context/src/test/java/org/springframework/jndi/JndiObjectFactoryBeanTests.java
+++ b/spring-context/src/test/java/org/springframework/jndi/JndiObjectFactoryBeanTests.java
@@ -20,6 +20,8 @@
import javax.naming.NamingException;
import org.junit.Test;
+
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.tests.mock.jndi.ExpectedLookupTemplate;
import org.springframework.tests.sample.beans.DerivedTestBean;
import org.springframework.tests.sample.beans.ITestBean;
@@ -142,8 +144,7 @@ public void testLookupWithExpectedTypeAndMatch() throws Exception {
@Test
public void testLookupWithExpectedTypeAndNoMatch() throws Exception {
JndiObjectFactoryBean jof = new JndiObjectFactoryBean();
- Object o = new Object();
- jof.setJndiTemplate(new ExpectedLookupTemplate("foo", o));
+ jof.setJndiTemplate(new ExpectedLookupTemplate("foo", new Object()));
jof.setJndiName("foo");
jof.setExpectedType(String.class);
try {
@@ -151,15 +152,14 @@ public void testLookupWithExpectedTypeAndNoMatch() throws Exception {
fail("Should have thrown NamingException");
}
catch (NamingException ex) {
- assertTrue(ex.getMessage().indexOf("java.lang.String") != -1);
+ assertTrue(ex.getMessage().contains("java.lang.String"));
}
}
@Test
public void testLookupWithDefaultObject() throws Exception {
JndiObjectFactoryBean jof = new JndiObjectFactoryBean();
- String s = "";
- jof.setJndiTemplate(new ExpectedLookupTemplate("foo", s));
+ jof.setJndiTemplate(new ExpectedLookupTemplate("foo", ""));
jof.setJndiName("myFoo");
jof.setExpectedType(String.class);
jof.setDefaultObject("myString");
@@ -170,8 +170,7 @@ public void testLookupWithDefaultObject() throws Exception {
@Test
public void testLookupWithDefaultObjectAndExpectedType() throws Exception {
JndiObjectFactoryBean jof = new JndiObjectFactoryBean();
- String s = "";
- jof.setJndiTemplate(new ExpectedLookupTemplate("foo", s));
+ jof.setJndiTemplate(new ExpectedLookupTemplate("foo", ""));
jof.setJndiName("myFoo");
jof.setExpectedType(String.class);
jof.setDefaultObject("myString");
@@ -179,14 +178,36 @@ public void testLookupWithDefaultObjectAndExpectedType() throws Exception {
assertEquals("myString", jof.getObject());
}
+ @Test
+ public void testLookupWithDefaultObjectAndExpectedTypeConversion() throws Exception {
+ JndiObjectFactoryBean jof = new JndiObjectFactoryBean();
+ jof.setJndiTemplate(new ExpectedLookupTemplate("foo", ""));
+ jof.setJndiName("myFoo");
+ jof.setExpectedType(Integer.class);
+ jof.setDefaultObject("5");
+ jof.afterPropertiesSet();
+ assertEquals(new Integer(5), jof.getObject());
+ }
+
+ @Test
+ public void testLookupWithDefaultObjectAndExpectedTypeConversionViaBeanFactory() throws Exception {
+ JndiObjectFactoryBean jof = new JndiObjectFactoryBean();
+ jof.setJndiTemplate(new ExpectedLookupTemplate("foo", ""));
+ jof.setJndiName("myFoo");
+ jof.setExpectedType(Integer.class);
+ jof.setDefaultObject("5");
+ jof.setBeanFactory(new DefaultListableBeanFactory());
+ jof.afterPropertiesSet();
+ assertEquals(new Integer(5), jof.getObject());
+ }
+
@Test
public void testLookupWithDefaultObjectAndExpectedTypeNoMatch() throws Exception {
JndiObjectFactoryBean jof = new JndiObjectFactoryBean();
- String s = "";
- jof.setJndiTemplate(new ExpectedLookupTemplate("foo", s));
+ jof.setJndiTemplate(new ExpectedLookupTemplate("foo", ""));
jof.setJndiName("myFoo");
- jof.setExpectedType(String.class);
- jof.setDefaultObject(Boolean.TRUE);
+ jof.setExpectedType(Boolean.class);
+ jof.setDefaultObject("5");
try {
jof.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
diff --git a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
index e5a3834528cd..b860aaafca8b 100644
--- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
+++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
@@ -131,8 +131,10 @@ public static Class> resolveReturnType(Method method, Class> clazz) {
* invoked, never {@code null}
* @return the resolved target return type, the standard return type, or {@code null}
* @since 3.2
- * @see #resolveReturnType
+ * @deprecated in favor of resolveReturnTypeForFactoryMethod in the internal
+ * AutowireUtils class in the beans module; we do not expect other use of it!
*/
+ @Deprecated
public static Class> resolveReturnTypeForGenericMethod(Method method, Object[] args) {
Assert.notNull(method, "Method must not be null");
Assert.notNull(args, "Argument array must not be null");
@@ -229,7 +231,7 @@ public static Class> resolveReturnTypeArgument(Method method, Class> generic
* @return the resolved type of the argument, or {@code null} if not resolvable
*/
public static Class> resolveTypeArgument(Class> clazz, Class> genericIfc) {
- Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
+ Class>[] typeArgs = resolveTypeArguments(clazz, genericIfc);
if (typeArgs == null) {
return null;
}
@@ -244,21 +246,25 @@ public static Class> resolveTypeArgument(Class> clazz, Class> genericIfc)
* Resolve the type arguments of the given generic interface against the given
* target class which is assumed to implement the generic interface and possibly
* declare concrete types for its type variables.
+ * Note: In Spring 3.2, this method doesn't return {@code null} in all scenarios
+ * where it should. To be fixed in Spring 4.0; for client code, this just means it
+ * might see {@code null} in a few more cases then where it now sees an array with
+ * a single {@link Object} type.
* @param clazz the target class to check against
* @param genericIfc the generic interface or superclass to resolve the type argument from
* @return the resolved type of each argument, with the array size matching the
* number of actual type arguments, or {@code null} if not resolvable
*/
- public static Class[] resolveTypeArguments(Class> clazz, Class> genericIfc) {
+ public static Class>[] resolveTypeArguments(Class> clazz, Class> genericIfc) {
return doResolveTypeArguments(clazz, clazz, genericIfc);
}
- private static Class[] doResolveTypeArguments(Class> ownerClass, Class> classToIntrospect, Class> genericIfc) {
+ private static Class>[] doResolveTypeArguments(Class> ownerClass, Class> classToIntrospect, Class> genericIfc) {
while (classToIntrospect != null) {
if (genericIfc.isInterface()) {
Type[] ifcs = classToIntrospect.getGenericInterfaces();
for (Type ifc : ifcs) {
- Class[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
+ Class>[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
if (result != null) {
return result;
}
@@ -266,7 +272,7 @@ private static Class[] doResolveTypeArguments(Class> ownerClass, Class> clas
}
else {
try {
- Class[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
+ Class>[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
if (result != null) {
return result;
}
@@ -281,13 +287,13 @@ private static Class[] doResolveTypeArguments(Class> ownerClass, Class> clas
return null;
}
- private static Class[] doResolveTypeArguments(Class> ownerClass, Type ifc, Class> genericIfc) {
+ private static Class>[] doResolveTypeArguments(Class> ownerClass, Type ifc, Class> genericIfc) {
if (ifc instanceof ParameterizedType) {
ParameterizedType paramIfc = (ParameterizedType) ifc;
Type rawType = paramIfc.getRawType();
if (genericIfc.equals(rawType)) {
Type[] typeArgs = paramIfc.getActualTypeArguments();
- Class[] result = new Class[typeArgs.length];
+ Class>[] result = new Class[typeArgs.length];
for (int i = 0; i < typeArgs.length; i++) {
Type arg = typeArgs[i];
result[i] = extractClass(ownerClass, arg);
@@ -305,7 +311,7 @@ else if (ifc != null && genericIfc.isAssignableFrom((Class) ifc)) {
}
/**
- * Extract a class instance from given Type.
+ * Extract a Class from the given Type.
*/
private static Class> extractClass(Class> ownerClass, Type arg) {
if (arg instanceof ParameterizedType) {
@@ -322,9 +328,12 @@ else if (arg instanceof TypeVariable) {
arg = getTypeVariableMap(ownerClass).get(tv);
if (arg == null) {
arg = extractBoundForTypeVariable(tv);
+ if (arg instanceof ParameterizedType) {
+ return extractClass(ownerClass, ((ParameterizedType) arg).getRawType());
+ }
}
else {
- arg = extractClass(ownerClass, arg);
+ return extractClass(ownerClass, arg);
}
}
return (arg instanceof Class ? (Class) arg : Object.class);
diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java
index 28e9f90677d3..d2f497d81405 100644
--- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java
+++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -179,7 +179,7 @@ private AnnotatedElement getAnnotatedElement() {
/**
* Return the class that declares the underlying Method or Constructor.
*/
- public Class getDeclaringClass() {
+ public Class> getDeclaringClass() {
return getMember().getDeclaringClass();
}
diff --git a/spring-core/src/main/java/org/springframework/core/env/EnvironmentCapable.java b/spring-core/src/main/java/org/springframework/core/env/EnvironmentCapable.java
index e95528fa3539..7d6ca3875fca 100644
--- a/spring-core/src/main/java/org/springframework/core/env/EnvironmentCapable.java
+++ b/spring-core/src/main/java/org/springframework/core/env/EnvironmentCapable.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -16,9 +16,8 @@
package org.springframework.core.env;
-
/**
- * Interface indicating a component contains and makes available an {@link Environment} object.
+ * Interface indicating a component that contains and exposes an {@link Environment} reference.
*
*
All Spring application contexts are EnvironmentCapable, and the interface is used primarily
* for performing {@code instanceof} checks in framework methods that accept BeanFactory
@@ -29,20 +28,20 @@
* extends EnvironmentCapable, and thus exposes a {@link #getEnvironment()} method; however,
* {@link org.springframework.context.ConfigurableApplicationContext ConfigurableApplicationContext}
* redefines {@link org.springframework.context.ConfigurableApplicationContext#getEnvironment
- * getEnvironment()} and narrows the signature to return a {@link ConfigurableEnvironment}. The effect
- * is that an Environment object is 'read-only' until it accessed from a ConfigurableApplicationContext,
- * at which point it too may be configured.
+ * getEnvironment()} and narrows the signature to return a {@link ConfigurableEnvironment}.
+ * The effect is that an Environment object is 'read-only' until it is being accessed from
+ * a ConfigurableApplicationContext, at which point it too may be configured.
*
* @author Chris Beams
* @since 3.1
* @see Environment
* @see ConfigurableEnvironment
- * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment
+ * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment()
*/
public interface EnvironmentCapable {
/**
- * Return the Environment for this object
+ * Return the {@link Environment} associated with this component.
*/
Environment getEnvironment();
diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java
index 0b89eeec7835..661ba304485f 100644
--- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java
+++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java
@@ -28,7 +28,6 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type;
@@ -122,7 +121,13 @@ public void visit(String attributeName, Object attributeValue) {
newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue);
}
else {
- Object[] newArray = (Object[]) Array.newInstance(newValue.getClass(), 1);
+ Class> arrayClass = newValue.getClass();
+ if(Enum.class.isAssignableFrom(arrayClass)) {
+ while(arrayClass.getSuperclass() != null && !arrayClass.isEnum()) {
+ arrayClass = arrayClass.getSuperclass();
+ }
+ }
+ Object[] newArray = (Object[]) Array.newInstance(arrayClass, 1);
newArray[0] = newValue;
newValue = newArray;
}
diff --git a/spring-core/src/main/java/org/springframework/util/ClassUtils.java b/spring-core/src/main/java/org/springframework/util/ClassUtils.java
index 467b0f7cb6ef..ed84ba02a639 100644
--- a/spring-core/src/main/java/org/springframework/util/ClassUtils.java
+++ b/spring-core/src/main/java/org/springframework/util/ClassUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -17,15 +17,12 @@
package org.springframework.util;
import java.beans.Introspector;
-
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
-
import java.security.AccessControlException;
-
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -578,7 +575,7 @@ public static boolean matchesTypeName(Class> clazz, String typeName) {
/**
* Determine whether the given class has a public constructor with the given signature.
*
Essentially translates {@code NoSuchMethodException} to "false".
- * @param clazz the clazz to analyze
+ * @param clazz the clazz to analyze
* @param paramTypes the parameter types of the method
* @return whether the class has a corresponding constructor
* @see Class#getMethod
@@ -591,7 +588,7 @@ public static boolean hasConstructor(Class> clazz, Class>... paramTypes) {
* Determine whether the given class has a public constructor with the given signature,
* and return it if available (else return {@code null}).
*
Essentially translates {@code NoSuchMethodException} to {@code null}.
- * @param clazz the clazz to analyze
+ * @param clazz the clazz to analyze
* @param paramTypes the parameter types of the method
* @return the constructor, or {@code null} if not found
* @see Class#getConstructor
@@ -607,9 +604,9 @@ public static Constructor getConstructorIfAvailable(Class clazz, Class
}
/**
- * Determine whether the given class has a method with the given signature.
+ * Determine whether the given class has a public method with the given signature.
* Essentially translates {@code NoSuchMethodException} to "false".
- * @param clazz the clazz to analyze
+ * @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
* @return whether the class has a corresponding method
@@ -620,12 +617,15 @@ public static boolean hasMethod(Class> clazz, String methodName, Class>... p
}
/**
- * Determine whether the given class has a method with the given signature,
+ * Determine whether the given class has a public method with the given signature,
* and return it if available (else throws an {@code IllegalStateException}).
+ *
In case of any signature specified, only returns the method if there is a
+ * unique candidate, i.e. a single public method with the specified name.
*
Essentially translates {@code NoSuchMethodException} to {@code IllegalStateException}.
- * @param clazz the clazz to analyze
+ * @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
+ * (may be {@code null} to indicate any signature)
* @return the method (never {@code null})
* @throws IllegalStateException if the method has not been found
* @see Class#getMethod
@@ -633,31 +633,69 @@ public static boolean hasMethod(Class> clazz, String methodName, Class>... p
public static Method getMethod(Class> clazz, String methodName, Class>... paramTypes) {
Assert.notNull(clazz, "Class must not be null");
Assert.notNull(methodName, "Method name must not be null");
- try {
- return clazz.getMethod(methodName, paramTypes);
+ if (paramTypes != null) {
+ try {
+ return clazz.getMethod(methodName, paramTypes);
+ }
+ catch (NoSuchMethodException ex) {
+ throw new IllegalStateException("Expected method not found: " + ex);
+ }
}
- catch (NoSuchMethodException ex) {
- throw new IllegalStateException("Expected method not found: " + ex);
+ else {
+ Set candidates = new HashSet(1);
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ if (methodName.equals(method.getName())) {
+ candidates.add(method);
+ }
+ }
+ if (candidates.size() == 1) {
+ return candidates.iterator().next();
+ }
+ else if (candidates.isEmpty()) {
+ throw new IllegalStateException("Expected method not found: " + clazz + "." + methodName);
+ }
+ else {
+ throw new IllegalStateException("No unique method found: " + clazz + "." + methodName);
+ }
}
}
/**
- * Determine whether the given class has a method with the given signature,
+ * Determine whether the given class has a public method with the given signature,
* and return it if available (else return {@code null}).
+ * In case of any signature specified, only returns the method if there is a
+ * unique candidate, i.e. a single public method with the specified name.
*
Essentially translates {@code NoSuchMethodException} to {@code null}.
- * @param clazz the clazz to analyze
+ * @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
+ * (may be {@code null} to indicate any signature)
* @return the method, or {@code null} if not found
* @see Class#getMethod
*/
public static Method getMethodIfAvailable(Class> clazz, String methodName, Class>... paramTypes) {
Assert.notNull(clazz, "Class must not be null");
Assert.notNull(methodName, "Method name must not be null");
- try {
- return clazz.getMethod(methodName, paramTypes);
+ if (paramTypes != null) {
+ try {
+ return clazz.getMethod(methodName, paramTypes);
+ }
+ catch (NoSuchMethodException ex) {
+ return null;
+ }
}
- catch (NoSuchMethodException ex) {
+ else {
+ Set candidates = new HashSet(1);
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ if (methodName.equals(method.getName())) {
+ candidates.add(method);
+ }
+ }
+ if (candidates.size() == 1) {
+ return candidates.iterator().next();
+ }
return null;
}
}
@@ -1025,7 +1063,7 @@ public static Class>[] toClassArray(Collection> collection) {
* @param instance the instance to analyze for interfaces
* @return all interfaces that the given instance implements as array
*/
- public static Class[] getAllInterfaces(Object instance) {
+ public static Class>[] getAllInterfaces(Object instance) {
Assert.notNull(instance, "Instance must not be null");
return getAllInterfacesForClass(instance.getClass());
}
diff --git a/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java b/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java
index 3da70f602b62..916b322576da 100644
--- a/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java
+++ b/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -48,7 +48,7 @@
* references at any time, so it may appear that an unknown thread is silently removing
* entries.
*
- * If not explicitly specified this implementation will use
+ *
If not explicitly specified, this implementation will use
* {@linkplain SoftReference soft entry references}.
*
* @param The key type
@@ -56,8 +56,7 @@
* @author Phillip Webb
* @since 3.2
*/
-public class ConcurrentReferenceHashMap extends AbstractMap implements
- ConcurrentMap {
+public class ConcurrentReferenceHashMap extends AbstractMap implements ConcurrentMap {
private static final int DEFAULT_INITIAL_CAPACITY = 16;
@@ -82,6 +81,9 @@ public class ConcurrentReferenceHashMap extends AbstractMap implemen
*/
private final float loadFactor;
+ /**
+ * The reference type: SOFT or WEAK.
+ */
private final ReferenceType referenceType;
/**
@@ -99,8 +101,7 @@ public class ConcurrentReferenceHashMap extends AbstractMap implemen
* Create a new {@code ConcurrentReferenceHashMap} instance.
*/
public ConcurrentReferenceHashMap() {
- this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL,
- DEFAULT_REFERENCE_TYPE);
+ this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
}
/**
@@ -108,8 +109,7 @@ public ConcurrentReferenceHashMap() {
* @param initialCapacity the initial capacity of the map
*/
public ConcurrentReferenceHashMap(int initialCapacity) {
- this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL,
- DEFAULT_REFERENCE_TYPE);
+ this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
}
/**
@@ -119,45 +119,44 @@ public ConcurrentReferenceHashMap(int initialCapacity) {
* exceeds this value resize will be attempted
*/
public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor) {
- this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL,
- DEFAULT_REFERENCE_TYPE);
+ this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
* @param initialCapacity the initial capacity of the map
- * @param concurrencyLevel the expected number of threads that will concurrently write
- * to the map
+ * @param concurrencyLevel the expected number of threads that will concurrently
+ * write to the map
*/
public ConcurrentReferenceHashMap(int initialCapacity, int concurrencyLevel) {
- this(initialCapacity, DEFAULT_LOAD_FACTOR, concurrencyLevel,
- DEFAULT_REFERENCE_TYPE);
+ this(initialCapacity, DEFAULT_LOAD_FACTOR, concurrencyLevel, DEFAULT_REFERENCE_TYPE);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
* @param initialCapacity the initial capacity of the map
- * @param loadFactor the load factor. When the average number of references per table
- * exceeds this value resize will be attempted
- * @param concurrencyLevel the expected number of threads that will concurrently write
- * to the map
+ * @param loadFactor the load factor. When the average number of references per
+ * table exceeds this value, resize will be attempted.
+ * @param concurrencyLevel the expected number of threads that will concurrently
+ * write to the map
*/
- public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor,
- int concurrencyLevel) {
+ public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
this(initialCapacity, loadFactor, concurrencyLevel, DEFAULT_REFERENCE_TYPE);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
* @param initialCapacity the initial capacity of the map
- * @param loadFactor the load factor. When the average number of references per table
- * exceeds this value resize will be attempted
- * @param concurrencyLevel the expected number of threads that will concurrently write
- * to the map
+ * @param loadFactor the load factor. When the average number of references per
+ * table exceeds this value, resize will be attempted.
+ * @param concurrencyLevel the expected number of threads that will concurrently
+ * write to the map
* @param referenceType the reference type used for entries
*/
- public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor,
- int concurrencyLevel, ReferenceType referenceType) {
+ @SuppressWarnings("unchecked")
+ public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor, int concurrencyLevel,
+ ReferenceType referenceType) {
+
Assert.isTrue(concurrencyLevel > 0, "ConcurrencyLevel must be positive");
Assert.isTrue(initialCapacity >= 0, "InitialCapacity must not be negative");
Assert.isTrue(loadFactor > 0f, "LoadFactor must be positive");
@@ -167,17 +166,12 @@ public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor,
int size = 1 << this.shift;
this.referenceType = referenceType;
int roundedUpSegmentCapactity = (int) ((initialCapacity + size - 1L) / size);
- this.segments = createSegmentsArray(size);
+ this.segments = (Segment[]) Array.newInstance(Segment.class, size);
for (int i = 0; i < this.segments.length; i++) {
this.segments[i] = new Segment(roundedUpSegmentCapactity);
}
}
- @SuppressWarnings("unchecked")
- private Segment[] createSegmentsArray(int size) {
- return (Segment[]) Array.newInstance(Segment.class, size);
- }
-
protected final float getLoadFactor() {
return this.loadFactor;
@@ -222,7 +216,7 @@ protected int getHash(Object o) {
public V get(Object key) {
Reference reference = getReference(key, Restructure.WHEN_NECESSARY);
Entry entry = (reference == null ? null : reference.get());
- return (entry == null ? null : entry.getValue());
+ return (entry != null ? entry.getValue() : null);
}
@Override
@@ -388,7 +382,7 @@ public static enum ReferenceType {
/**
* Use {@link WeakReference}s.
*/
- WEAK;
+ WEAK
}
@@ -421,14 +415,12 @@ protected final class Segment extends ReentrantLock {
*/
private int resizeThreshold;
-
public Segment(int initialCapacity) {
this.referenceManager = createReferenceManager();
this.initialSize = 1 << calculateShift(initialCapacity, MAXIMUM_SEGMENT_SIZE);
setReferences(createReferenceArray(this.initialSize));
}
-
public Reference getReference(Object key, int hash, Restructure restructure) {
if (restructure == Restructure.WHEN_NECESSARY) {
restructureIfNecessary(false);
@@ -452,17 +444,13 @@ public Reference getReference(Object key, int hash, Restructure restructur
* @return the result of the operation
*/
public T doTask(final int hash, final Object key, final Task task) {
-
boolean resize = task.hasOption(TaskOption.RESIZE);
-
if (task.hasOption(TaskOption.RESTRUCTURE_BEFORE)) {
restructureIfNecessary(resize);
}
-
if (task.hasOption(TaskOption.SKIP_IF_EMPTY) && (this.count == 0)) {
return task.execute(null, null, null);
}
-
lock();
try {
final int index = getIndex(hash, this.references);
@@ -480,7 +468,8 @@ public void add(V value) {
}
};
return task.execute(reference, entry, entries);
- } finally {
+ }
+ finally {
unlock();
if (task.hasOption(TaskOption.RESTRUCTURE_AFTER)) {
restructureIfNecessary(resize);
@@ -569,8 +558,7 @@ private void restructureIfNecessary(boolean allowResize) {
}
}
- private Reference findInChain(Reference reference, Object key,
- int hash) {
+ private Reference findInChain(Reference reference, Object key, int hash) {
while (reference != null) {
if (reference.getHash() == hash) {
Entry entry = reference.get();
@@ -752,6 +740,7 @@ protected T execute(Reference reference, Entry entry) {
* Various options supported by a {@link Task}.
*/
private static enum TaskOption {
+
RESTRUCTURE_BEFORE, RESTRUCTURE_AFTER, SKIP_IF_EMPTY, RESIZE
}
@@ -783,8 +772,7 @@ public Iterator> iterator() {
public boolean contains(Object o) {
if (o != null && o instanceof Map.Entry, ?>) {
Map.Entry, ?> entry = (java.util.Map.Entry, ?>) o;
- Reference reference = ConcurrentReferenceHashMap.this.getReference(
- entry.getKey(), Restructure.NEVER);
+ Reference reference = ConcurrentReferenceHashMap.this.getReference(entry.getKey(), Restructure.NEVER);
Entry other = (reference == null ? null : reference.get());
if (other != null) {
return ObjectUtils.nullSafeEquals(entry.getValue(), other.getValue());
@@ -797,8 +785,7 @@ public boolean contains(Object o) {
public boolean remove(Object o) {
if (o instanceof Map.Entry, ?>) {
Map.Entry, ?> entry = (Map.Entry, ?>) o;
- return ConcurrentReferenceHashMap.this.remove(entry.getKey(),
- entry.getValue());
+ return ConcurrentReferenceHashMap.this.remove(entry.getKey(), entry.getValue());
}
return false;
}
@@ -897,6 +884,7 @@ public void remove() {
* The types of restructuring that can be performed.
*/
protected static enum Restructure {
+
WHEN_NECESSARY, NEVER
}
@@ -916,8 +904,7 @@ protected class ReferenceManager {
* @param next the next reference in the chain or {@code null}
* @return a new {@link Reference}
*/
- public Reference createReference(Entry entry, int hash,
- Reference next) {
+ public Reference createReference(Entry entry, int hash, Reference next) {
if (ConcurrentReferenceHashMap.this.referenceType == ReferenceType.WEAK) {
return new WeakEntryReference(entry, hash, next, this.queue);
}
@@ -941,15 +928,13 @@ public Reference pollForPurge() {
/**
* Internal {@link Reference} implementation for {@link SoftReference}s.
*/
- private static final class SoftEntryReference extends
- SoftReference> implements Reference {
+ private static final class SoftEntryReference extends SoftReference> implements Reference {
private final int hash;
private final Reference nextReference;
- public SoftEntryReference(Entry entry, int hash, Reference next,
- ReferenceQueue> queue) {
+ public SoftEntryReference(Entry entry, int hash, Reference next, ReferenceQueue> queue) {
super(entry, queue);
this.hash = hash;
this.nextReference = next;
@@ -973,15 +958,13 @@ public void release() {
/**
* Internal {@link Reference} implementation for {@link WeakReference}s.
*/
- private static final class WeakEntryReference extends
- WeakReference> implements Reference {
+ private static final class WeakEntryReference extends WeakReference> implements Reference {
private final int hash;
private final Reference nextReference;
- public WeakEntryReference(Entry entry, int hash, Reference next,
- ReferenceQueue> queue) {
+ public WeakEntryReference(Entry entry, int hash, Reference next, ReferenceQueue> queue) {
super(entry, queue);
this.hash = hash;
this.nextReference = next;
@@ -1000,4 +983,5 @@ public void release() {
clear();
}
}
+
}
diff --git a/spring-core/src/main/java/org/springframework/util/MethodInvoker.java b/spring-core/src/main/java/org/springframework/util/MethodInvoker.java
index 565a68c696dd..b1bd04ccb380 100644
--- a/spring-core/src/main/java/org/springframework/util/MethodInvoker.java
+++ b/spring-core/src/main/java/org/springframework/util/MethodInvoker.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -40,7 +40,7 @@
*/
public class MethodInvoker {
- private Class targetClass;
+ private Class> targetClass;
private Object targetObject;
@@ -61,14 +61,14 @@ public class MethodInvoker {
* @see #setTargetObject
* @see #setTargetMethod
*/
- public void setTargetClass(Class targetClass) {
+ public void setTargetClass(Class> targetClass) {
this.targetClass = targetClass;
}
/**
* Return the target class on which to call the target method.
*/
- public Class getTargetClass() {
+ public Class> getTargetClass() {
return this.targetClass;
}
@@ -158,7 +158,7 @@ public void prepare() throws ClassNotFoundException, NoSuchMethodException {
this.targetMethod = methodName;
}
- Class targetClass = getTargetClass();
+ Class> targetClass = getTargetClass();
String targetMethod = getTargetMethod();
if (targetClass == null) {
throw new IllegalArgumentException("Either 'targetClass' or 'targetObject' is required");
@@ -194,7 +194,7 @@ public void prepare() throws ClassNotFoundException, NoSuchMethodException {
* @return the resolved Class
* @throws ClassNotFoundException if the class name was invalid
*/
- protected Class resolveClassName(String className) throws ClassNotFoundException {
+ protected Class> resolveClassName(String className) throws ClassNotFoundException {
return ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
}
@@ -287,19 +287,22 @@ public Object invoke() throws InvocationTargetException, IllegalAccessException
* Therefore, with an arg of type Integer, a constructor (Integer) would be preferred to a
* constructor (Number) which would in turn be preferred to a constructor (Object).
* All argument weights get accumulated.
+ * Note: This is the algorithm used by MethodInvoker itself and also the algorithm
+ * used for constructor and factory method selection in Spring's bean container (in case
+ * of lenient constructor resolution which is the default for regular bean definitions).
* @param paramTypes the parameter types to match
* @param args the arguments to match
* @return the accumulated weight for all arguments
*/
- public static int getTypeDifferenceWeight(Class[] paramTypes, Object[] args) {
+ public static int getTypeDifferenceWeight(Class>[] paramTypes, Object[] args) {
int result = 0;
for (int i = 0; i < paramTypes.length; i++) {
if (!ClassUtils.isAssignableValue(paramTypes[i], args[i])) {
return Integer.MAX_VALUE;
}
if (args[i] != null) {
- Class paramType = paramTypes[i];
- Class superClass = args[i].getClass().getSuperclass();
+ Class> paramType = paramTypes[i];
+ Class> superClass = args[i].getClass().getSuperclass();
while (superClass != null) {
if (paramType.equals(superClass)) {
result = result + 2;
diff --git a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
index 87ba4211412e..f2455db1589e 100644
--- a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
+++ b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -98,8 +98,8 @@ public static void setField(Field field, Object target, Object value) {
}
catch (IllegalAccessException ex) {
handleReflectionException(ex);
- throw new IllegalStateException("Unexpected reflection exception - " + ex.getClass().getName() + ": "
- + ex.getMessage());
+ throw new IllegalStateException(
+ "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage());
}
}
@@ -153,8 +153,8 @@ public static Method findMethod(Class> clazz, String name, Class>... paramTy
while (searchType != null) {
Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods());
for (Method method : methods) {
- if (name.equals(method.getName())
- && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
+ if (name.equals(method.getName()) &&
+ (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
return method;
}
}
diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxEventXMLReader.java b/spring-core/src/main/java/org/springframework/util/xml/StaxEventXMLReader.java
index 0d32a3fc08c5..fd43354a68b2 100644
--- a/spring-core/src/main/java/org/springframework/util/xml/StaxEventXMLReader.java
+++ b/spring-core/src/main/java/org/springframework/util/xml/StaxEventXMLReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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,12 +51,12 @@
* an {@code XMLEventReader}, and calls the corresponding methods on the SAX callback interfaces.
*
* @author Arjen Poutsma
+ * @since 3.0
* @see XMLEventReader
* @see #setContentHandler(org.xml.sax.ContentHandler)
* @see #setDTDHandler(org.xml.sax.DTDHandler)
* @see #setEntityResolver(org.xml.sax.EntityResolver)
* @see #setErrorHandler(org.xml.sax.ErrorHandler)
- * @since 3.0
*/
class StaxEventXMLReader extends AbstractStaxXMLReader {
@@ -70,11 +70,11 @@ class StaxEventXMLReader extends AbstractStaxXMLReader {
private String encoding;
+
/**
* Constructs a new instance of the {@code StaxEventXmlReader} that reads from the given
* {@code XMLEventReader}. The supplied event reader must be in {@code XMLStreamConstants.START_DOCUMENT} or
* {@code XMLStreamConstants.START_ELEMENT} state.
- *
* @param reader the {@code XMLEventReader} to read from
* @throws IllegalStateException if the reader is not at the start of a document or element
*/
@@ -89,17 +89,17 @@ class StaxEventXMLReader extends AbstractStaxXMLReader {
catch (XMLStreamException ex) {
throw new IllegalStateException("Could not read first element: " + ex.getMessage());
}
-
this.reader = reader;
}
+
@Override
protected void parseInternal() throws SAXException, XMLStreamException {
boolean documentStarted = false;
boolean documentEnded = false;
int elementDepth = 0;
- while (reader.hasNext() && elementDepth >= 0) {
- XMLEvent event = reader.nextEvent();
+ while (this.reader.hasNext() && elementDepth >= 0) {
+ XMLEvent event = this.reader.nextEvent();
if (!event.isStartDocument() && !event.isEndDocument() && !documentStarted) {
handleStartDocument(event);
documentStarted = true;
@@ -165,36 +165,28 @@ private void handleStartDocument(final XMLEvent event) throws SAXException {
this.encoding = startDocument.getCharacterEncodingScheme();
}
}
-
if (getContentHandler() != null) {
final Location location = event.getLocation();
getContentHandler().setDocumentLocator(new Locator2() {
-
public int getColumnNumber() {
- return location != null ? location.getColumnNumber() : -1;
+ return (location != null ? location.getColumnNumber() : -1);
}
-
public int getLineNumber() {
- return location != null ? location.getLineNumber() : -1;
+ return (location != null ? location.getLineNumber() : -1);
}
-
public String getPublicId() {
- return location != null ? location.getPublicId() : null;
+ return (location != null ? location.getPublicId() : null);
}
-
public String getSystemId() {
- return location != null ? location.getSystemId() : null;
+ return (location != null ? location.getSystemId() : null);
}
-
public String getXMLVersion() {
return xmlVersion;
}
-
public String getEncoding() {
return encoding;
}
});
-
getContentHandler().startDocument();
}
}
@@ -311,7 +303,6 @@ private void handleEntityReference(EntityReference reference) throws SAXExceptio
private Attributes getAttributes(StartElement event) {
AttributesImpl attributes = new AttributesImpl();
-
for (Iterator i = event.getAttributes(); i.hasNext();) {
Attribute attribute = (Attribute) i.next();
QName qName = attribute.getName();
@@ -323,8 +314,7 @@ private Attributes getAttributes(StartElement event) {
if (type == null) {
type = "CDATA";
}
- attributes
- .addAttribute(namespace, qName.getLocalPart(), toQualifiedName(qName), type, attribute.getValue());
+ attributes.addAttribute(namespace, qName.getLocalPart(), toQualifiedName(qName), type, attribute.getValue());
}
if (hasNamespacePrefixesFeature()) {
for (Iterator i = event.getNamespaces(); i.hasNext();) {
diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxStreamXMLReader.java b/spring-core/src/main/java/org/springframework/util/xml/StaxStreamXMLReader.java
index bca48d1cdbe6..26394c56a678 100644
--- a/spring-core/src/main/java/org/springframework/util/xml/StaxStreamXMLReader.java
+++ b/spring-core/src/main/java/org/springframework/util/xml/StaxStreamXMLReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -31,16 +31,16 @@
import org.springframework.util.StringUtils;
/**
- * SAX {@code XMLReader} that reads from a StAX {@code XMLStreamReader}. Reads from an
+ * SAX {@code XMLReader} that reads from a StAX {@code XMLStreamReader}. Reads from an
* {@code XMLStreamReader}, and calls the corresponding methods on the SAX callback interfaces.
*
* @author Arjen Poutsma
+ * @since 3.0
* @see XMLStreamReader
* @see #setContentHandler(org.xml.sax.ContentHandler)
* @see #setDTDHandler(org.xml.sax.DTDHandler)
* @see #setEntityResolver(org.xml.sax.EntityResolver)
* @see #setErrorHandler(org.xml.sax.ErrorHandler)
- * @since 3.0
*/
class StaxStreamXMLReader extends AbstractStaxXMLReader {
@@ -52,11 +52,11 @@ class StaxStreamXMLReader extends AbstractStaxXMLReader {
private String encoding;
+
/**
- * Constructs a new instance of the {@code StaxStreamXmlReader} that reads from the given
- * {@code XMLStreamReader}. The supplied stream reader must be in {@code XMLStreamConstants.START_DOCUMENT}
+ * Construct a new instance of the {@code StaxStreamXmlReader} that reads from the given
+ * {@code XMLStreamReader}. The supplied stream reader must be in {@code XMLStreamConstants.START_DOCUMENT}
* or {@code XMLStreamConstants.START_ELEMENT} state.
- *
* @param reader the {@code XMLEventReader} to read from
* @throws IllegalStateException if the reader is not at the start of a document or element
*/
@@ -69,12 +69,13 @@ class StaxStreamXMLReader extends AbstractStaxXMLReader {
this.reader = reader;
}
+
@Override
protected void parseInternal() throws SAXException, XMLStreamException {
boolean documentStarted = false;
boolean documentEnded = false;
int elementDepth = 0;
- int eventType = reader.getEventType();
+ int eventType = this.reader.getEventType();
while (true) {
if (eventType != XMLStreamConstants.START_DOCUMENT && eventType != XMLStreamConstants.END_DOCUMENT &&
!documentStarted) {
@@ -118,8 +119,8 @@ protected void parseInternal() throws SAXException, XMLStreamException {
handleEntityReference();
break;
}
- if (reader.hasNext() && elementDepth >= 0) {
- eventType = reader.next();
+ if (this.reader.hasNext() && elementDepth >= 0) {
+ eventType = this.reader.next();
}
else {
break;
@@ -131,66 +132,58 @@ protected void parseInternal() throws SAXException, XMLStreamException {
}
private void handleStartDocument() throws SAXException {
- if (XMLStreamConstants.START_DOCUMENT == reader.getEventType()) {
- String xmlVersion = reader.getVersion();
+ if (XMLStreamConstants.START_DOCUMENT == this.reader.getEventType()) {
+ String xmlVersion = this.reader.getVersion();
if (StringUtils.hasLength(xmlVersion)) {
this.xmlVersion = xmlVersion;
}
- this.encoding = reader.getCharacterEncodingScheme();
+ this.encoding = this.reader.getCharacterEncodingScheme();
}
-
if (getContentHandler() != null) {
- final Location location = reader.getLocation();
-
+ final Location location = this.reader.getLocation();
getContentHandler().setDocumentLocator(new Locator2() {
-
public int getColumnNumber() {
- return location != null ? location.getColumnNumber() : -1;
+ return (location != null ? location.getColumnNumber() : -1);
}
-
public int getLineNumber() {
- return location != null ? location.getLineNumber() : -1;
+ return (location != null ? location.getLineNumber() : -1);
}
-
public String getPublicId() {
- return location != null ? location.getPublicId() : null;
+ return (location != null ? location.getPublicId() : null);
}
-
public String getSystemId() {
- return location != null ? location.getSystemId() : null;
+ return (location != null ? location.getSystemId() : null);
}
-
public String getXMLVersion() {
return xmlVersion;
}
-
public String getEncoding() {
return encoding;
}
});
getContentHandler().startDocument();
- if (reader.standaloneSet()) {
- setStandalone(reader.isStandalone());
+ if (this.reader.standaloneSet()) {
+ setStandalone(this.reader.isStandalone());
}
}
}
private void handleStartElement() throws SAXException {
if (getContentHandler() != null) {
- QName qName = reader.getName();
+ QName qName = this.reader.getName();
if (hasNamespacesFeature()) {
- for (int i = 0; i < reader.getNamespaceCount(); i++) {
- startPrefixMapping(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
+ for (int i = 0; i < this.reader.getNamespaceCount(); i++) {
+ startPrefixMapping(this.reader.getNamespacePrefix(i), this.reader.getNamespaceURI(i));
}
- for (int i = 0; i < reader.getAttributeCount(); i++) {
- String prefix = reader.getAttributePrefix(i);
- String namespace = reader.getAttributeNamespace(i);
+ for (int i = 0; i < this.reader.getAttributeCount(); i++) {
+ String prefix = this.reader.getAttributePrefix(i);
+ String namespace = this.reader.getAttributeNamespace(i);
if (StringUtils.hasLength(namespace)) {
startPrefixMapping(prefix, namespace);
}
}
- getContentHandler().startElement(qName.getNamespaceURI(), qName.getLocalPart(), toQualifiedName(qName),
- getAttributes());
+ getContentHandler().startElement(qName.getNamespaceURI(), qName.getLocalPart(),
+ toQualifiedName(qName), getAttributes());
}
else {
getContentHandler().startElement("", "", toQualifiedName(qName), getAttributes());
@@ -200,11 +193,11 @@ private void handleStartElement() throws SAXException {
private void handleEndElement() throws SAXException {
if (getContentHandler() != null) {
- QName qName = reader.getName();
+ QName qName = this.reader.getName();
if (hasNamespacesFeature()) {
getContentHandler().endElement(qName.getNamespaceURI(), qName.getLocalPart(), toQualifiedName(qName));
- for (int i = 0; i < reader.getNamespaceCount(); i++) {
- String prefix = reader.getNamespacePrefix(i);
+ for (int i = 0; i < this.reader.getNamespaceCount(); i++) {
+ String prefix = this.reader.getNamespacePrefix(i);
if (prefix == null) {
prefix = "";
}
@@ -218,31 +211,33 @@ private void handleEndElement() throws SAXException {
}
private void handleCharacters() throws SAXException {
- if (getContentHandler() != null && reader.isWhiteSpace()) {
- getContentHandler()
- .ignorableWhitespace(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
+ if (getContentHandler() != null && this.reader.isWhiteSpace()) {
+ getContentHandler().ignorableWhitespace(this.reader.getTextCharacters(),
+ this.reader.getTextStart(), this.reader.getTextLength());
return;
}
- if (XMLStreamConstants.CDATA == reader.getEventType() && getLexicalHandler() != null) {
+ if (XMLStreamConstants.CDATA == this.reader.getEventType() && getLexicalHandler() != null) {
getLexicalHandler().startCDATA();
}
if (getContentHandler() != null) {
- getContentHandler().characters(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
+ getContentHandler().characters(this.reader.getTextCharacters(),
+ this.reader.getTextStart(), this.reader.getTextLength());
}
- if (XMLStreamConstants.CDATA == reader.getEventType() && getLexicalHandler() != null) {
+ if (XMLStreamConstants.CDATA == this.reader.getEventType() && getLexicalHandler() != null) {
getLexicalHandler().endCDATA();
}
}
private void handleComment() throws SAXException {
if (getLexicalHandler() != null) {
- getLexicalHandler().comment(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
+ getLexicalHandler().comment(this.reader.getTextCharacters(),
+ this.reader.getTextStart(), this.reader.getTextLength());
}
}
private void handleDtd() throws SAXException {
if (getLexicalHandler() != null) {
- javax.xml.stream.Location location = reader.getLocation();
+ javax.xml.stream.Location location = this.reader.getLocation();
getLexicalHandler().startDTD(null, location.getPublicId(), location.getSystemId());
}
if (getLexicalHandler() != null) {
@@ -252,10 +247,10 @@ private void handleDtd() throws SAXException {
private void handleEntityReference() throws SAXException {
if (getLexicalHandler() != null) {
- getLexicalHandler().startEntity(reader.getLocalName());
+ getLexicalHandler().startEntity(this.reader.getLocalName());
}
if (getLexicalHandler() != null) {
- getLexicalHandler().endEntity(reader.getLocalName());
+ getLexicalHandler().endEntity(this.reader.getLocalName());
}
}
@@ -267,29 +262,28 @@ private void handleEndDocument() throws SAXException {
private void handleProcessingInstruction() throws SAXException {
if (getContentHandler() != null) {
- getContentHandler().processingInstruction(reader.getPITarget(), reader.getPIData());
+ getContentHandler().processingInstruction(this.reader.getPITarget(), this.reader.getPIData());
}
}
private Attributes getAttributes() {
AttributesImpl attributes = new AttributesImpl();
-
- for (int i = 0; i < reader.getAttributeCount(); i++) {
- String namespace = reader.getAttributeNamespace(i);
+ for (int i = 0; i < this.reader.getAttributeCount(); i++) {
+ String namespace = this.reader.getAttributeNamespace(i);
if (namespace == null || !hasNamespacesFeature()) {
namespace = "";
}
- String type = reader.getAttributeType(i);
+ String type = this.reader.getAttributeType(i);
if (type == null) {
type = "CDATA";
}
- attributes.addAttribute(namespace, reader.getAttributeLocalName(i),
- toQualifiedName(reader.getAttributeName(i)), type, reader.getAttributeValue(i));
+ attributes.addAttribute(namespace, this.reader.getAttributeLocalName(i),
+ toQualifiedName(this.reader.getAttributeName(i)), type, this.reader.getAttributeValue(i));
}
if (hasNamespacePrefixesFeature()) {
- for (int i = 0; i < reader.getNamespaceCount(); i++) {
- String prefix = reader.getNamespacePrefix(i);
- String namespaceUri = reader.getNamespaceURI(i);
+ for (int i = 0; i < this.reader.getNamespaceCount(); i++) {
+ String prefix = this.reader.getNamespacePrefix(i);
+ String namespaceUri = this.reader.getNamespaceURI(i);
String qName;
if (StringUtils.hasLength(prefix)) {
qName = "xmlns:" + prefix;
diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxUtils.java b/spring-core/src/main/java/org/springframework/util/xml/StaxUtils.java
index efafa259d43c..41fd3f63dc91 100644
--- a/spring-core/src/main/java/org/springframework/util/xml/StaxUtils.java
+++ b/spring-core/src/main/java/org/springframework/util/xml/StaxUtils.java
@@ -111,7 +111,16 @@ public static Source createStaxSource(XMLEventReader eventReader) throws XMLStre
* 1.4 {@link StAXSource}; {@code false} otherwise.
*/
public static boolean isStaxSource(Source source) {
- return (source instanceof StaxSource || (jaxp14Available && Jaxp14StaxHandler.isStaxSource(source)));
+ return ((source instanceof StaxSource) || (jaxp14Available && Jaxp14StaxHandler.isStaxSource(source)));
+ }
+
+ /**
+ * Indicate whether the given class is a StAX Source class.
+ * @return {@code true} if {@code source} is a custom StAX source or JAXP
+ * 1.4 {@link StAXSource} class; {@code false} otherwise.
+ */
+ public static boolean isStaxSourceClass(Class extends Source> clazz) {
+ return (StaxSource.class.equals(clazz) || (jaxp14Available && Jaxp14StaxHandler.isStaxSourceClass(clazz)));
}
@@ -348,6 +357,10 @@ private static boolean isStaxSource(Source source) {
return (source instanceof StAXSource);
}
+ private static boolean isStaxSourceClass(Class extends Source> clazz) {
+ return StAXSource.class.equals(clazz);
+ }
+
private static boolean isStaxResult(Result result) {
return (result instanceof StAXResult);
}
diff --git a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
index 5bdc32d9a2ca..ca335500027c 100644
--- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
+++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -19,15 +19,15 @@
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
-
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.junit.Test;
+import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
-
import static org.springframework.core.GenericTypeResolver.*;
import static org.springframework.util.ReflectionUtils.*;
@@ -65,92 +65,110 @@ public void nullIfNotResolvable() {
@Test
public void methodReturnTypes() {
- assertEquals(Integer.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
- assertEquals(String.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
+ assertEquals(Integer.class,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
+ assertEquals(String.class,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "raw"), MyInterfaceType.class));
- assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
+ assertEquals(null,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
}
- /**
- * @since 3.2
- */
@Test
- public void genericMethodReturnTypes() {
-
- Method notParameterized = findMethod(MyTypeWithMethods.class, "notParameterized", new Class[] {});
- assertEquals(String.class, resolveReturnTypeForGenericMethod(notParameterized, new Object[] {}));
-
- Method notParameterizedWithArguments = findMethod(MyTypeWithMethods.class, "notParameterizedWithArguments",
- new Class[] { Integer.class, Boolean.class });
- assertEquals(String.class,
- resolveReturnTypeForGenericMethod(notParameterizedWithArguments, new Object[] { 99, true }));
+ public void testResolveType() {
+ Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
+ MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
+ assertEquals(MyInterfaceType.class,
+ resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap()));
+
+ Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage",
+ MyInterfaceType[].class);
+ MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
+ assertEquals(MyInterfaceType[].class,
+ resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap()));
+
+ Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage",
+ Object[].class);
+ MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
+ Map varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
+ assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
+ }
- Method createProxy = findMethod(MyTypeWithMethods.class, "createProxy", new Class[] { Object.class });
- assertEquals(String.class, resolveReturnTypeForGenericMethod(createProxy, new Object[] { "foo" }));
+ @Test
+ public void testBoundParameterizedType() {
+ assertEquals(B.class, resolveTypeArgument(TestImpl.class, ITest.class));
+ }
- Method createNamedProxyWithDifferentTypes = findMethod(MyTypeWithMethods.class, "createNamedProxy",
- new Class[] { String.class, Object.class });
- // one argument to few
- assertNull(resolveReturnTypeForGenericMethod(createNamedProxyWithDifferentTypes, new Object[] { "enigma" }));
- assertEquals(Long.class,
- resolveReturnTypeForGenericMethod(createNamedProxyWithDifferentTypes, new Object[] { "enigma", 99L }));
+ @Test
+ public void testGetTypeVariableMap() throws Exception {
+ Map map;
+
+ map = GenericTypeResolver.getTypeVariableMap(MySimpleInterfaceType.class);
+ assertThat(map.toString(), equalTo("{T=class java.lang.String}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(MyCollectionInterfaceType.class);
+ assertThat(map.toString(), equalTo("{T=java.util.Collection}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(MyCollectionSuperclassType.class);
+ assertThat(map.toString(), equalTo("{T=java.util.Collection}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(MySimpleTypeWithMethods.class);
+ assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(TopLevelClass.class);
+ assertThat(map.toString(), equalTo("{}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.class);
+ assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.TypedNested.class);
+ assertThat(map.size(), equalTo(2));
+ Type t = null;
+ Type x = null;
+ for (Map.Entry entry : map.entrySet()) {
+ if(entry.getKey().toString().equals("T")) {
+ t = entry.getValue();
+ }
+ else {
+ x = entry.getValue();
+ }
+ }
+ assertThat(t, equalTo((Type) Integer.class));
+ assertThat(x, equalTo((Type) Long.class));
+ }
- Method createNamedProxyWithDuplicateTypes = findMethod(MyTypeWithMethods.class, "createNamedProxy",
- new Class[] { String.class, Object.class });
- assertEquals(String.class,
- resolveReturnTypeForGenericMethod(createNamedProxyWithDuplicateTypes, new Object[] { "enigma", "foo" }));
-
- Method createMock = findMethod(MyTypeWithMethods.class, "createMock", new Class[] { Class.class });
- assertEquals(Runnable.class, resolveReturnTypeForGenericMethod(createMock, new Object[] { Runnable.class }));
-
- Method createNamedMock = findMethod(MyTypeWithMethods.class, "createNamedMock", new Class[] { String.class,
- Class.class });
- assertEquals(Runnable.class,
- resolveReturnTypeForGenericMethod(createNamedMock, new Object[] { "foo", Runnable.class }));
-
- Method createVMock = findMethod(MyTypeWithMethods.class, "createVMock",
- new Class[] { Object.class, Class.class });
- assertEquals(Runnable.class,
- resolveReturnTypeForGenericMethod(createVMock, new Object[] { "foo", Runnable.class }));
-
- // Ideally we would expect String.class instead of Object.class, but
- // resolveReturnTypeForGenericMethod() does not currently support this form of
- // look-up.
- Method extractValueFrom = findMethod(MyTypeWithMethods.class, "extractValueFrom",
- new Class[] { MyInterfaceType.class });
- assertEquals(Object.class,
- resolveReturnTypeForGenericMethod(extractValueFrom, new Object[] { new MySimpleInterfaceType() }));
-
- // Ideally we would expect Boolean.class instead of Object.class, but this
- // information is not available at run-time due to type erasure.
- Map map = new HashMap();
- map.put(0, false);
- map.put(1, true);
- Method extractMagicValue = findMethod(MyTypeWithMethods.class, "extractMagicValue", new Class[] { Map.class });
- assertEquals(Object.class, resolveReturnTypeForGenericMethod(extractMagicValue, new Object[] { map }));
- }
-
- /**
- * @since 3.2
- */
@Test
- public void testResolveType() {
- Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
- MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
- assertEquals(MyInterfaceType.class,
- resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap()));
+ public void getGenericsCannotBeResolved() throws Exception {
+ // SPR-11030
+ Class>[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class);
+ // Note: to be changed to return null in Spring 4.0
+ assertThat(resolved, equalTo(new Class[] {Object.class}));
+ }
- Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage", MyInterfaceType[].class);
- MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
- assertEquals(MyInterfaceType[].class,
- resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap()));
+ @Test
+ public void getRawMapTypeCannotBeResolved() throws Exception {
+ // SPR-11052
+ Class>[] resolved = GenericTypeResolver.resolveTypeArguments(Map.class, Map.class);
+ assertNull(resolved);
+ }
- Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage", Object[].class);
- MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
- Map varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
- assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
+ @Test
+ public void getGenericsOnArrayFromParamCannotBeResolved() throws Exception {
+ // SPR-11044
+ MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(
+ WithArrayBase.class.getDeclaredMethod("array", Object[].class), 0);
+ Class> resolved = GenericTypeResolver.resolveParameterType(methodParameter, WithArray.class);
+ assertThat(resolved, equalTo((Class) Object[].class));
}
+ @Test
+ public void getGenericsOnArrayFromReturnCannotBeResolved() throws Exception {
+ // SPR-11044
+ Class> resolved = GenericTypeResolver.resolveReturnType(
+ WithArrayBase.class.getDeclaredMethod("array", Object[].class),
+ WithArray.class);
+ assertThat(resolved, equalTo((Class) Object[].class));
+ }
public interface MyInterfaceType {
}
@@ -171,26 +189,44 @@ public class MyCollectionSuperclassType extends MySuperclassType {
- public MyInterfaceType integer() { return null; }
- public MySimpleInterfaceType string() { return null; }
- public Object object() { return null; }
+
+ public MyInterfaceType integer() {
+ return null;
+ }
+
+ public MySimpleInterfaceType string() {
+ return null;
+ }
+
+ public Object object() {
+ return null;
+ }
+
@SuppressWarnings("rawtypes")
- public MyInterfaceType raw() { return null; }
- public String notParameterized() { return null; }
- public String notParameterizedWithArguments(Integer x, Boolean b) { return null; }
+ public MyInterfaceType raw() {
+ return null;
+ }
+
+ public String notParameterized() {
+ return null;
+ }
+
+ public String notParameterizedWithArguments(Integer x, Boolean b) {
+ return null;
+ }
/**
- * Simulates a factory method that wraps the supplied object in a proxy
- * of the same type.
+ * Simulates a factory method that wraps the supplied object in a proxy of the
+ * same type.
*/
public static T createProxy(T object) {
return null;
}
/**
- * Similar to {@link #createProxy(Object)} but adds an additional argument
- * before the argument of type {@code T}. Note that they may potentially
- * be of the same time when invoked!
+ * Similar to {@link #createProxy(Object)} but adds an additional argument before
+ * the argument of type {@code T}. Note that they may potentially be of the same
+ * time when invoked!
*/
public static T createNamedProxy(String name, T object) {
return null;
@@ -204,8 +240,8 @@ public static MOCK createMock(Class toMock) {
}
/**
- * Similar to {@link #createMock(Class)} but adds an additional method
- * argument before the parameterized argument.
+ * Similar to {@link #createMock(Class)} but adds an additional method argument
+ * before the parameterized argument.
*/
public static T createNamedMock(String name, Class toMock) {
return null;
@@ -220,8 +256,8 @@ public static T createVMock(V name, Class toMock) {
}
/**
- * Extract some value of the type supported by the interface (i.e., by
- * a concrete, non-generic implementation of the interface).
+ * Extract some value of the type supported by the interface (i.e., by a concrete,
+ * non-generic implementation of the interface).
*/
public static T extractValueFrom(MyInterfaceType myInterfaceType) {
return null;
@@ -250,4 +286,31 @@ public static class MySimpleTypeWithMethods extends MyTypeWithMethods {
static class GenericClass {
}
+ class A{}
+
+ class B{}
+
+ class ITest{}
+
+ class TestImpl> extends ITest{
+ }
+
+ static class TopLevelClass {
+ class Nested {
+ }
+ }
+
+ static class TypedTopLevelClass extends TopLevelClass {
+ class TypedNested extends Nested {
+ }
+ }
+
+ static abstract class WithArrayBase {
+
+ public abstract T[] array(T... args);
+ }
+
+ static abstract class WithArray extends WithArrayBase {
+ }
+
}
diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java
index c08cdeebb775..07aa882307a4 100644
--- a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java
+++ b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -49,7 +49,7 @@
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
-import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
@@ -81,14 +81,14 @@ public void canConvertIllegalArgumentNullTargetType() {
try {
assertFalse(conversionService.canConvert(String.class, null));
fail("Should have failed");
- } catch (IllegalArgumentException e) {
-
+ }
+ catch (IllegalArgumentException ex) {
}
try {
assertFalse(conversionService.canConvert(TypeDescriptor.valueOf(String.class), null));
fail("Should have failed");
- } catch (IllegalArgumentException e) {
-
+ }
+ catch (IllegalArgumentException ex) {
}
}
@@ -151,8 +151,8 @@ public Object convert(Object source) {
}
});
fail("Should have failed");
- } catch (IllegalArgumentException e) {
-
+ }
+ catch (IllegalArgumentException ex) {
}
}
@@ -254,7 +254,8 @@ public void genericConverterDelegatingBackToConversionServiceConverterNotFound()
try {
conversionService.convert("3,4,5", Integer[].class);
fail("should have failed");
- } catch (ConverterNotFoundException e) {
+ }
+ catch (ConverterNotFoundException ex) {
}
}
@@ -389,7 +390,7 @@ public void testIgnoreCopyConstructor() {
}
@Test
- public void testConvertUUID() throws Exception {
+ public void testConvertUUID() {
GenericConversionService service = new DefaultConversionService();
UUID uuid = UUID.randomUUID();
String convertToString = service.convert(uuid, String.class);
@@ -472,7 +473,7 @@ public void testPerformance3() throws Exception {
public static Map map;
@Test
- public void emptyListToArray() throws Exception {
+ public void emptyListToArray() {
conversionService.addConverter(new CollectionToArrayConverter(conversionService));
conversionService.addConverterFactory(new StringToNumberConverterFactory());
List list = new ArrayList();
@@ -483,7 +484,7 @@ public void emptyListToArray() throws Exception {
}
@Test
- public void emptyListToObject() throws Exception {
+ public void emptyListToObject() {
conversionService.addConverter(new CollectionToObjectConverter(conversionService));
conversionService.addConverterFactory(new StringToNumberConverterFactory());
List list = new ArrayList();
@@ -590,14 +591,14 @@ public void stringToCollectionCanConvert() throws Exception {
public Collection stringToCollection;
@Test
- public void testConvertiblePairsInSet() throws Exception {
+ public void testConvertiblePairsInSet() {
Set set = new HashSet();
set.add(new GenericConverter.ConvertiblePair(Number.class, String.class));
assert set.contains(new GenericConverter.ConvertiblePair(Number.class, String.class));
}
@Test
- public void testConvertiblePairEqualsAndHash() throws Exception {
+ public void testConvertiblePairEqualsAndHash() {
GenericConverter.ConvertiblePair pair = new GenericConverter.ConvertiblePair(Number.class, String.class);
GenericConverter.ConvertiblePair pairEqual = new GenericConverter.ConvertiblePair(Number.class, String.class);
assertEquals(pair, pairEqual);
@@ -605,7 +606,7 @@ public void testConvertiblePairEqualsAndHash() throws Exception {
}
@Test
- public void testConvertiblePairDifferentEqualsAndHash() throws Exception {
+ public void testConvertiblePairDifferentEqualsAndHash() {
GenericConverter.ConvertiblePair pair = new GenericConverter.ConvertiblePair(Number.class, String.class);
GenericConverter.ConvertiblePair pairOpposite = new GenericConverter.ConvertiblePair(String.class, Number.class);
assertFalse(pair.equals(pairOpposite));
@@ -613,7 +614,7 @@ public void testConvertiblePairDifferentEqualsAndHash() throws Exception {
}
@Test
- public void convertPrimitiveArray() throws Exception {
+ public void convertPrimitiveArray() {
GenericConversionService conversionService = new DefaultConversionService();
byte[] byteArray = new byte[] { 1, 2, 3 };
Byte[] converted = conversionService.convert(byteArray, Byte[].class);
@@ -625,7 +626,8 @@ public void canConvertIllegalArgumentNullTargetTypeFromClass() {
try {
conversionService.canConvert(String.class, null);
fail("Did not thow IllegalArgumentException");
- } catch(IllegalArgumentException e) {
+ }
+ catch (IllegalArgumentException ex) {
}
}
@@ -634,13 +636,14 @@ public void canConvertIllegalArgumentNullTargetTypeFromTypeDescriptor() {
try {
conversionService.canConvert(TypeDescriptor.valueOf(String.class), null);
fail("Did not thow IllegalArgumentException");
- } catch(IllegalArgumentException e) {
+ }
+ catch(IllegalArgumentException ex) {
}
}
@Test
@SuppressWarnings({ "rawtypes" })
- public void convertHashMapValuesToList() throws Exception {
+ public void convertHashMapValuesToList() {
GenericConversionService conversionService = new DefaultConversionService();
Map hashMap = new LinkedHashMap();
hashMap.put("1", 1);
@@ -650,7 +653,7 @@ public void convertHashMapValuesToList() throws Exception {
}
@Test
- public void removeConvertible() throws Exception {
+ public void removeConvertible() {
conversionService.addConverter(new ColorConverter());
assertTrue(conversionService.canConvert(String.class, Color.class));
conversionService.removeConvertible(String.class, Color.class);
@@ -658,7 +661,7 @@ public void removeConvertible() throws Exception {
}
@Test
- public void conditionalConverter() throws Exception {
+ public void conditionalConverter() {
GenericConversionService conversionService = new GenericConversionService();
MyConditionalConverter converter = new MyConditionalConverter();
conversionService.addConverter(new ColorConverter());
@@ -668,7 +671,7 @@ public void conditionalConverter() throws Exception {
}
@Test
- public void conditionalConverterFactory() throws Exception {
+ public void conditionalConverterFactory() {
GenericConversionService conversionService = new GenericConversionService();
MyConditionalConverterFactory converter = new MyConditionalConverterFactory();
conversionService.addConverter(new ColorConverter());
@@ -679,32 +682,29 @@ public void conditionalConverterFactory() throws Exception {
}
@Test
- public void shouldNotSuportNullConvertibleTypesFromNonConditionalGenericConverter()
- throws Exception {
+ public void shouldNotSuportNullConvertibleTypesFromNonConditionalGenericConverter() {
GenericConversionService conversionService = new GenericConversionService();
GenericConverter converter = new GenericConverter() {
-
@Override
public Set getConvertibleTypes() {
return null;
}
-
@Override
- public Object convert(Object source, TypeDescriptor sourceType,
- TypeDescriptor targetType) {
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return null;
}
};
try {
conversionService.addConverter(converter);
fail("Did not throw");
- } catch (IllegalStateException e) {
- assertEquals("Only conditional converters may return null convertible types", e.getMessage());
+ }
+ catch (IllegalStateException ex) {
+ assertEquals("Only conditional converters may return null convertible types", ex.getMessage());
}
}
@Test
- public void conditionalConversionForAllTypes() throws Exception {
+ public void conditionalConversionForAllTypes() {
GenericConversionService conversionService = new GenericConversionService();
MyConditionalGenericConverter converter = new MyConditionalGenericConverter();
conversionService.addConverter(converter);
@@ -717,7 +717,7 @@ public void conditionalConversionForAllTypes() throws Exception {
}
@Test
- public void convertOptimizeArray() throws Exception {
+ public void convertOptimizeArray() {
// SPR-9566
GenericConversionService conversionService = new DefaultConversionService();
byte[] byteArray = new byte[] { 1, 2, 3 };
@@ -726,7 +726,7 @@ public void convertOptimizeArray() throws Exception {
}
@Test
- public void convertCannotOptimizeArray() throws Exception {
+ public void convertCannotOptimizeArray() {
GenericConversionService conversionService = new GenericConversionService();
conversionService.addConverter(new Converter() {
@Override
@@ -766,6 +766,7 @@ public void convertNullAnnotatedStringToString() throws Exception {
conversionService.convert(source, sourceType, targetType);
}
+
@ExampleAnnotation
public String annotatedString;
@@ -773,8 +774,7 @@ public void convertNullAnnotatedStringToString() throws Exception {
public static @interface ExampleAnnotation {
}
- private static class MyConditionalConverter implements Converter,
- ConditionalConverter {
+ private static class MyConditionalConverter implements Converter, ConditionalConverter {
private int matchAttempts = 0;
@@ -850,23 +850,26 @@ public int getNestedMatchAttempts() {
}
interface MyEnumInterface {
+
String getCode();
}
public static enum MyEnum implements MyEnumInterface {
+
A {
@Override
public String getCode() {
return "1";
}
- };
+ }
}
- private static class MyEnumInterfaceToStringConverter
- implements Converter {
+ private static class MyEnumInterfaceToStringConverter implements Converter {
+
@Override
public String convert(T source) {
return source.getCode();
}
}
+
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java
index 27a83cab3701..9d509c6121bb 100644
--- a/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java
+++ b/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -19,12 +19,12 @@
import java.util.List;
/**
- * Expressions are executed in an evaluation context. It is in this context that references
- * are resolved when encountered during expression evaluation.
+ * Expressions are executed in an evaluation context. It is in this context that
+ * references are resolved when encountered during expression evaluation.
*
* There is a default implementation of the EvaluationContext,
- * {@link org.springframework.expression.spel.support.StandardEvaluationContext}
- * that can be extended, rather than having to implement everything.
+ * {@link org.springframework.expression.spel.support.StandardEvaluationContext} that can
+ * be extended, rather than having to implement everything.
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -33,49 +33,51 @@
public interface EvaluationContext {
/**
- * @return the default root context object against which unqualified properties/methods/etc
- * should be resolved. This can be overridden when evaluating an expression.
+ * Return the default root context object against which unqualified
+ * properties/methods/etc should be resolved. This can be overridden
+ * when evaluating an expression.
*/
TypedValue getRootObject();
/**
- * @return a list of resolvers that will be asked in turn to locate a constructor
+ * Return a list of resolvers that will be asked in turn to locate a constructor.
*/
List getConstructorResolvers();
/**
- * @return a list of resolvers that will be asked in turn to locate a method
+ * Return a list of resolvers that will be asked in turn to locate a method.
*/
List getMethodResolvers();
/**
- * @return a list of accessors that will be asked in turn to read/write a property
+ * Return a list of accessors that will be asked in turn to read/write a property.
*/
List getPropertyAccessors();
/**
- * @return a type locator that can be used to find types, either by short or fully qualified name.
+ * Return a type locator that can be used to find types, either by short or
+ * fully qualified name.
*/
TypeLocator getTypeLocator();
/**
- * @return a type converter that can convert (or coerce) a value from one type to another.
+ * Return a type converter that can convert (or coerce) a value from one type to another.
*/
TypeConverter getTypeConverter();
/**
- * @return a type comparator for comparing pairs of objects for equality.
+ * Return a type comparator for comparing pairs of objects for equality.
*/
TypeComparator getTypeComparator();
/**
- * @return an operator overloader that may support mathematical operations
- * between more than the standard set of types
+ * Return an operator overloader that may support mathematical operations
+ * between more than the standard set of types.
*/
OperatorOverloader getOperatorOverloader();
/**
- * @return a bean resolver that can look up beans by name
+ * Return a bean resolver that can look up beans by name.
*/
BeanResolver getBeanResolver();
diff --git a/spring-expression/src/main/java/org/springframework/expression/MethodExecutor.java b/spring-expression/src/main/java/org/springframework/expression/MethodExecutor.java
index bd4dd74516cc..6b94e4fbac8b 100644
--- a/spring-expression/src/main/java/org/springframework/expression/MethodExecutor.java
+++ b/spring-expression/src/main/java/org/springframework/expression/MethodExecutor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -17,13 +17,15 @@
package org.springframework.expression;
/**
- * MethodExecutors are built by the resolvers and can be cached by the infrastructure to repeat an operation quickly
- * without going back to the resolvers. For example, the particular method to run on an object may be discovered by the
- * reflection method resolver - it will then build a MethodExecutor that executes that method and the MethodExecutor can
- * be reused without needing to go back to the resolver to discover the method again.
+ * MethodExecutors are built by the resolvers and can be cached by the infrastructure to
+ * repeat an operation quickly without going back to the resolvers. For example, the
+ * particular method to run on an object may be discovered by the reflection method
+ * resolver - it will then build a MethodExecutor that executes that method and the
+ * MethodExecutor can be reused without needing to go back to the resolver to discover
+ * the method again.
*
- * They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go
- * back to the resolvers to ask for a new one.
+ *
They can become stale, and in that case should throw an AccessException:
+ * This will cause the infrastructure to go back to the resolvers to ask for a new one.
*
* @author Andy Clement
* @since 3.0
@@ -34,10 +36,11 @@ public interface MethodExecutor {
* Execute a command using the specified arguments, and using the specified expression state.
* @param context the evaluation context in which the command is being executed
* @param target the target object of the call - null for static methods
- * @param arguments the arguments to the executor, should match (in terms of number and type) whatever the
- * command will need to run
+ * @param arguments the arguments to the executor, should match (in terms of number
+ * and type) whatever the command will need to run
* @return the value returned from execution
- * @throws AccessException if there is a problem executing the command or the MethodExecutor is no longer valid
+ * @throws AccessException if there is a problem executing the command or the
+ * MethodExecutor is no longer valid
*/
TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException;
diff --git a/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java b/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java
index 75cdc5eab8b7..e33dc8423fea 100644
--- a/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java
+++ b/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -21,8 +21,9 @@
import org.springframework.core.convert.TypeDescriptor;
/**
- * A method resolver attempts locate a method and returns a command executor that can be used to invoke that method.
- * The command executor will be cached but if it 'goes stale' the resolvers will be called again.
+ * A method resolver attempts locate a method and returns a command executor that can be
+ * used to invoke that method. The command executor will be cached but if it 'goes stale'
+ * the resolvers will be called again.
*
* @author Andy Clement
* @since 3.0
@@ -30,9 +31,9 @@
public interface MethodResolver {
/**
- * Within the supplied context determine a suitable method on the supplied object that can handle the
- * specified arguments. Return a MethodExecutor that can be used to invoke that method
- * (or {@code null} if no method could be found).
+ * Within the supplied context determine a suitable method on the supplied object that
+ * can handle the specified arguments. Return a {@link MethodExecutor} that can be used
+ * to invoke that method, or {@code null} if no method could be found.
* @param context the current evaluation context
* @param targetObject the object upon which the method is being called
* @param argumentTypes the arguments that the constructor must be able to handle
diff --git a/spring-expression/src/main/java/org/springframework/expression/TypedValue.java b/spring-expression/src/main/java/org/springframework/expression/TypedValue.java
index 67ecb99ad278..785f5a81f294 100644
--- a/spring-expression/src/main/java/org/springframework/expression/TypedValue.java
+++ b/spring-expression/src/main/java/org/springframework/expression/TypedValue.java
@@ -73,7 +73,7 @@ public TypeDescriptor getTypeDescriptor() {
@Override
public String toString() {
StringBuilder str = new StringBuilder();
- str.append("TypedValue: '").append(this.value).append("' of [").append(getTypeDescriptor() + "]");
+ str.append("TypedValue: '").append(this.value).append("' of [").append(getTypeDescriptor()).append("]");
return str.toString();
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/common/LiteralExpression.java b/spring-expression/src/main/java/org/springframework/expression/common/LiteralExpression.java
index ae7713c50f36..8740322c7297 100644
--- a/spring-expression/src/main/java/org/springframework/expression/common/LiteralExpression.java
+++ b/spring-expression/src/main/java/org/springframework/expression/common/LiteralExpression.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -102,7 +102,7 @@ public String getValue(EvaluationContext context, Object rootObject) throws Eval
public T getValue(EvaluationContext context, Object rootObject, Class desiredResultType) throws EvaluationException {
Object value = getValue(context, rootObject);
- return ExpressionUtils.convert(null, value, desiredResultType);
+ return ExpressionUtils.convert(context, value, desiredResultType);
}
public Class getValueType(Object rootObject) throws EvaluationException {
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java b/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java
index 85c1727c5c04..bf4866660a49 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -30,14 +30,18 @@
import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
+import org.springframework.util.Assert;
/**
- * An ExpressionState is for maintaining per-expression-evaluation state, any changes to it are not seen by other
- * expressions but it gives a place to hold local variables and for component expressions in a compound expression to
- * communicate state. This is in contrast to the EvaluationContext, which is shared amongst expression evaluations, and
- * any changes to it will be seen by other expressions or any code that chooses to ask questions of the context.
+ * An ExpressionState is for maintaining per-expression-evaluation state, any changes to
+ * it are not seen by other expressions but it gives a place to hold local variables and
+ * for component expressions in a compound expression to communicate state. This is in
+ * contrast to the EvaluationContext, which is shared amongst expression evaluations, and
+ * any changes to it will be seen by other expressions or any code that chooses to ask
+ * questions of the context.
*
- * It also acts as a place for to define common utility routines that the various Ast nodes might need.
+ *
It also acts as a place for to define common utility routines that the various AST
+ * nodes might need.
*
* @author Andy Clement
* @since 3.0
@@ -46,35 +50,33 @@ public class ExpressionState {
private final EvaluationContext relatedContext;
- private Stack variableScopes;
+ private final TypedValue rootObject;
- private Stack contextObjects;
+ private final SpelParserConfiguration configuration;
- private final TypedValue rootObject;
+ private Stack variableScopes;
- private SpelParserConfiguration configuration;
+ private Stack contextObjects;
public ExpressionState(EvaluationContext context) {
- this.relatedContext = context;
- this.rootObject = context.getRootObject();
+ this(context, context.getRootObject(), new SpelParserConfiguration(false, false));
}
public ExpressionState(EvaluationContext context, SpelParserConfiguration configuration) {
- this.relatedContext = context;
- this.configuration = configuration;
- this.rootObject = context.getRootObject();
+ this(context, context.getRootObject(), configuration);
}
public ExpressionState(EvaluationContext context, TypedValue rootObject) {
- this.relatedContext = context;
- this.rootObject = rootObject;
+ this(context, rootObject, new SpelParserConfiguration(false, false));
}
public ExpressionState(EvaluationContext context, TypedValue rootObject, SpelParserConfiguration configuration) {
+ Assert.notNull(context, "EvaluationContext must not be null");
+ Assert.notNull(configuration, "SpelParserConfiguration must not be null");
this.relatedContext = context;
- this.configuration = configuration;
this.rootObject = rootObject;
+ this.configuration = configuration;
}
@@ -90,23 +92,22 @@ private void ensureVariableScopesInitialized() {
* The active context object is what unqualified references to properties/etc are resolved against.
*/
public TypedValue getActiveContextObject() {
- if (this.contextObjects==null || this.contextObjects.isEmpty()) {
+ if (this.contextObjects == null || this.contextObjects.isEmpty()) {
return this.rootObject;
}
-
return this.contextObjects.peek();
}
public void pushActiveContextObject(TypedValue obj) {
- if (this.contextObjects==null) {
- this.contextObjects = new Stack();
+ if (this.contextObjects == null) {
+ this.contextObjects = new Stack();
}
this.contextObjects.push(obj);
}
public void popActiveContextObject() {
- if (this.contextObjects==null) {
- this.contextObjects = new Stack();
+ if (this.contextObjects == null) {
+ this.contextObjects = new Stack();
}
this.contextObjects.pop();
}
@@ -138,7 +139,8 @@ public Class> findType(String type) throws EvaluationException {
}
public Object convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
- return this.relatedContext.getTypeConverter().convertValue(value, TypeDescriptor.forObject(value), targetTypeDescriptor);
+ return this.relatedContext.getTypeConverter().convertValue(value,
+ TypeDescriptor.forObject(value), targetTypeDescriptor);
}
public TypeConverter getTypeConverter() {
@@ -151,9 +153,8 @@ public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor
}
/*
- * A new scope is entered when a function is invoked
+ * A new scope is entered when a function is invoked.
*/
-
public void enterScope(Map argMap) {
ensureVariableScopesInitialized();
this.variableScopes.push(new VariableScope(argMap));
@@ -192,8 +193,8 @@ public TypedValue operate(Operation op, Object left, Object right) throws Evalua
return new TypedValue(returnValue);
}
else {
- String leftType = (left==null?"null":left.getClass().getName());
- String rightType = (right==null?"null":right.getClass().getName());
+ String leftType = (left == null ? "null" : left.getClass().getName());
+ String rightType = (right == null? "null" : right.getClass().getName());
throw new SpelEvaluationException(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, leftType, rightType);
}
}
@@ -210,16 +211,20 @@ public SpelParserConfiguration getConfiguration() {
return this.configuration;
}
+
/**
- * A new scope is entered when a function is called and it is used to hold the parameters to the function call. If the names
- * of the parameters clash with those in a higher level scope, those in the higher level scope will not be accessible whilst
- * the function is executing. When the function returns the scope is exited.
+ * A new scope is entered when a function is called and it is used to hold the
+ * parameters to the function call. If the names of the parameters clash with
+ * those in a higher level scope, those in the higher level scope will not be
+ * accessible whilst the function is executing. When the function returns,
+ * the scope is exited.
*/
private static class VariableScope {
private final Map vars = new HashMap();
- public VariableScope() { }
+ public VariableScope() {
+ }
public VariableScope(Map