Sync with 3.1.x
* 3.1.x: (61 commits) Compensate for changes in JDK 7 Introspector Avoid 'type mismatch' errors in ExtendedBeanInfo Polish ExtendedBeanInfo and tests Infer AnnotationAttributes method return types Minor fix in MVC reference doc chapter Hibernate 4.1 etc TypeDescriptor equals implementation accepts annotations in any order "setBasenames" uses varargs now (for programmatic setup; SPR-9106) @ActiveProfiles mechanism works with @ImportResource as well (SPR-8992 polishing clarified Resource's "getFilename" method to consistently return null substituteNamedParameters detects and unwraps SqlParameterValue object Replace spaces with tabs Consider security in ClassUtils#getMostSpecificMethod Adding null check for username being null. Improvements for registering custom SQL exception translators in app c SPR-7680 Adding QueryTimeoutException to the DataAccessException hiera Minor polish in WebMvcConfigurationSupport Detect overridden boolean getters in ExtendedBeanInfo Polish ExtendedBeanInfoTests ...
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,6 +35,7 @@ import java.util.TreeSet;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -74,12 +75,6 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
public ExtendedBeanInfo(BeanInfo delegate) throws IntrospectionException {
|
||||
this.delegate = delegate;
|
||||
|
||||
// PropertyDescriptor instances from the delegate object are never added directly, but always
|
||||
// copied to the local collection of #propertyDescriptors and returned by calls to
|
||||
// #getPropertyDescriptors(). this algorithm iterates through all methods (method descriptors)
|
||||
// in the wrapped BeanInfo object, copying any existing PropertyDescriptor or creating a new
|
||||
// one for any non-standard setter methods found.
|
||||
|
||||
ALL_METHODS:
|
||||
for (MethodDescriptor md : delegate.getMethodDescriptors()) {
|
||||
Method method = md.getMethod();
|
||||
@@ -136,19 +131,13 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
Method indexedReadMethod = ipd.getIndexedReadMethod();
|
||||
Method indexedWriteMethod = ipd.getIndexedWriteMethod();
|
||||
// has the setter already been found by the wrapped BeanInfo?
|
||||
if (indexedWriteMethod != null
|
||||
&& indexedWriteMethod.getName().equals(method.getName())) {
|
||||
// yes -> copy it, including corresponding getter method (if any -- may be null)
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
// has a getter corresponding to this setter already been found by the wrapped BeanInfo?
|
||||
if (indexedReadMethod != null
|
||||
&& indexedReadMethod.getName().equals(getterMethodNameFor(propertyName))
|
||||
&& indexedReadMethod.getReturnType().equals(method.getParameterTypes()[1])) {
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, indexedReadMethod, method);
|
||||
continue ALL_METHODS;
|
||||
if (!(indexedWriteMethod != null
|
||||
&& indexedWriteMethod.getName().equals(method.getName()))) {
|
||||
indexedWriteMethod = method;
|
||||
}
|
||||
// yes -> copy it, including corresponding getter method (if any -- may be null)
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
// the INDEXED setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor
|
||||
// for it. no corresponding INDEXED getter was detected, so the 'indexed read method' parameter is null.
|
||||
@@ -159,24 +148,27 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
// the method is not a setter, but is it a getter?
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
// have we already copied this read method to a property descriptor locally?
|
||||
String propertyName = pd.getName();
|
||||
Method readMethod = pd.getReadMethod();
|
||||
Method mostSpecificReadMethod = ClassUtils.getMostSpecificMethod(readMethod, method.getDeclaringClass());
|
||||
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
|
||||
if (method.equals(pd.getReadMethod())
|
||||
&& existingPD.getName().equals(pd.getName())) {
|
||||
if (method.equals(mostSpecificReadMethod)
|
||||
&& existingPD.getName().equals(propertyName)) {
|
||||
if (existingPD.getReadMethod() == null) {
|
||||
// no -> add it now
|
||||
this.addOrUpdatePropertyDescriptor(pd, pd.getName(), method, pd.getWriteMethod());
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, method, pd.getWriteMethod());
|
||||
}
|
||||
// yes -> do not add a duplicate
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
}
|
||||
if (method == pd.getReadMethod()
|
||||
|| (pd instanceof IndexedPropertyDescriptor && method == ((IndexedPropertyDescriptor) pd).getIndexedReadMethod())) {
|
||||
if (method.equals(mostSpecificReadMethod)
|
||||
|| (pd instanceof IndexedPropertyDescriptor && method.equals(((IndexedPropertyDescriptor) pd).getIndexedReadMethod()))) {
|
||||
// yes -> copy it, including corresponding setter method (if any -- may be null)
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
this.addOrUpdatePropertyDescriptor(pd, pd.getName(), pd.getReadMethod(), pd.getWriteMethod(), ((IndexedPropertyDescriptor)pd).getIndexedReadMethod(), ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod());
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, pd.getWriteMethod(), ((IndexedPropertyDescriptor)pd).getIndexedReadMethod(), ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod());
|
||||
} else {
|
||||
this.addOrUpdatePropertyDescriptor(pd, pd.getName(), pd.getReadMethod(), pd.getWriteMethod());
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, pd.getWriteMethod());
|
||||
}
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
@@ -221,7 +213,9 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's write method
|
||||
if (writeMethod != null) {
|
||||
if (writeMethod != null
|
||||
&& !(existingPD instanceof IndexedPropertyDescriptor &&
|
||||
!writeMethod.getParameterTypes()[0].isArray())) {
|
||||
existingPD.setWriteMethod(writeMethod);
|
||||
}
|
||||
|
||||
@@ -278,7 +272,7 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
}
|
||||
this.propertyDescriptors.add(pd);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.warn(format("Could not create new PropertyDescriptor for readMethod [%s] writeMethod [%s] " +
|
||||
logger.debug(format("Could not create new PropertyDescriptor for readMethod [%s] writeMethod [%s] " +
|
||||
"indexedReadMethod [%s] indexedWriteMethod [%s] for property [%s]. Reason: %s",
|
||||
readMethod, writeMethod, indexedReadMethod, indexedWriteMethod, propertyName, ex.getMessage()));
|
||||
// suppress exception and attempt to continue
|
||||
@@ -289,10 +283,20 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
try {
|
||||
pd.setWriteMethod(writeMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.warn(format("Could not add write method [%s] for property [%s]. Reason: %s",
|
||||
logger.debug(format("Could not add write method [%s] for property [%s]. Reason: %s",
|
||||
writeMethod, propertyName, ex.getMessage()));
|
||||
// fall through -> add property descriptor as best we can
|
||||
}
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
((IndexedPropertyDescriptor)pd).setIndexedReadMethod(indexedReadMethod);
|
||||
try {
|
||||
((IndexedPropertyDescriptor)pd).setIndexedWriteMethod(indexedWriteMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.debug(format("Could not add indexed write method [%s] for property [%s]. Reason: %s",
|
||||
indexedWriteMethod, propertyName, ex.getMessage()));
|
||||
// fall through -> add property descriptor as best we can
|
||||
}
|
||||
}
|
||||
this.propertyDescriptors.add(pd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implem
|
||||
*/
|
||||
public AnnotatedGenericBeanDefinition(Class beanClass) {
|
||||
setBeanClass(beanClass);
|
||||
this.annotationMetadata = new StandardAnnotationMetadata(beanClass);
|
||||
this.annotationMetadata = new StandardAnnotationMetadata(beanClass, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.springframework.core.GenericCollectionTypeResolver;
|
||||
@@ -56,6 +57,8 @@ public class DependencyDescriptor implements Serializable {
|
||||
|
||||
private final boolean eager;
|
||||
|
||||
private int nestingLevel = 1;
|
||||
|
||||
private transient Annotation[] fieldAnnotations;
|
||||
|
||||
|
||||
@@ -153,6 +156,13 @@ public class DependencyDescriptor implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
public void increaseNestingLevel() {
|
||||
this.nestingLevel++;
|
||||
if (this.methodParameter != null) {
|
||||
this.methodParameter.increaseNestingLevel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize parameter name discovery for the underlying method parameter, if any.
|
||||
* <p>This method does not actually try to retrieve the parameter name at
|
||||
@@ -178,15 +188,30 @@ public class DependencyDescriptor implements Serializable {
|
||||
* @return the declared type (never <code>null</code>)
|
||||
*/
|
||||
public Class<?> getDependencyType() {
|
||||
return (this.field != null ? this.field.getType() : this.methodParameter.getParameterType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the generic type of the wrapped parameter/field.
|
||||
* @return the generic type (never <code>null</code>)
|
||||
*/
|
||||
public Type getGenericDependencyType() {
|
||||
return (this.field != null ? this.field.getGenericType() : this.methodParameter.getGenericParameterType());
|
||||
if (this.field != null) {
|
||||
if (this.nestingLevel > 1) {
|
||||
Type type = this.field.getGenericType();
|
||||
if (type instanceof ParameterizedType) {
|
||||
Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
if (arg instanceof Class) {
|
||||
return (Class) arg;
|
||||
}
|
||||
else if (arg instanceof ParameterizedType) {
|
||||
arg = ((ParameterizedType) arg).getRawType();
|
||||
if (arg instanceof Class) {
|
||||
return (Class) arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Object.class;
|
||||
}
|
||||
else {
|
||||
return this.field.getType();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this.methodParameter.getNestedParameterType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,7 +220,7 @@ public class DependencyDescriptor implements Serializable {
|
||||
*/
|
||||
public Class<?> getCollectionType() {
|
||||
return (this.field != null ?
|
||||
GenericCollectionTypeResolver.getCollectionFieldType(this.field) :
|
||||
GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) :
|
||||
GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter));
|
||||
}
|
||||
|
||||
@@ -205,7 +230,7 @@ public class DependencyDescriptor implements Serializable {
|
||||
*/
|
||||
public Class<?> getMapKeyType() {
|
||||
return (this.field != null ?
|
||||
GenericCollectionTypeResolver.getMapKeyFieldType(this.field) :
|
||||
GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) :
|
||||
GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter));
|
||||
}
|
||||
|
||||
@@ -215,7 +240,7 @@ public class DependencyDescriptor implements Serializable {
|
||||
*/
|
||||
public Class<?> getMapValueType() {
|
||||
return (this.field != null ?
|
||||
GenericCollectionTypeResolver.getMapValueFieldType(this.field) :
|
||||
GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) :
|
||||
GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter));
|
||||
}
|
||||
|
||||
@@ -248,13 +273,18 @@ public class DependencyDescriptor implements Serializable {
|
||||
if (this.fieldName != null) {
|
||||
this.field = this.declaringClass.getDeclaredField(this.fieldName);
|
||||
}
|
||||
else if (this.methodName != null) {
|
||||
this.methodParameter = new MethodParameter(
|
||||
this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
|
||||
}
|
||||
else {
|
||||
this.methodParameter = new MethodParameter(
|
||||
this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
|
||||
if (this.methodName != null) {
|
||||
this.methodParameter = new MethodParameter(
|
||||
this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
|
||||
}
|
||||
else {
|
||||
this.methodParameter = new MethodParameter(
|
||||
this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
|
||||
}
|
||||
for (int i = 1; i < this.nestingLevel; i++) {
|
||||
this.methodParameter.increaseNestingLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1328,8 +1328,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* @param mbd the corresponding bean definition
|
||||
*/
|
||||
protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
|
||||
Class<?> beanClass = predictBeanType(beanName, mbd, FactoryBean.class);
|
||||
return (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass));
|
||||
Class<?> predictedType = predictBeanType(beanName, mbd, FactoryBean.class);
|
||||
return (predictedType != null && FactoryBean.class.isAssignableFrom(predictedType)) ||
|
||||
(mbd.hasBeanClass() && FactoryBean.class.isAssignableFrom(mbd.getBeanClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1000,27 +1000,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private final Class type;
|
||||
|
||||
public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) {
|
||||
this.descriptor = descriptor;
|
||||
this.beanName = beanName;
|
||||
this.type = determineObjectFactoryType();
|
||||
}
|
||||
|
||||
private Class determineObjectFactoryType() {
|
||||
Type type = this.descriptor.getGenericDependencyType();
|
||||
if (type instanceof ParameterizedType) {
|
||||
Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
if (arg instanceof Class) {
|
||||
return (Class) arg;
|
||||
}
|
||||
}
|
||||
return Object.class;
|
||||
this.descriptor.increaseNestingLevel();
|
||||
}
|
||||
|
||||
public Object getObject() throws BeansException {
|
||||
return doResolveDependency(this.descriptor, this.type, this.beanName, null, null);
|
||||
return doResolveDependency(this.descriptor, this.descriptor.getDependencyType(), this.beanName, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import test.beans.CustomEnum;
|
||||
import test.beans.GenericBean;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
@@ -109,4 +110,14 @@ public final class BeanWrapperEnumTests {
|
||||
assertTrue(gb.getCustomEnumSet().contains(CustomEnum.VALUE_2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomEnumSetWithGetterSetterMismatch() {
|
||||
GenericBean<?> gb = new GenericBean<Object>();
|
||||
BeanWrapper bw = new BeanWrapperImpl(gb);
|
||||
bw.setPropertyValue("customEnumSetMismatch", new String[] {"VALUE_1", "VALUE_2"});
|
||||
assertEquals(2, gb.getCustomEnumSet().size());
|
||||
assertTrue(gb.getCustomEnumSet().contains(CustomEnum.VALUE_1));
|
||||
assertTrue(gb.getCustomEnumSet().contains(CustomEnum.VALUE_2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
@@ -32,9 +32,10 @@ import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.ExtendedBeanInfo.PropertyDescriptorComparator;
|
||||
import org.springframework.core.JdkVersion;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
@@ -429,11 +430,12 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
// interesting! standard Inspector picks up non-void return types on indexed write methods by default
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(trueUntilJdk17()));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
|
||||
@@ -456,13 +458,12 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
|
||||
// again as above, standard Inspector picks up non-void return types on indexed write methods by default
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(trueUntilJdk17()));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foos"), is(true));
|
||||
// again as above, standard Inspector picks up non-void return types on indexed write methods by default
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(true));
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
|
||||
@@ -550,17 +551,17 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasReadMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(trueUntilJdk17()));
|
||||
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "dateFormat"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(trueUntilJdk17()));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(true));
|
||||
}
|
||||
@@ -663,52 +664,18 @@ public class ExtendedBeanInfoTests {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reproSpr8806_y() throws IntrospectionException, SecurityException, NoSuchMethodException {
|
||||
Introspector.getBeanInfo(LawLibrary.class);
|
||||
private boolean trueUntilJdk17() {
|
||||
return JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_17;
|
||||
}
|
||||
|
||||
@Ignore @Test
|
||||
public void reproSpr8806_x() throws IntrospectionException, SecurityException, NoSuchMethodException {
|
||||
BeanInfo info = Introspector.getBeanInfo(LawLibrary.class);
|
||||
for (PropertyDescriptor d : info.getPropertyDescriptors()) {
|
||||
if (d.getName().equals("book")) {
|
||||
Method readMethod = d.getReadMethod();
|
||||
Method writeMethod = d.getWriteMethod();
|
||||
System.out.println(format("READ : %s.%s (bridge:%s)",
|
||||
readMethod.getDeclaringClass().getSimpleName(), readMethod.getName(), readMethod.isBridge()));
|
||||
System.out.println(format("WRITE: %s.%s (bridge:%s)",
|
||||
writeMethod.getDeclaringClass().getSimpleName(), writeMethod.getName(), writeMethod.isBridge()));
|
||||
new PropertyDescriptor("book", readMethod, writeMethod);
|
||||
}
|
||||
}
|
||||
|
||||
Method readMethod = LawLibrary.class.getMethod("getBook");
|
||||
Method writeMethod = LawLibrary.class.getMethod("setBook", Book.class);
|
||||
|
||||
System.out.println(format("read : %s.%s (bridge:%s)",
|
||||
readMethod.getDeclaringClass().getSimpleName(), readMethod.getName(), readMethod.isBridge()));
|
||||
System.out.println(format("write: %s.%s (bridge:%s)",
|
||||
writeMethod.getDeclaringClass().getSimpleName(), writeMethod.getName(), writeMethod.isBridge()));
|
||||
|
||||
|
||||
System.out.println("--------");
|
||||
for (Method m : LawLibrary.class.getMethods()) {
|
||||
if (m.getDeclaringClass() == Object.class) continue;
|
||||
System.out.println(format("%s %s.%s(%s) [bridge:%s]",
|
||||
m.getReturnType().getSimpleName(), m.getDeclaringClass().getSimpleName(),
|
||||
m.getName(),
|
||||
m.getParameterTypes().length == 1 ? m.getParameterTypes()[0].getSimpleName() : "",
|
||||
m.isBridge()));
|
||||
}
|
||||
|
||||
//new ExtendedBeanInfo(info);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reproSpr8806() throws IntrospectionException {
|
||||
BeanInfo bi = Introspector.getBeanInfo(LawLibrary.class);
|
||||
new ExtendedBeanInfo(bi); // throws
|
||||
// does not throw
|
||||
Introspector.getBeanInfo(LawLibrary.class);
|
||||
|
||||
// does not throw after the changes introduced in SPR-8806
|
||||
new ExtendedBeanInfo(Introspector.getBeanInfo(LawLibrary.class));
|
||||
}
|
||||
|
||||
interface Book { }
|
||||
@@ -734,4 +701,74 @@ public class ExtendedBeanInfoTests {
|
||||
class LawLibrary extends Library implements TextBookOperations {
|
||||
public LawBook getBook() { return null; }
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void cornerSpr8949() throws IntrospectionException {
|
||||
class A {
|
||||
@SuppressWarnings("unused")
|
||||
public boolean isTargetMethod() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
@Override
|
||||
public boolean isTargetMethod() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(B.class);
|
||||
|
||||
/* first, demonstrate the 'problem':
|
||||
* java.beans.Introspector returns the "wrong" declaring class for overridden read
|
||||
* methods, which in turn violates expectations in {@link ExtendedBeanInfo} regarding
|
||||
* method equality. Spring's {@link ClassUtils#getMostSpecificMethod(Method, Class)}
|
||||
* helps out here, and is now put into use in ExtendedBeanInfo as well
|
||||
*/
|
||||
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
|
||||
if ("targetMethod".equals(pd.getName())) {
|
||||
Method readMethod = pd.getReadMethod();
|
||||
assertTrue(readMethod.getDeclaringClass().equals(A.class)); // we expected B!
|
||||
|
||||
Method msReadMethod = ClassUtils.getMostSpecificMethod(readMethod, B.class);
|
||||
assertTrue(msReadMethod.getDeclaringClass().equals(B.class)); // and now we get it.
|
||||
}
|
||||
}
|
||||
|
||||
// and now demonstrate that we've indeed fixed the problem
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "targetMethod"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "targetMethod"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "targetMethod"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "targetMethod"), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cornerSpr8937() throws IntrospectionException {
|
||||
@SuppressWarnings("unused") class A {
|
||||
public void setAddress(String addr){ }
|
||||
public void setAddress(int index, String addr) { }
|
||||
public String getAddress(int index){ return null; }
|
||||
}
|
||||
|
||||
{ // baseline. ExtendedBeanInfo needs to behave exactly like the following
|
||||
BeanInfo bi = Introspector.getBeanInfo(A.class);
|
||||
assertThat(hasReadMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "address"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
|
||||
}
|
||||
{
|
||||
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(A.class));
|
||||
assertThat(hasReadMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "address"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,6 @@ import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.IndexedTestBean;
|
||||
@@ -40,6 +39,8 @@ import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor}
|
||||
* processing the JSR-303 {@link javax.inject.Inject} annotation.
|
||||
@@ -206,8 +207,8 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
|
||||
ConstructorsCollectionResourceInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean",
|
||||
new RootBeanDefinition(ConstructorsCollectionResourceInjectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
NestedTestBean ntb1 = new NestedTestBean();
|
||||
@@ -415,6 +416,74 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryWithTypedListField() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryListFieldInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
bf.setSerializationId("test");
|
||||
|
||||
ObjectFactoryListFieldInjectionBean bean = (ObjectFactoryListFieldInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bean = (ObjectFactoryListFieldInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryWithTypedListMethod() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryListMethodInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
bf.setSerializationId("test");
|
||||
|
||||
ObjectFactoryListMethodInjectionBean bean = (ObjectFactoryListMethodInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bean = (ObjectFactoryListMethodInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryWithTypedMapField() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryMapFieldInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
bf.setSerializationId("test");
|
||||
|
||||
ObjectFactoryMapFieldInjectionBean bean = (ObjectFactoryMapFieldInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bean = (ObjectFactoryMapFieldInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryWithTypedMapMethod() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryMapMethodInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
bf.setSerializationId("test");
|
||||
|
||||
ObjectFactoryMapMethodInjectionBean bean = (ObjectFactoryMapMethodInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bean = (ObjectFactoryMapMethodInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a dependency on a {@link org.springframework.beans.factory.FactoryBean} can be autowired via
|
||||
* {@link org.springframework.beans.factory.annotation.Autowired @Inject}, specifically addressing the JIRA issue
|
||||
@@ -835,6 +904,66 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryListFieldInjectionBean implements Serializable {
|
||||
|
||||
@Inject
|
||||
private Provider<List<TestBean>> testBeanFactory;
|
||||
|
||||
public void setTestBeanFactory(Provider<List<TestBean>> testBeanFactory) {
|
||||
this.testBeanFactory = testBeanFactory;
|
||||
}
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return this.testBeanFactory.get().get(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryListMethodInjectionBean implements Serializable {
|
||||
|
||||
private Provider<List<TestBean>> testBeanFactory;
|
||||
|
||||
@Inject
|
||||
public void setTestBeanFactory(Provider<List<TestBean>> testBeanFactory) {
|
||||
this.testBeanFactory = testBeanFactory;
|
||||
}
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return this.testBeanFactory.get().get(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryMapFieldInjectionBean implements Serializable {
|
||||
|
||||
@Inject
|
||||
private Provider<Map<String, TestBean>> testBeanFactory;
|
||||
|
||||
public void setTestBeanFactory(Provider<Map<String, TestBean>> testBeanFactory) {
|
||||
this.testBeanFactory = testBeanFactory;
|
||||
}
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return this.testBeanFactory.get().values().iterator().next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryMapMethodInjectionBean implements Serializable {
|
||||
|
||||
private Provider<Map<String, TestBean>> testBeanFactory;
|
||||
|
||||
@Inject
|
||||
public void setTestBeanFactory(Provider<Map<String, TestBean>> testBeanFactory) {
|
||||
this.testBeanFactory = testBeanFactory;
|
||||
}
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return this.testBeanFactory.get().values().iterator().next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bean with a dependency on a {@link org.springframework.beans.factory.FactoryBean}.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
|
||||
/**
|
||||
* Unit tests for SPR-8954, in which a custom {@link InstantiationAwareBeanPostProcessor}
|
||||
* forces the predicted type of a FactoryBean, effectively preventing retrieval of the
|
||||
* bean from calls to #getBeansOfType(FactoryBean.class). The implementation of
|
||||
* {@link AbstractBeanFactory#isFactoryBean(String, RootBeanDefinition)} now ensures
|
||||
* that not only the predicted bean type is considered, but also the original bean
|
||||
* definition's beanClass.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class Spr8954Tests {
|
||||
|
||||
@Test
|
||||
public void repro() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerBeanDefinition("foo", new RootBeanDefinition(FooFactoryBean.class));
|
||||
bf.addBeanPostProcessor(new PredictingBPP());
|
||||
|
||||
assertThat(bf.getBean("foo"), instanceOf(Foo.class));
|
||||
assertThat(bf.getBean("&foo"), instanceOf(FooFactoryBean.class));
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Map<String, FactoryBean> fbBeans = bf.getBeansOfType(FactoryBean.class);
|
||||
assertThat(1, equalTo(fbBeans.size()));
|
||||
assertThat("&foo", equalTo(fbBeans.keySet().iterator().next()));
|
||||
|
||||
Map<String, AnInterface> aiBeans = bf.getBeansOfType(AnInterface.class);
|
||||
assertThat(1, equalTo(aiBeans.size()));
|
||||
assertThat("&foo", equalTo(aiBeans.keySet().iterator().next()));
|
||||
}
|
||||
|
||||
static class FooFactoryBean implements FactoryBean<Foo>, AnInterface {
|
||||
|
||||
public Foo getObject() throws Exception {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return Foo.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
interface AnInterface {
|
||||
}
|
||||
|
||||
static class Foo {
|
||||
}
|
||||
|
||||
interface PredictedType {
|
||||
|
||||
}
|
||||
|
||||
static class PredictingBPP extends InstantiationAwareBeanPostProcessorAdapter {
|
||||
|
||||
@Override
|
||||
public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
|
||||
return FactoryBean.class.isAssignableFrom(beanClass) ?
|
||||
PredictedType.class :
|
||||
super.predictBeanType(beanClass, beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -244,6 +245,17 @@ public class GenericBean<T> {
|
||||
this.customEnumSet = customEnumSet;
|
||||
}
|
||||
|
||||
public Set<CustomEnum> getCustomEnumSetMismatch() {
|
||||
return customEnumSet;
|
||||
}
|
||||
|
||||
public void setCustomEnumSetMismatch(Set<String> customEnumSet) {
|
||||
this.customEnumSet = new HashSet<CustomEnum>(customEnumSet.size());
|
||||
for (Iterator<String> iterator = customEnumSet.iterator(); iterator.hasNext(); ) {
|
||||
this.customEnumSet.add(CustomEnum.valueOf(iterator.next()));
|
||||
}
|
||||
}
|
||||
|
||||
public static GenericBean createInstance(Set<Integer> integerSet) {
|
||||
return new GenericBean(integerSet);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user