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:
Chris Beams
2012-02-13 15:17:30 +01:00
134 changed files with 3552 additions and 1471 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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()));
}
/**

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}
}

View File

@@ -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}.
*/

View File

@@ -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);
}
}
}

View File

@@ -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);
}