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

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.
@@ -60,6 +60,10 @@ public class JobDetailFactoryBean
private JobDataMap jobDataMap = new JobDataMap();
private boolean durability = false;
private String description;
private String beanName;
private ApplicationContext applicationContext;
@@ -120,6 +124,21 @@ public class JobDetailFactoryBean
getJobDataMap().putAll(jobDataAsMap);
}
/**
* Specify the job's durability, i.e. whether it should remain stored
* in the job store even if no triggers point to it anymore.
*/
public void setDurability(boolean durability) {
this.durability = durability;
}
/**
* Set a textual description for this job.
*/
public void setDescription(String description) {
this.description = description;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@@ -172,6 +191,8 @@ public class JobDetailFactoryBean
jdi.setGroup(this.group);
jdi.setJobClass(this.jobClass);
jdi.setJobDataMap(this.jobDataMap);
jdi.setDurability(this.durability);
jdi.setDescription(this.description);
this.jobDetail = jdi;
*/
@@ -188,6 +209,8 @@ public class JobDetailFactoryBean
pvs.add("group", this.group);
pvs.add("jobClass", this.jobClass);
pvs.add("jobDataMap", this.jobDataMap);
pvs.add("durability", this.durability);
pvs.add("description", this.description);
bw.setPropertyValues(pvs);
this.jobDetail = (JobDetail) bw.getWrappedInstance();
}

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.
@@ -17,7 +17,6 @@
package org.springframework.cache.annotation;
import java.util.Collection;
import java.util.Map;
import javax.annotation.PostConstruct;
@@ -26,6 +25,7 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -41,8 +41,7 @@ import org.springframework.util.CollectionUtils;
@Configuration
public abstract class AbstractCachingConfiguration implements ImportAware {
/** Parsed annotation metadata for {@code @EnableCaching} on the importing class. */
protected Map<String, Object> enableCaching;
protected AnnotationAttributes enableCaching;
protected CacheManager cacheManager;
protected KeyGenerator keyGenerator;
@@ -52,8 +51,8 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
private Collection<CachingConfigurer> cachingConfigurers;
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableCaching = importMetadata.getAnnotationAttributes(
EnableCaching.class.getName(), false);
this.enableCaching = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableCaching.class.getName(), false));
Assert.notNull(this.enableCaching,
"@EnableCaching is not present on importing class " +
importMetadata.getClassName());

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.
@@ -44,7 +44,7 @@ public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
new BeanFactoryCacheOperationSourceAdvisor();
advisor.setCacheOperationSource(cacheOperationSource());
advisor.setAdvice(cacheInterceptor());
advisor.setOrder(((Integer)this.enableCaching.get("order")));
advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
return advisor;
}

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,10 +16,12 @@
package org.springframework.context.annotation;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.lang.annotation.Annotation;
import java.util.Map;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
@@ -64,26 +66,16 @@ public abstract class AdviceModeImportSelector<A extends Annotation> implements
* returns {@code null}
*/
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<?> annoType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Class<?> annoType = GenericTypeResolver.resolveTypeArgument(this.getClass(), AdviceModeImportSelector.class);
Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(annoType.getName());
AnnotationAttributes attributes = attributesFor(importingClassMetadata, annoType);
Assert.notNull(attributes, String.format(
"@%s is not present on importing class '%s' as expected",
annoType.getSimpleName(), importingClassMetadata.getClassName()));
String modeAttrName = getAdviceModeAttributeName();
Assert.hasText(modeAttrName);
AdviceMode adviceMode = attributes.getEnum(this.getAdviceModeAttributeName());
Object adviceMode = attributes.get(modeAttrName);
Assert.notNull(adviceMode, String.format(
"Advice mode attribute @%s#%s() does not exist",
annoType.getSimpleName(), modeAttrName));
Assert.isInstanceOf(AdviceMode.class, adviceMode, String.format(
"Incorrect type for advice mode attribute '@%s#%s()': ",
annoType.getSimpleName(), modeAttrName));
String[] imports = selectImports((AdviceMode) adviceMode);
String[] imports = selectImports(adviceMode);
Assert.notNull(imports, String.format("Unknown AdviceMode: '%s'", adviceMode));
return imports;

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.
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
@@ -136,8 +137,9 @@ public class AnnotatedBeanDefinitionReader {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
AnnotationMetadata metadata = abd.getMetadata();
if (ProfileHelper.isProfileAnnotationPresent(metadata)) {
if (!this.environment.acceptsProfiles(ProfileHelper.getCandidateProfiles(metadata))) {
if (metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
return;
}
}

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.
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -85,7 +86,7 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
Set<String> types = amd.getAnnotationTypes();
String beanName = null;
for (String type : types) {
Map<String, Object> attributes = amd.getAnnotationAttributes(type);
AnnotationAttributes attributes = MetadataUtils.attributesFor(amd, type);
if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
String value = (String) attributes.get("value");
if (StringUtils.hasLength(value)) {

View File

@@ -16,6 +16,8 @@
package org.springframework.context.annotation;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -27,6 +29,7 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
/**
@@ -217,21 +220,20 @@ public class AnnotationConfigUtils {
}
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
if (abd.getMetadata().isAnnotated(Primary.class.getName())) {
AnnotationMetadata metadata = abd.getMetadata();
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
if (abd.getMetadata().isAnnotated(Lazy.class.getName())) {
Boolean value = (Boolean) abd.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value");
abd.setLazyInit(value);
if (metadata.isAnnotated(Lazy.class.getName())) {
abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
}
if (abd.getMetadata().isAnnotated(DependsOn.class.getName())) {
String[] value = (String[]) abd.getMetadata().getAnnotationAttributes(DependsOn.class.getName()).get("value");
abd.setDependsOn(value);
if (metadata.isAnnotated(DependsOn.class.getName())) {
abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
}
if (abd instanceof AbstractBeanDefinition) {
if (abd.getMetadata().isAnnotated(Role.class.getName())) {
int value = (Integer) abd.getMetadata().getAnnotationAttributes(Role.class.getName()).get("value");
((AbstractBeanDefinition)abd).setRole(value);
if (metadata.isAnnotated(Role.class.getName())) {
int role = attributesFor(metadata, Role.class).getNumber("value");
((AbstractBeanDefinition)abd).setRole(role);
}
}
}

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,11 +16,13 @@
package org.springframework.context.annotation;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.lang.annotation.Annotation;
import java.util.Map;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.util.Assert;
/**
@@ -75,11 +77,11 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
Map<String, Object> attributes =
annDef.getMetadata().getAnnotationAttributes(this.scopeAnnotationType.getName());
AnnotationAttributes attributes =
attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName((String) attributes.get("value"));
ScopedProxyMode proxyMode = (ScopedProxyMode) attributes.get("proxyMode");
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}

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,10 +16,11 @@
package org.springframework.context.annotation;
import java.util.Map;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
/**
@@ -41,12 +42,11 @@ class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> enableAJAutoProxy =
importingClassMetadata.getAnnotationAttributes(EnableAspectJAutoProxy.class.getName());
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
if ((Boolean)enableAJAutoProxy.get("proxyTargetClass")) {
AnnotationAttributes enableAJAutoProxy =
attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.context.annotation;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.util.Map;
import java.util.Set;
@@ -23,6 +25,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
/**
@@ -58,7 +61,7 @@ public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
boolean candidateFound = false;
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
for (String annoType : annoTypes) {
Map<String, Object> candidate = importingClassMetadata.getAnnotationAttributes(annoType);
AnnotationAttributes candidate = attributesFor(importingClassMetadata, annoType);
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != 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.
@@ -29,6 +29,7 @@ import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
@@ -302,10 +303,11 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if (!ProfileHelper.isProfileAnnotationPresent(metadata)) {
if (!metadata.isAnnotated(Profile.class.getName())) {
return true;
}
return this.environment.acceptsProfiles(ProfileHelper.getCandidateProfiles(metadata));
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
return this.environment.acceptsProfiles(profile.getStringArray("value"));
}
}
return false;

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.
@@ -19,20 +19,20 @@ package org.springframework.context.annotation;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
@@ -46,18 +46,23 @@ import org.springframework.util.StringUtils;
class ComponentScanAnnotationParser {
private final ResourceLoader resourceLoader;
private final Environment environment;
private final BeanDefinitionRegistry registry;
public ComponentScanAnnotationParser(ResourceLoader resourceLoader, Environment environment, BeanDefinitionRegistry registry) {
public ComponentScanAnnotationParser(
ResourceLoader resourceLoader, Environment environment, BeanDefinitionRegistry registry) {
this.resourceLoader = resourceLoader;
this.environment = environment;
this.registry = registry;
}
public Set<BeanDefinitionHolder> parse(Map<String, Object> componentScanAttributes) {
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan) {
ClassPathBeanDefinitionScanner scanner =
new ClassPathBeanDefinitionScanner(registry, (Boolean)componentScanAttributes.get("useDefaultFilters"));
new ClassPathBeanDefinitionScanner(registry, componentScan.getBoolean("useDefaultFilters"));
Assert.notNull(this.environment, "Environment must not be null");
scanner.setEnvironment(this.environment);
@@ -65,46 +70,43 @@ class ComponentScanAnnotationParser {
Assert.notNull(this.resourceLoader, "ResourceLoader must not be null");
scanner.setResourceLoader(this.resourceLoader);
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(
(Class<?>)componentScanAttributes.get("nameGenerator"), BeanNameGenerator.class));
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
ScopedProxyMode scopedProxyMode = (ScopedProxyMode) componentScanAttributes.get("scopedProxy");
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
} else {
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(
(Class<?>)componentScanAttributes.get("scopeResolver"), ScopeMetadataResolver.class));
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
scanner.setResourcePattern((String)componentScanAttributes.get("resourcePattern"));
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
for (Filter filterAnno : (Filter[])componentScanAttributes.get("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filterAnno)) {
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
for (Filter filterAnno : (Filter[])componentScanAttributes.get("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filterAnno)) {
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
List<String> basePackages = new ArrayList<String>();
for (String pkg : (String[])componentScanAttributes.get("value")) {
for (String pkg : componentScan.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : (String[])componentScanAttributes.get("basePackages")) {
for (String pkg : componentScan.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : (Class<?>[])componentScanAttributes.get("basePackageClasses")) {
// TODO: loading user types directly here. implications on load-time
// weaving may mean we need to revert to stringified class names in
// annotation metadata
basePackages.add(clazz.getPackage().getName());
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
@@ -114,10 +116,12 @@ class ComponentScanAnnotationParser {
return scanner.doScan(basePackages.toArray(new String[]{}));
}
private List<TypeFilter> typeFiltersFor(Filter filterAnno) {
private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
List<TypeFilter> typeFilters = new ArrayList<TypeFilter>();
for (Class<?> filterClass : (Class<?>[])filterAnno.value()) {
switch (filterAnno.type()) {
FilterType filterType = filterAttributes.getEnum("type");
for (Class<?> filterClass : filterAttributes.getClassArray("value")) {
switch (filterType) {
case ANNOTATION:
Assert.isAssignable(Annotation.class, filterClass,
"An error occured when processing a @ComponentScan " +
@@ -136,7 +140,7 @@ class ComponentScanAnnotationParser {
typeFilters.add(BeanUtils.instantiateClass(filterClass, TypeFilter.class));
break;
default:
throw new IllegalArgumentException("unknown filter type " + filterAnno.type());
throw new IllegalArgumentException("unknown filter type " + filterType);
}
}
return typeFilters;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -25,11 +25,13 @@ import java.util.Set;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
@@ -49,25 +51,73 @@ final class ConfigurationClass {
private final Resource resource;
private final Map<String, Class<?>> importedResources = new LinkedHashMap<String, Class<?>>();
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<String, Class<? extends BeanDefinitionReader>>();
private final Set<BeanMethod> beanMethods = new LinkedHashSet<BeanMethod>();
private String beanName;
private final boolean imported;
/**
* Create a new {@link ConfigurationClass} with the given name.
* @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName must not be {@code null}
* @throws IllegalArgumentException if beanName is null (as of Spring 3.1.1)
* @see ConfigurationClass#ConfigurationClass(Class, boolean)
*/
public ConfigurationClass(MetadataReader metadataReader, String beanName) {
Assert.hasText(beanName, "bean name must not be null");
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.beanName = beanName;
this.imported = false;
}
/**
* Create a new {@link ConfigurationClass} representing a class that was imported
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}).
* @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName name of the {@code @Configuration} class bean
* @since 3.1.1
*/
public ConfigurationClass(MetadataReader metadataReader, boolean imported) {
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.imported = imported;
}
/**
* Create a new {@link ConfigurationClass} with the given name.
* @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean
* @throws IllegalArgumentException if beanName is null (as of Spring 3.1.1)
* @see ConfigurationClass#ConfigurationClass(Class, boolean)
*/
public ConfigurationClass(Class<?> clazz, String beanName) {
this.metadata = new StandardAnnotationMetadata(clazz);
Assert.hasText(beanName, "bean name must not be null");
this.metadata = new StandardAnnotationMetadata(clazz, true);
this.resource = new DescriptiveResource(clazz.toString());
this.beanName = beanName;
this.imported = false;
}
/**
* Create a new {@link ConfigurationClass} representing a class that was imported
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}).
* @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean
* @since 3.1.1
*/
public ConfigurationClass(Class<?> clazz, boolean imported) {
this.metadata = new StandardAnnotationMetadata(clazz, true);
this.resource = new DescriptiveResource(clazz.toString());
this.imported = imported;
}
public AnnotationMetadata getMetadata() {
return this.metadata;
@@ -81,6 +131,15 @@ final class ConfigurationClass {
return ClassUtils.getShortName(getMetadata().getClassName());
}
/**
* Return whether this configuration class was registered via @{@link Import} or
* automatically registered due to being nested within another configuration class.
* @since 3.1.1
*/
public boolean isImported() {
return this.imported;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@@ -97,11 +156,12 @@ final class ConfigurationClass {
return this.beanMethods;
}
public void addImportedResource(String importedResource, Class<?> readerClass) {
public void addImportedResource(
String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
public Map<String, Class<?>> getImportedResources() {
public Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
@@ -181,5 +241,4 @@ final class ConfigurationClass {
}
}
}

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.
@@ -27,6 +27,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
@@ -43,6 +44,8 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
@@ -51,6 +54,8 @@ import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
/**
* Reads a given fully-populated set of ConfigurationClass instances, registering bean
* definitions with the given {@link BeanDefinitionRegistry} based on its contents.
@@ -76,7 +81,10 @@ class ConfigurationClassBeanDefinitionReader {
private final MetadataReaderFactory metadataReaderFactory;
private ResourceLoader resourceLoader;
private final ResourceLoader resourceLoader;
private final Environment environment;
/**
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
@@ -86,13 +94,14 @@ class ConfigurationClassBeanDefinitionReader {
*/
public ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory,
ResourceLoader resourceLoader) {
ResourceLoader resourceLoader, Environment environment) {
this.registry = registry;
this.sourceExtractor = sourceExtractor;
this.problemReporter = problemReporter;
this.metadataReaderFactory = metadataReaderFactory;
this.resourceLoader = resourceLoader;
this.environment = environment;
}
@@ -122,32 +131,43 @@ class ConfigurationClassBeanDefinitionReader {
* Register the {@link Configuration} class itself as a bean definition.
*/
private void doLoadBeanDefinitionForConfigurationClassIfNecessary(ConfigurationClass configClass) {
if (configClass.getBeanName() != null) {
// a bean definition already exists for this configuration class -> nothing to do
if (!configClass.isImported()) {
return;
}
// no bean definition exists yet -> this must be an imported configuration class (@Import).
BeanDefinition configBeanDef = new GenericBeanDefinition();
String className = configClass.getMetadata().getClassName();
configBeanDef.setBeanClassName(className);
MetadataReader reader;
try {
reader = this.metadataReaderFactory.getMetadataReader(className);
}
catch (IOException ex) {
throw new IllegalStateException("Could not create MetadataReader for class " + className);
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
String configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName((AbstractBeanDefinition)configBeanDef, this.registry);
Map<String, Object> configAttributes =
reader.getAnnotationMetadata().getAnnotationAttributes(Configuration.class.getName());
// has the 'value' attribute of @Configuration been set?
String configBeanName = (String) configAttributes.get("value");
if (StringUtils.hasText(configBeanName)) {
// yes -> register the configuration class bean with this name
this.registry.registerBeanDefinition(configBeanName, configBeanDef);
}
else {
// no -> register the configuration class bean with a generated name
configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName((AbstractBeanDefinition)configBeanDef, this.registry);
}
configClass.setBeanName(configBeanName);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName));
}
}
else {
try {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
AnnotationMetadata metadata = reader.getAnnotationMetadata();
this.problemReporter.error(
new InvalidConfigurationImportProblem(className, reader.getResource(), metadata));
}
catch (IOException ex) {
throw new IllegalStateException("Could not create MetadataReader for class " + className);
}
AnnotationMetadata metadata = reader.getAnnotationMetadata();
this.problemReporter.error(
new InvalidConfigurationImportProblem(className, reader.getResource(), metadata));
}
}
@@ -176,15 +196,14 @@ class ConfigurationClassBeanDefinitionReader {
beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
// consider role
Map<String, Object> roleAttributes = metadata.getAnnotationAttributes(Role.class.getName());
if (roleAttributes != null) {
int role = (Integer) roleAttributes.get("value");
beanDef.setRole(role);
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
beanDef.setRole(role.<Integer>getNumber("value"));
}
// consider name and any aliases
Map<String, Object> beanAttributes = metadata.getAnnotationAttributes(Bean.class.getName());
List<String> names = new ArrayList<String>(Arrays.asList((String[]) beanAttributes.get("name")));
AnnotationAttributes bean = attributesFor(metadata, Bean.class);
List<String> names = new ArrayList<String>(Arrays.asList(bean.getStringArray("name")));
String beanName = (names.size() > 0 ? names.remove(0) : beanMethod.getMetadata().getMethodName());
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
@@ -211,40 +230,43 @@ class ConfigurationClassBeanDefinitionReader {
// is this bean to be instantiated lazily?
if (metadata.isAnnotated(Lazy.class.getName())) {
beanDef.setLazyInit((Boolean) metadata.getAnnotationAttributes(Lazy.class.getName()).get("value"));
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
beanDef.setLazyInit(lazy.getBoolean("value"));
}
else if (configClass.getMetadata().isAnnotated(Lazy.class.getName())){
beanDef.setLazyInit((Boolean) configClass.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value"));
AnnotationAttributes lazy = attributesFor(configClass.getMetadata(), Lazy.class);
beanDef.setLazyInit(lazy.getBoolean("value"));
}
if (metadata.isAnnotated(DependsOn.class.getName())) {
String[] dependsOn = (String[]) metadata.getAnnotationAttributes(DependsOn.class.getName()).get("value");
if (dependsOn.length > 0) {
beanDef.setDependsOn(dependsOn);
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
String[] otherBeans = dependsOn.getStringArray("value");
if (otherBeans.length > 0) {
beanDef.setDependsOn(otherBeans);
}
}
Autowire autowire = (Autowire) beanAttributes.get("autowire");
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
String initMethodName = (String) beanAttributes.get("initMethod");
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
String destroyMethodName = (String) beanAttributes.get("destroyMethod");
String destroyMethodName = bean.getString("destroyMethod");
if (StringUtils.hasText(destroyMethodName)) {
beanDef.setDestroyMethodName(destroyMethodName);
}
// consider scoping
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
Map<String, Object> scopeAttributes = metadata.getAnnotationAttributes(Scope.class.getName());
if (scopeAttributes != null) {
beanDef.setScope((String) scopeAttributes.get("value"));
proxyMode = (ScopedProxyMode) scopeAttributes.get("proxyMode");
AnnotationAttributes scope = attributesFor(metadata, Scope.class);
if (scope != null) {
beanDef.setScope(scope.getString("value"));
proxyMode = scope.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
@@ -266,22 +288,24 @@ class ConfigurationClassBeanDefinitionReader {
}
private void loadBeanDefinitionsFromImportedResources(Map<String, Class<?>> importedResources) {
private void loadBeanDefinitionsFromImportedResources(
Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<Class<?>, BeanDefinitionReader>();
for (Map.Entry<String, Class<?>> entry : importedResources.entrySet()) {
for (Map.Entry<String, Class<? extends BeanDefinitionReader>> entry : importedResources.entrySet()) {
String resource = entry.getKey();
Class<?> readerClass = entry.getValue();
Class<? extends BeanDefinitionReader> readerClass = entry.getValue();
if (!readerInstanceCache.containsKey(readerClass)) {
try {
// Instantiate the specified BeanDefinitionReader
BeanDefinitionReader readerInstance = (BeanDefinitionReader)
BeanDefinitionReader readerInstance =
readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
// Delegate the current ResourceLoader to it if possible
if (readerInstance instanceof AbstractBeanDefinitionReader) {
((AbstractBeanDefinitionReader)readerInstance).setResourceLoader(this.resourceLoader);
AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) readerInstance);
abdr.setResourceLoader(this.resourceLoader);
abdr.setEnvironment(this.environment);
}
readerInstanceCache.put(readerClass, readerInstance);
}
catch (Exception ex) {
@@ -336,6 +360,7 @@ class ConfigurationClassBeanDefinitionReader {
* declare at least one {@link Bean @Bean} method.
*/
private static class InvalidConfigurationImportProblem extends Problem {
public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) {
super(String.format("%s was @Import'ed but is not annotated with @Configuration " +
"nor does it declare any @Bean methods; it does not implement ImportSelector " +

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,6 +16,8 @@
package org.springframework.context.annotation;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
@@ -34,7 +36,9 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ResourceLoader;
@@ -119,8 +123,7 @@ class ConfigurationClassParser {
/**
* Parse the specified {@link Configuration @Configuration} class.
* @param clazz the Class to parse
* @param beanName may be null, but if populated represents the bean id
* (assumes that this configuration class was configured via XML)
* @param beanName must not be null (as of Spring 3.1.1)
*/
public void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
@@ -128,8 +131,9 @@ class ConfigurationClassParser {
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
AnnotationMetadata metadata = configClass.getMetadata();
if (this.environment != null && ProfileHelper.isProfileAnnotationPresent(metadata)) {
if (!this.environment.acceptsProfiles(ProfileHelper.getCandidateProfiles(metadata))) {
if (this.environment != null && metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
return;
}
}
@@ -140,7 +144,7 @@ class ConfigurationClassParser {
if (superClassName != null && !Object.class.getName().equals(superClassName)) {
if (metadata instanceof StandardAnnotationMetadata) {
Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
metadata = new StandardAnnotationMetadata(clazz.getSuperclass());
metadata = new StandardAnnotationMetadata(clazz.getSuperclass(), true);
}
else {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superClassName);
@@ -167,16 +171,16 @@ class ConfigurationClassParser {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName);
AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata();
if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) {
processConfigurationClass(new ConfigurationClass(reader, null));
processConfigurationClass(new ConfigurationClass(reader, true));
}
}
// process any @PropertySource annotations
Map<String, Object> propertySourceAttributes =
metadata.getAnnotationAttributes(org.springframework.context.annotation.PropertySource.class.getName());
if (propertySourceAttributes != null) {
String name = (String) propertySourceAttributes.get("name");
String[] locations = (String[]) propertySourceAttributes.get("value");
AnnotationAttributes propertySource =
attributesFor(metadata, org.springframework.context.annotation.PropertySource.class);
if (propertySource != null) {
String name = propertySource.getString("name");
String[] locations = propertySource.getStringArray("value");
ClassLoader classLoader = this.resourceLoader.getClassLoader();
for (String location : locations) {
location = this.environment.resolveRequiredPlaceholders(location);
@@ -188,34 +192,31 @@ class ConfigurationClassParser {
}
// process any @ComponentScan annotions
Map<String, Object> componentScanAttributes = metadata.getAnnotationAttributes(ComponentScan.class.getName());
if (componentScanAttributes != null) {
AnnotationAttributes componentScan = attributesFor(metadata, ComponentScan.class);
if (componentScan != null) {
// the config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScanAttributes);
Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan);
// check the set of scanned definitions for any further config classes and parse recursively if necessary
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), metadataReaderFactory)) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
// process any @Import annotations
List<Map<String, Object>> allImportAttribs =
List<AnnotationAttributes> imports =
findAllAnnotationAttributes(Import.class, metadata.getClassName(), true);
for (Map<String, Object> importAttribs : allImportAttribs) {
processImport(configClass, (String[]) importAttribs.get("value"), true);
for (AnnotationAttributes importAnno : imports) {
processImport(configClass, importAnno.getStringArray("value"), true);
}
// process any @ImportResource annotations
if (metadata.isAnnotated(ImportResource.class.getName())) {
String[] resources = (String[]) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("value");
Class<?> readerClass = (Class<?>) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("reader");
if (readerClass == null) {
throw new IllegalStateException("No reader class associated with imported resources: " +
StringUtils.arrayToCommaDelimitedString(resources));
}
AnnotationAttributes importResource = attributesFor(metadata, ImportResource.class);
String[] resources = importResource.getStringArray("value");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
configClass.addImportedResource(resource, readerClass);
}
@@ -239,11 +240,11 @@ class ConfigurationClassParser {
* @param annotatedClassName the class to inspect
* @param classValuesAsString whether class attributes should be returned as strings
*/
private List<Map<String, Object>> findAllAnnotationAttributes(
private List<AnnotationAttributes> findAllAnnotationAttributes(
Class<? extends Annotation> targetAnnotation, String annotatedClassName,
boolean classValuesAsString) throws IOException {
List<Map<String, Object>> allAttribs = new ArrayList<Map<String, Object>>();
List<AnnotationAttributes> allAttribs = new ArrayList<AnnotationAttributes>();
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(annotatedClassName);
AnnotationMetadata metadata = reader.getAnnotationMetadata();
@@ -253,16 +254,17 @@ class ConfigurationClassParser {
if (annotationType.equals(targetAnnotationType)) {
continue;
}
MetadataReader metaReader = this.metadataReaderFactory.getMetadataReader(annotationType);
Map<String, Object> targetAttribs =
metaReader.getAnnotationMetadata().getAnnotationAttributes(targetAnnotationType, classValuesAsString);
AnnotationMetadata metaAnnotations =
this.metadataReaderFactory.getMetadataReader(annotationType).getAnnotationMetadata();
AnnotationAttributes targetAttribs =
AnnotationAttributes.fromMap(metaAnnotations.getAnnotationAttributes(targetAnnotationType, classValuesAsString));
if (targetAttribs != null) {
allAttribs.add(targetAttribs);
}
}
Map<String, Object> localAttribs =
metadata.getAnnotationAttributes(targetAnnotationType, classValuesAsString);
AnnotationAttributes localAttribs =
AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(targetAnnotationType, classValuesAsString));
if (localAttribs != null) {
allAttribs.add(localAttribs);
}
@@ -300,7 +302,7 @@ class ConfigurationClassParser {
else {
// the candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class
this.importStack.registerImport(importingClassMetadata.getClassName(), candidate);
processConfigurationClass(new ConfigurationClass(reader, null));
processConfigurationClass(new ConfigurationClass(reader, true));
}
}
this.importStack.pop();

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.
@@ -199,8 +199,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private ConfigurationClassBeanDefinitionReader getConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry) {
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory, this.resourceLoader);
this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor,
this.problemReporter, this.metadataReaderFactory, this.resourceLoader, this.environment);
}
return this.reader;
}

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.
@@ -60,7 +60,8 @@ abstract class ConfigurationClassUtils {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
metadata = new StandardAnnotationMetadata(((AbstractBeanDefinition) beanDef).getBeanClass());
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
metadata = new StandardAnnotationMetadata(beanClass, true);
}
else {
String className = beanDef.getBeanClassName();

View File

@@ -18,8 +18,6 @@ package org.springframework.context.annotation;
import static org.springframework.context.weaving.AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE;
import java.util.Map;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -27,6 +25,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.EnableLoadTimeWeaving.AspectJWeaving;
import org.springframework.context.weaving.AspectJWeavingEnabler;
import org.springframework.context.weaving.DefaultContextLoadTimeWeaver;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.util.Assert;
@@ -46,7 +45,7 @@ import org.springframework.util.Assert;
@Configuration
public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoaderAware {
private Map<String, Object> enableLTW;
private AnnotationAttributes enableLTW;
@Autowired(required=false)
private LoadTimeWeavingConfigurer ltwConfigurer;
@@ -54,7 +53,7 @@ public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoade
private ClassLoader beanClassLoader;
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableLTW = importMetadata.getAnnotationAttributes(EnableLoadTimeWeaving.class.getName(), false);
this.enableLTW = MetadataUtils.attributesFor(importMetadata, EnableLoadTimeWeaving.class);
Assert.notNull(this.enableLTW,
"@EnableLoadTimeWeaving is not present on importing class " +
importMetadata.getClassName());
@@ -79,7 +78,8 @@ public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoade
loadTimeWeaver = new DefaultContextLoadTimeWeaver(this.beanClassLoader);
}
switch ((AspectJWeaving) this.enableLTW.get("aspectjWeaving")) {
AspectJWeaving aspectJWeaving = this.enableLTW.getEnum("aspectjWeaving");
switch (aspectJWeaving) {
case DISABLED:
// AJ weaving is disabled -> do nothing
break;

View File

@@ -0,0 +1,49 @@
/*
* 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.context.annotation;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
/**
* Convenience methods adapting {@link AnnotationMetadata} and {@link MethodMetadata}
* annotation attribute maps to the {@link AnnotationAttributes} API. As of Spring 3.1.1,
* both the reflection- and ASM-based implementations of these SPIs return
* {@link AnnotationAttributes} instances anyway, but for backward-compatibility, their
* signatures still return Maps. Therefore, for the usual case, these methods perform
* little more than a cast from Map to AnnotationAttributes.
*
* @author Chris Beams
* @since 3.1.1
* @see AnnotationAttributes#fromMap(java.util.Map)
*/
class MetadataUtils {
public static AnnotationAttributes attributesFor(AnnotationMetadata metadata, Class<?> annoClass) {
return attributesFor(metadata, annoClass.getName());
}
public static AnnotationAttributes attributesFor(AnnotationMetadata metadata, String annoClassName) {
return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annoClassName, false));
}
public static AnnotationAttributes attributesFor(MethodMetadata metadata, Class<?> targetAnno) {
return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(targetAnno.getName()));
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2002-2011 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.context.annotation;
import org.springframework.core.type.AnnotationMetadata;
class ProfileHelper {
/**
* Return whether the given metadata includes Profile information, whether directly or
* through meta-annotation.
*/
static boolean isProfileAnnotationPresent(AnnotationMetadata metadata) {
return metadata.isAnnotated(Profile.class.getName());
}
/**
* Return the String[] of candidate profiles from {@link Profile#value()}.
*/
static String[] getCandidateProfiles(AnnotationMetadata metadata) {
return (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get("value");
}
}

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.
@@ -135,7 +135,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
* @see java.util.ResourceBundle
*/
public void setBasename(String basename) {
setBasenames(new String[] {basename});
setBasenames(basename);
}
/**
@@ -153,7 +153,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
* @see #setBasename
* @see java.util.ResourceBundle
*/
public void setBasenames(String[] basenames) {
public void setBasenames(String... basenames) {
if (basenames != null) {
this.basenames = new String[basenames.length];
for (int i = 0; i < basenames.length; i++) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 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.
@@ -100,7 +100,7 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
* @see java.util.ResourceBundle#getBundle(String)
*/
public void setBasename(String basename) {
setBasenames(new String[] {basename});
setBasenames(basename);
}
/**
@@ -119,7 +119,7 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
* @see #setBasename
* @see java.util.ResourceBundle#getBundle(String)
*/
public void setBasenames(String[] basenames) {
public void setBasenames(String... basenames) {
if (basenames != null) {
this.basenames = new String[basenames.length];
for (int i = 0; i < basenames.length; i++) {

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.
@@ -36,6 +36,7 @@ import org.springframework.util.ClassUtils;
* <p>Thanks to Ales Justin and Marius Bogoevici for the initial prototype.
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.0
*/
public class JBossLoadTimeWeaver implements LoadTimeWeaver {
@@ -55,23 +56,18 @@ public class JBossLoadTimeWeaver implements LoadTimeWeaver {
/**
* Create a new instance of the {@link JBossLoadTimeWeaver} class using
* the supplied {@link ClassLoader}.
* @param classLoader the <code>ClassLoader</code> to delegate to for
* weaving (must not be <code>null</code>)
* @param classLoader the <code>ClassLoader</code> to delegate to for weaving
* (must not be <code>null</code>)
*/
public JBossLoadTimeWeaver(ClassLoader classLoader) {
Assert.notNull(classLoader, "ClassLoader must not be null");
String loaderClassName = classLoader.getClass().getName();
if (loaderClassName.startsWith("org.jboss.classloader")) {
// JBoss AS 5 or JBoss AS 6
this.adapter = new JBossMCAdapter(classLoader);
}
else if (loaderClassName.startsWith("org.jboss.modules")) {
if (classLoader.getClass().getName().startsWith("org.jboss.modules")) {
// JBoss AS 7
this.adapter = new JBossModulesAdapter(classLoader);
}
else {
throw new IllegalArgumentException("Unexpected ClassLoader type: " + loaderClassName);
// JBoss AS 5 or JBoss AS 6
this.adapter = new JBossMCAdapter(classLoader);
}
}

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.
@@ -17,12 +17,12 @@
package org.springframework.scheduling.annotation;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Executor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
@@ -37,12 +37,13 @@ import org.springframework.util.Assert;
@Configuration
public abstract class AbstractAsyncConfiguration implements ImportAware {
protected Map<String, Object> enableAsync;
protected AnnotationAttributes enableAsync;
protected Executor executor;
public void setImportMetadata(AnnotationMetadata importMetadata) {
enableAsync = importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false);
Assert.notNull(enableAsync,
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
Assert.notNull(this.enableAsync,
"@EnableAsync is not present on importing class " +
importMetadata.getClassName());
}

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.
@@ -42,13 +42,11 @@ public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name=AnnotationConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
Assert.notNull(enableAsync, "@EnableAsync annotation metadata was not injected");
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
@SuppressWarnings("unchecked")
Class<? extends Annotation> customAsyncAnnotation =
(Class<? extends Annotation>) enableAsync.get("annotation");
Class<? extends Annotation> customAsyncAnnotation = enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
@@ -57,9 +55,8 @@ public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
bpp.setExecutor(this.executor);
}
bpp.setProxyTargetClass((Boolean) enableAsync.get("proxyTargetClass"));
bpp.setOrder(((Integer) enableAsync.get("order")));
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}

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.
@@ -28,7 +28,7 @@ import org.springframework.beans.factory.InitializingBean;
* (ideally on the VM bootstrap classpath).
*
* <p>For details on the ForkJoinPool API and its use with RecursiveActions, see the
* <a href="http://download.java.net/jdk7/docs/api/java/util/concurrent/ForkJoinPool.html">JDK 7 javadoc</a>.
* <a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinPool.html">JDK 7 javadoc</a>.
*
* <p><code>jsr166.jar</code>, containing <code>java.util.concurrent</code> updates for Java 6, can be obtained
* from the <a href="http://gee.cs.oswego.edu/dl/concurrency-interest/">concurrency interest website</a>.

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.
@@ -120,13 +120,6 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
this.errors.addAll(errors.getAllErrors());
}
/**
* Resolve the given error code into message codes.
* Calls the MessageCodesResolver with appropriate parameters.
* @param errorCode the error code to resolve into message codes
* @return the resolved message codes
* @see #setMessageCodesResolver
*/
public String[] resolveMessageCodes(String errorCode) {
return getMessageCodesResolver().resolveMessageCodes(errorCode, getObjectName());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 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.
@@ -220,6 +220,10 @@ public class BindException extends Exception implements BindingResult {
this.bindingResult.addError(error);
}
public String[] resolveMessageCodes(String errorCode) {
return this.bindingResult.resolveMessageCodes(errorCode);
}
public String[] resolveMessageCodes(String errorCode, String field) {
return this.bindingResult.resolveMessageCodes(errorCode, field);
}

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.
@@ -112,6 +112,14 @@ public interface BindingResult extends Errors {
*/
void addError(ObjectError error);
/**
* Resolve the given error code into message codes.
* <p>Calls the configured {@link MessageCodesResolver} with appropriate parameters.
* @param errorCode the error code to resolve into message codes
* @return the resolved message codes
*/
String[] resolveMessageCodes(String errorCode);
/**
* Resolve the given error code into message codes for the given field.
* <p>Calls the configured {@link MessageCodesResolver} with appropriate parameters.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -28,7 +28,6 @@ import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorContext;
import javax.validation.ValidatorFactory;
import javax.validation.spi.ValidationProvider;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
@@ -87,7 +86,7 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
* @see javax.validation.Validation#byDefaultProvider()
*/
@SuppressWarnings("rawtypes")
public void setProviderClass(Class<? extends ValidationProvider> providerClass) {
public void setProviderClass(Class providerClass) {
this.providerClass = providerClass;
}

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.
@@ -119,12 +119,11 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
// can do custom FieldError registration with invalid value from ConstraintViolation,
// as necessary for Hibernate Validator compatibility (non-indexed set path in field)
BindingResult bindingResult = (BindingResult) errors;
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
String nestedField = bindingResult.getNestedPath() + field;
ObjectError error;
if ("".equals(nestedField)) {
error = new ObjectError(
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage());
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
bindingResult.addError(new ObjectError(
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage()));
}
else {
Object invalidValue = violation.getInvalidValue();
@@ -132,11 +131,11 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
// bean constraint with property path: retrieve the actual property value
invalidValue = bindingResult.getRawFieldValue(field);
}
error = new FieldError(
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
bindingResult.addError(new FieldError(
errors.getObjectName(), nestedField, invalidValue, false,
errorCodes, errorArgs, violation.getMessage());
errorCodes, errorArgs, violation.getMessage()));
}
bindingResult.addError(error);
}
else {
// got no BindingResult - can only do standard rejectValue call

View File

@@ -42,7 +42,7 @@ public abstract class AbstractCircularImportDetectionTests {
public void simpleCircularImportIsDetected() throws Exception {
boolean threw = false;
try {
newParser().parse(loadAsConfigurationSource(A.class), null);
newParser().parse(loadAsConfigurationSource(A.class), "A");
} catch (BeanDefinitionParsingException ex) {
assertTrue("Wrong message. Got: " + ex.getMessage(),
ex.getMessage().contains(
@@ -59,7 +59,7 @@ public abstract class AbstractCircularImportDetectionTests {
public void complexCircularImportIsDetected() throws Exception {
boolean threw = false;
try {
newParser().parse(loadAsConfigurationSource(X.class), null);
newParser().parse(loadAsConfigurationSource(X.class), "X");
}
catch (BeanDefinitionParsingException ex) {
assertTrue("Wrong message. Got: " + ex.getMessage(),

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.
@@ -19,18 +19,19 @@ package org.springframework.context.annotation;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
@@ -55,8 +56,8 @@ public class ImportAwareTests {
AnnotationMetadata importMetadata = importAwareConfig.importMetadata;
assertThat("import metadata was not injected", importMetadata, notNullValue());
assertThat(importMetadata.getClassName(), is(ImportingConfig.class.getName()));
Map<String, Object> importAttribs = importMetadata.getAnnotationAttributes(Import.class.getName());
Class<?>[] importedClasses = (Class<?>[])importAttribs.get("value");
AnnotationAttributes importAttribs = attributesFor(importMetadata, Import.class);
Class<?>[] importedClasses = importAttribs.getClassArray("value");
assertThat(importedClasses[0].getName(), is(ImportedConfig.class.getName()));
}
@@ -72,8 +73,8 @@ public class ImportAwareTests {
AnnotationMetadata importMetadata = importAwareConfig.importMetadata;
assertThat("import metadata was not injected", importMetadata, notNullValue());
assertThat(importMetadata.getClassName(), is(IndirectlyImportingConfig.class.getName()));
Map<String, Object> enableAttribs = importMetadata.getAnnotationAttributes(EnableImportedConfig.class.getName());
String foo = (String)enableAttribs.get("foo");
AnnotationAttributes enableAttribs = attributesFor(importMetadata, EnableImportedConfig.class);
String foo = enableAttribs.getString("foo");
assertThat(foo, is("xyz"));
}
@@ -129,7 +130,6 @@ public class ImportAwareTests {
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("ImportAwareTests.BPP.setBeanFactory()");
}
}
}

View File

@@ -25,6 +25,7 @@ import test.beans.TestBean;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
@@ -53,13 +54,6 @@ public class ImportTests {
assertThat(beanFactory.getBeanDefinitionCount(), equalTo(expectedCount));
}
@Test
public void testProcessImports() {
int configClasses = 2;
int beansInClasses = 2;
assertBeanDefinitionCount((configClasses + beansInClasses), ConfigurationWithImportAnnotation.class);
}
@Test
public void testProcessImportsWithAsm() {
int configClasses = 2;
@@ -315,4 +309,36 @@ public class ImportTests {
static class ConfigAnnotated { }
static class NonConfigAnnotated { }
// ------------------------------------------------------------------------
/**
* Test that values supplied to @Configuration(value="...") are propagated as the
* bean name for the configuration class even in the case of inclusion via @Import
* or in the case of automatic registration via nesting
*/
@Test
public void reproSpr9023() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(B.class);
ctx.refresh();
System.out.println(ctx.getBeanFactory());
assertThat(ctx.getBeanNamesForType(B.class)[0], is("config-b"));
assertThat(ctx.getBeanNamesForType(A.class)[0], is("config-a"));
}
@Configuration("config-a")
static class A { }
@Configuration("config-b")
@Import(A.class)
static class B { }
@Test
public void testProcessImports() {
int configClasses = 2;
int beansInClasses = 2;
assertBeanDefinitionCount((configClasses + beansInClasses), ConfigurationWithImportAnnotation.class);
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.context.annotation.configuration.spr9031;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.configuration.spr9031.scanpackage.Spr9031Component;
/**
* Unit tests cornering bug SPR-9031.
*
* @author Chris Beams
* @since 3.1.1
*/
public class Spr9031Tests {
/**
* Use of @Import to register LowLevelConfig results in ASM-based annotation
* processing.
*/
@Test
public void withAsmAnnotationProcessing() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(HighLevelConfig.class);
ctx.refresh();
assertThat(ctx.getBean(LowLevelConfig.class).scanned, not(nullValue()));
}
/**
* Direct registration of LowLevelConfig results in reflection-based annotation
* processing.
*/
@Test
public void withoutAsmAnnotationProcessing() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(LowLevelConfig.class);
ctx.refresh();
assertThat(ctx.getBean(LowLevelConfig.class).scanned, not(nullValue()));
}
@Configuration
@Import(LowLevelConfig.class)
static class HighLevelConfig {}
@Configuration
@ComponentScan(
basePackages = "org.springframework.context.annotation.configuration.spr9031.scanpackage",
includeFilters = { @Filter(MarkerAnnotation.class) })
static class LowLevelConfig {
// fails to wire when LowLevelConfig is processed with ASM because nested @Filter
// annotation is not parsed
@Autowired Spr9031Component scanned;
}
public @interface MarkerAnnotation {}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.context.annotation.configuration.spr9031.scanpackage;
import org.springframework.context.annotation.configuration.spr9031.Spr9031Tests.MarkerAnnotation;
@MarkerAnnotation
public class Spr9031Component {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -110,11 +110,22 @@ public class ValidatorFactoryTests {
assertEquals(2, result.getErrorCount());
FieldError fieldError = result.getFieldError("name");
assertEquals("name", fieldError.getField());
System.out.println(Arrays.asList(fieldError.getCodes()));
List<String> errorCodes = Arrays.asList(fieldError.getCodes());
assertEquals(4, errorCodes.size());
assertTrue(errorCodes.contains("NotNull.person.name"));
assertTrue(errorCodes.contains("NotNull.name"));
assertTrue(errorCodes.contains("NotNull.java.lang.String"));
assertTrue(errorCodes.contains("NotNull"));
System.out.println(fieldError.getDefaultMessage());
fieldError = result.getFieldError("address.street");
assertEquals("address.street", fieldError.getField());
System.out.println(Arrays.asList(fieldError.getCodes()));
errorCodes = Arrays.asList(fieldError.getCodes());
assertEquals(5, errorCodes.size());
assertTrue(errorCodes.contains("NotNull.person.address.street"));
assertTrue(errorCodes.contains("NotNull.address.street"));
assertTrue(errorCodes.contains("NotNull.street"));
assertTrue(errorCodes.contains("NotNull.java.lang.String"));
assertTrue(errorCodes.contains("NotNull"));
System.out.println(fieldError.getDefaultMessage());
}
@@ -129,7 +140,10 @@ public class ValidatorFactoryTests {
validator.validate(person, result);
assertEquals(1, result.getErrorCount());
ObjectError globalError = result.getGlobalError();
System.out.println(Arrays.asList(globalError.getCodes()));
List<String> errorCodes = Arrays.asList(globalError.getCodes());
assertEquals(2, errorCodes.size());
assertTrue(errorCodes.contains("NameAddressValid.person"));
assertTrue(errorCodes.contains("NameAddressValid"));
System.out.println(globalError.getDefaultMessage());
}

View File

@@ -21,6 +21,7 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
@@ -233,6 +234,29 @@ public class MethodParameter {
return this.genericParameterType;
}
public Class<?> getNestedParameterType() {
if (this.nestingLevel > 1) {
Type type = getGenericParameterType();
if (type instanceof ParameterizedType) {
Integer index = getTypeIndexForCurrentLevel();
Type arg = ((ParameterizedType) type).getActualTypeArguments()[index != null ? index : 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 getParameterType();
}
}
/**
* Return the annotations associated with the target method/constructor itself.
*/

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.
@@ -40,7 +40,7 @@ public class OrderComparator implements Comparator<Object> {
/**
* Shared default instance of OrderComparator.
*/
public static OrderComparator INSTANCE = new OrderComparator();
public static final OrderComparator INSTANCE = new OrderComparator();
public int compare(Object o1, Object o2) {

View File

@@ -0,0 +1,132 @@
/*
* 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.core.annotation;
import static java.lang.String.format;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* {@link LinkedHashMap} subclass representing annotation attribute key/value pairs
* as read by Spring's reflection- or ASM-based {@link
* org.springframework.core.type.AnnotationMetadata AnnotationMetadata} implementations.
* Provides 'pseudo-reification' to avoid noisy Map generics in the calling code as well
* as convenience methods for looking up annotation attributes in a type-safe fashion.
*
* @author Chris Beams
* @since 3.1.1
*/
@SuppressWarnings("serial")
public class AnnotationAttributes extends LinkedHashMap<String, Object> {
/**
* Create a new, empty {@link AnnotationAttributes} instance.
*/
public AnnotationAttributes() {
}
/**
* Create a new, empty {@link AnnotationAttributes} instance with the given initial
* capacity to optimize performance.
* @param initialCapacity initial size of the underlying map
*/
public AnnotationAttributes(int initialCapacity) {
super(initialCapacity);
}
/**
* Create a new {@link AnnotationAttributes} instance, wrapping the provided map
* and all its key/value pairs.
* @param map original source of annotation attribute key/value pairs to wrap
* @see #fromMap(Map)
*/
public AnnotationAttributes(Map<String, Object> map) {
super(map);
}
/**
* Return an {@link AnnotationAttributes} instance based on the given map; if the map
* is already an {@code AnnotationAttributes} instance, it is casted and returned
* immediately without creating any new instance; otherwise create a new instance by
* wrapping the map with the {@link #AnnotationAttributes(Map)} constructor.
* @param map original source of annotation attribute key/value pairs
*/
public static AnnotationAttributes fromMap(Map<String, Object> map) {
if (map == null) {
return null;
}
if (map instanceof AnnotationAttributes) {
return (AnnotationAttributes) map;
}
return new AnnotationAttributes(map);
}
public String getString(String attributeName) {
return doGet(attributeName, String.class);
}
public String[] getStringArray(String attributeName) {
return doGet(attributeName, String[].class);
}
public boolean getBoolean(String attributeName) {
return doGet(attributeName, Boolean.class);
}
@SuppressWarnings("unchecked")
public <N extends Number> N getNumber(String attributeName) {
return (N) doGet(attributeName, Integer.class);
}
@SuppressWarnings("unchecked")
public <E extends Enum<?>> E getEnum(String attributeName) {
return (E) doGet(attributeName, Enum.class);
}
@SuppressWarnings("unchecked")
public <T> Class<? extends T> getClass(String attributeName) {
return (Class<T>)doGet(attributeName, Class.class);
}
public Class<?>[] getClassArray(String attributeName) {
return doGet(attributeName, Class[].class);
}
public AnnotationAttributes getAnnotation(String attributeName) {
return doGet(attributeName, AnnotationAttributes.class);
}
public AnnotationAttributes[] getAnnotationArray(String attributeName) {
return doGet(attributeName, AnnotationAttributes[].class);
}
@SuppressWarnings("unchecked")
private <T> T doGet(String attributeName, Class<T> expectedType) {
Assert.hasText(attributeName, "attributeName must not be null or empty");
Object value = this.get(attributeName);
Assert.notNull(value, format("Attribute '%s' not found", attributeName));
Assert.isAssignable(expectedType, value.getClass(),
format("Attribute '%s' is of type [%s], but [%s] was expected. Cause: ",
attributeName, value.getClass().getSimpleName(), expectedType.getSimpleName()));
return (T) value;
}
}

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.
@@ -20,7 +20,6 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
@@ -300,25 +299,58 @@ public abstract class AnnotationUtils {
}
/**
* Retrieve the given annotation's attributes as a Map, preserving all attribute types as-is.
* Retrieve the given annotation's attributes as a Map, preserving all attribute types
* as-is.
* <p>Note: As of Spring 3.1.1, the returned map is actually an
* {@link AnnotationAttributes} instance, however the Map signature of this method has
* been preserved for binary compatibility.
* @param annotation the annotation to retrieve the attributes for
* @return the Map of annotation attributes, with attribute names as keys and
* corresponding attribute values as values
*/
public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
return getAnnotationAttributes(annotation, false);
return getAnnotationAttributes(annotation, false, false);
}
/**
* Retrieve the given annotation's attributes as a Map.
* Retrieve the given annotation's attributes as a Map. Equivalent to calling
* {@link #getAnnotationAttributes(Annotation, boolean, boolean)} with
* the {@code nestedAnnotationsAsMap} parameter set to {@code false}.
* <p>Note: As of Spring 3.1.1, the returned map is actually an
* {@link AnnotationAttributes} instance, however the Map signature of this method has
* been preserved for binary compatibility.
* @param annotation the annotation to retrieve the attributes for
* @param classValuesAsString whether to turn Class references into Strings (for compatibility with
* {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as Class references
* @param classValuesAsString whether to turn Class references into Strings (for
* compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to
* preserve them as Class references
* @return the Map of annotation attributes, with attribute names as keys and
* corresponding attribute values as values
*/
public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {
Map<String, Object> attrs = new HashMap<String, Object>();
return getAnnotationAttributes(annotation, classValuesAsString, false);
}
/**
* Retrieve the given annotation's attributes as an {@link AnnotationAttributes}
* map structure. Implemented in Spring 3.1.1 to provide fully recursive annotation
* reading capabilities on par with that of the reflection-based
* {@link org.springframework.core.type.StandardAnnotationMetadata}.
* @param annotation the annotation to retrieve the attributes for
* @param classValuesAsString whether to turn Class references into Strings (for
* compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to
* preserve them as Class references
* @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
* {@link AnnotationAttributes} maps (for compatibility with
* {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as
* Annotation instances
* @return the annotation attributes (a specialized Map) with attribute names as keys
* and corresponding attribute values as values
* @since 3.1.1
*/
public static AnnotationAttributes getAnnotationAttributes(
Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
AnnotationAttributes attrs = new AnnotationAttributes();
Method[] methods = annotation.annotationType().getDeclaredMethods();
for (Method method : methods) {
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
@@ -337,7 +369,22 @@ public abstract class AnnotationUtils {
value = newValue;
}
}
attrs.put(method.getName(), value);
if (nestedAnnotationsAsMap && value instanceof Annotation) {
attrs.put(method.getName(), getAnnotationAttributes(
(Annotation)value, classValuesAsString, nestedAnnotationsAsMap));
}
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
Annotation[] realAnnotations = (Annotation[])value;
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
for (int i = 0; i < realAnnotations.length; i++) {
mappedAnnotations[i] = getAnnotationAttributes(
realAnnotations[i], classValuesAsString, nestedAnnotationsAsMap);
}
attrs.put(method.getName(), mappedAnnotations);
}
else {
attrs.put(method.getName(), value);
}
}
catch (Exception ex) {
throw new IllegalStateException("Could not obtain annotation attribute values", 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert;
import java.lang.annotation.Annotation;
@@ -141,13 +142,20 @@ public final class Property {
private MethodParameter resolveMethodParameter() {
MethodParameter read = resolveReadMethodParameter();
MethodParameter write = resolveWriteMethodParameter();
if (read == null && write == null) {
throw new IllegalStateException("Property is neither readable nor writeable");
if (write == null) {
if (read == null) {
throw new IllegalStateException("Property is neither readable nor writeable");
}
return read;
}
if (read != null && write != null && !write.getParameterType().isAssignableFrom(read.getParameterType())) {
throw new IllegalStateException("Write parameter is not assignable from read parameter");
if (read != null) {
Class<?> readType = read.getParameterType();
Class<?> writeType = write.getParameterType();
if (!writeType.equals(readType) && writeType.isAssignableFrom(readType)) {
return read;
}
}
return read != null ? read : write;
return write;
}
private MethodParameter resolveReadMethodParameter() {

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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert;
import java.lang.annotation.Annotation;
@@ -59,6 +60,7 @@ public class TypeDescriptor {
typeDescriptorCache.put(String.class, new TypeDescriptor(String.class));
}
private final Class<?> type;
private final TypeDescriptor elementTypeDescriptor;
@@ -69,6 +71,7 @@ public class TypeDescriptor {
private final Annotation[] annotations;
/**
* Create a new type descriptor from a {@link MethodParameter}.
* Use this constructor when a source or target conversion point is a constructor parameter, method parameter, or method return value.
@@ -96,6 +99,7 @@ public class TypeDescriptor {
this(new BeanPropertyDescriptor(property));
}
/**
* Create a new type descriptor from the given type.
* Use this to instruct the conversion system to convert an object to a specific target type, when no type location such as a method parameter or field is available to provide additional conversion context.
@@ -207,6 +211,7 @@ public class TypeDescriptor {
return (source != null ? valueOf(source.getClass()) : null);
}
/**
* The type of the backing class, method parameter, field, or property described by this TypeDescriptor.
* Returns primitive types as-is.
@@ -477,6 +482,7 @@ public class TypeDescriptor {
private TypeDescriptor(Class<?> type, TypeDescriptor elementTypeDescriptor, TypeDescriptor mapKeyTypeDescriptor,
TypeDescriptor mapValueTypeDescriptor, Annotation[] annotations) {
this.type = type;
this.elementTypeDescriptor = elementTypeDescriptor;
this.mapKeyTypeDescriptor = mapKeyTypeDescriptor;
@@ -538,15 +544,24 @@ public class TypeDescriptor {
return false;
}
TypeDescriptor other = (TypeDescriptor) obj;
boolean annotatedTypeEquals = ObjectUtils.nullSafeEquals(getType(), other.getType()) && ObjectUtils.nullSafeEquals(getAnnotations(), other.getAnnotations());
if (!annotatedTypeEquals) {
if (!ObjectUtils.nullSafeEquals(getType(), other.getType())) {
return false;
}
Annotation[] annotations = getAnnotations();
if (annotations.length != other.getAnnotations().length) {
return false;
}
for (Annotation ann : annotations) {
if (other.getAnnotation(ann.annotationType()) == null) {
return false;
}
}
if (isCollection() || isArray()) {
return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), other.getElementTypeDescriptor());
}
else if (isMap()) {
return ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), other.getMapKeyTypeDescriptor()) && ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), other.getMapValueTypeDescriptor());
return ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), other.getMapKeyTypeDescriptor()) &&
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), other.getMapValueTypeDescriptor());
}
else {
return true;

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.
@@ -150,11 +150,11 @@ public abstract class AbstractResource implements Resource {
}
/**
* This implementation always throws IllegalStateException,
* assuming that the resource does not have a filename.
* This implementation always returns <code>null</code>,
* assuming that this resource type does not have a filename.
*/
public String getFilename() throws IllegalStateException {
throw new IllegalStateException(getDescription() + " does not have a filename");
return null;
}

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.
@@ -116,8 +116,10 @@ public interface Resource extends InputStreamSource {
Resource createRelative(String relativePath) throws IOException;
/**
* Return a filename for this resource, i.e. typically the last
* Determine a filename for this resource, i.e. typically the last
* part of the path: for example, "myfile.txt".
* <p>Returns <code>null</code> if this type of resource does not
* have a filename.
*/
String getFilename();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -27,7 +27,7 @@ import org.springframework.util.Assert;
/**
* VFS based {@link Resource} implementation.
* Supports the corresponding VFS API versions on JBoss AS 5.x as well as 6.x.
* Supports the corresponding VFS API versions on JBoss AS 5.x as well as 6.x and 7.x.
*
* @author Ales Justin
* @author Juergen Hoeller
@@ -47,22 +47,21 @@ public class VfsResource extends AbstractResource {
}
public boolean exists() {
return VfsUtils.exists(this.resource);
}
public boolean isReadable() {
return VfsUtils.isReadable(this.resource);
}
public long lastModified() throws IOException {
return VfsUtils.getLastModified(this.resource);
}
public InputStream getInputStream() throws IOException {
return VfsUtils.getInputStream(this.resource);
}
@Override
public boolean exists() {
return VfsUtils.exists(this.resource);
}
@Override
public boolean isReadable() {
return VfsUtils.isReadable(this.resource);
}
@Override
public URL getURL() throws IOException {
try {
return VfsUtils.getURL(this.resource);
@@ -72,6 +71,7 @@ public class VfsResource extends AbstractResource {
}
}
@Override
public URI getURI() throws IOException {
try {
return VfsUtils.getURI(this.resource);
@@ -81,10 +81,17 @@ public class VfsResource extends AbstractResource {
}
}
@Override
public File getFile() throws IOException {
return VfsUtils.getFile(this.resource);
}
@Override
public long lastModified() throws IOException {
return VfsUtils.getLastModified(this.resource);
}
@Override
public Resource createRelative(String relativePath) throws IOException {
if (!relativePath.startsWith(".") && relativePath.contains("/")) {
try {
@@ -98,6 +105,7 @@ public class VfsResource extends AbstractResource {
return new VfsResource(VfsUtils.getRelative(new URL(getURL(), relativePath)));
}
@Override
public String getFilename() {
return VfsUtils.getName(this.resource);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 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,7 @@ import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.AbstractFileResolvingResource;
import org.springframework.core.io.Resource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DefaultPropertiesPersister;
@@ -179,9 +179,7 @@ public abstract class PropertiesLoaderSupport {
InputStream is = null;
try {
is = location.getInputStream();
String filename = (location instanceof AbstractFileResolvingResource) ?
location.getFilename() : null;
String filename = location.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
this.propertiesPersister.loadFromXml(props, is);
}

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.
@@ -22,24 +22,45 @@ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
/**
* {@link AnnotationMetadata} implementation that uses standard reflection
* to introspect a given <code>Class</code>.
* to introspect a given {@link Class}.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Chris Beams
* @since 2.5
*/
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
private final boolean nestedAnnotationsAsMap;
/**
* Create a new StandardAnnotationMetadata wrapper for the given Class.
* Create a new {@code StandardAnnotationMetadata} wrapper for the given Class.
* @param introspectedClass the Class to introspect
* @see #StandardAnnotationMetadata(Class, boolean)
*/
public StandardAnnotationMetadata(Class introspectedClass) {
public StandardAnnotationMetadata(Class<?> introspectedClass) {
this(introspectedClass, false);
}
/**
* Create a new {@link StandardAnnotationMetadata} wrapper for the given Class,
* providing the option to return any nested annotations or annotation arrays in the
* form of {@link AnnotationAttributes} instead of actual {@link Annotation} instances.
* @param introspectedClass the Class to instrospect
* @param nestedAnnotationsAsMap return nested annotations and annotation arrays as
* {@link AnnotationAttributes} for compatibility with ASM-based
* {@link AnnotationMetadata} implementations
* @since 3.1.1
*/
public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
super(introspectedClass);
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
}
@@ -114,18 +135,20 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
}
public Map<String, Object> getAnnotationAttributes(String annotationType) {
return getAnnotationAttributes(annotationType, false);
return this.getAnnotationAttributes(annotationType, false);
}
public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
Annotation[] anns = getIntrospectedClass().getAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(ann, classValuesAsString);
return AnnotationUtils.getAnnotationAttributes(
ann, classValuesAsString, this.nestedAnnotationsAsMap);
}
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(metaAnn, classValuesAsString);
return AnnotationUtils.getAnnotationAttributes(
metaAnn, classValuesAsString, this.nestedAnnotationsAsMap);
}
}
}
@@ -157,13 +180,13 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
for (Method method : methods) {
for (Annotation ann : method.getAnnotations()) {
if (ann.annotationType().getName().equals(annotationType)) {
annotatedMethods.add(new StandardMethodMetadata(method));
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
break;
}
else {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
annotatedMethods.add(new StandardMethodMetadata(method));
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
break;
}
}

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.
@@ -37,14 +37,27 @@ public class StandardMethodMetadata implements MethodMetadata {
private final Method introspectedMethod;
private final boolean nestedAnnotationsAsMap;
/**
* Create a new StandardMethodMetadata wrapper for the given Method.
* @param introspectedMethod the Method to introspect
*/
public StandardMethodMetadata(Method introspectedMethod) {
this(introspectedMethod, false);
}
/**
* Create a new StandardMethodMetadata wrapper for the given Method.
* @param introspectedMethod the Method to introspect
* @param nestedAnnotationsAsMap
* @since 3.1.1
*/
public StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap) {
Assert.notNull(introspectedMethod, "Method must not be null");
this.introspectedMethod = introspectedMethod;
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
}
/**
@@ -94,11 +107,13 @@ public class StandardMethodMetadata implements MethodMetadata {
Annotation[] anns = this.introspectedMethod.getAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(ann, true);
return AnnotationUtils.getAnnotationAttributes(
ann, true, nestedAnnotationsAsMap);
}
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(metaAnn, true);
return AnnotationUtils.getAnnotationAttributes(
metaAnn, true, this.nestedAnnotationsAsMap);
}
}
}

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.
@@ -20,131 +20,242 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.Type;
import org.springframework.asm.commons.EmptyVisitor;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* ASM visitor which looks for the annotations defined on a class or method.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
* @since 3.1.1
*/
final class AnnotationAttributesReadingVisitor implements AnnotationVisitor {
abstract class AbstractRecursiveAnnotationVisitor implements AnnotationVisitor {
private final String annotationType;
protected final Log logger = LogFactory.getLog(this.getClass());
private final Map<String, Map<String, Object>> attributesMap;
protected final AnnotationAttributes attributes;
private final Map<String, Set<String>> metaAnnotationMap;
private final ClassLoader classLoader;
private final Map<String, Object> localAttributes = new LinkedHashMap<String, Object>();
protected final ClassLoader classLoader;
public AnnotationAttributesReadingVisitor(
String annotationType, Map<String, Map<String, Object>> attributesMap,
Map<String, Set<String>> metaAnnotationMap, ClassLoader classLoader) {
this.annotationType = annotationType;
this.attributesMap = attributesMap;
this.metaAnnotationMap = metaAnnotationMap;
public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) {
this.classLoader = classLoader;
this.attributes = attributes;
}
public void visit(String name, Object value) {
this.localAttributes.put(name, value);
public void visit(String attributeName, Object attributeValue) {
this.attributes.put(attributeName, attributeValue);
}
public void visitEnum(String name, String desc, String value) {
Object valueToUse = value;
public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) {
String annotationType = Type.getType(asmTypeDescriptor).getClassName();
AnnotationAttributes nestedAttributes = new AnnotationAttributes();
this.attributes.put(attributeName, nestedAttributes);
return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader);
}
public AnnotationVisitor visitArray(String attributeName) {
return new RecursiveAnnotationArrayVisitor(attributeName, this.attributes, this.classLoader);
}
public void visitEnum(String attributeName, String asmTypeDescriptor, String attributeValue) {
Object valueToUse = attributeValue;
try {
Class<?> enumType = this.classLoader.loadClass(Type.getType(desc).getClassName());
Field enumConstant = ReflectionUtils.findField(enumType, value);
Class<?> enumType = this.classLoader.loadClass(Type.getType(asmTypeDescriptor).getClassName());
Field enumConstant = ReflectionUtils.findField(enumType, attributeValue);
if (enumConstant != null) {
valueToUse = enumConstant.get(null);
}
}
catch (Exception ex) {
// Class not found - can't resolve class reference in annotation attribute.
logNonFatalException(ex);
}
this.localAttributes.put(name, valueToUse);
this.attributes.put(attributeName, valueToUse);
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
return new EmptyVisitor();
protected void logNonFatalException(Exception ex) {
this.logger.warn("Failed to classload type while reading annotation metadata. " +
"This is a non-fatal error, but certain annotation metadata may be " +
"unavailable.", ex);
}
}
/**
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1.1
*/
final class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationVisitor {
private final String attributeName;
private final List<AnnotationAttributes> allNestedAttributes = new ArrayList<AnnotationAttributes>();
public RecursiveAnnotationArrayVisitor(
String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) {
super(classLoader, attributes);
this.attributeName = attributeName;
}
public AnnotationVisitor visitArray(final String attrName) {
return new AnnotationVisitor() {
public void visit(String name, Object value) {
Object newValue = value;
Object existingValue = localAttributes.get(attrName);
if (existingValue != null) {
newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue);
}
else {
Object[] newArray = (Object[]) Array.newInstance(newValue.getClass(), 1);
newArray[0] = newValue;
newValue = newArray;
}
localAttributes.put(attrName, newValue);
}
public void visitEnum(String name, String desc, String value) {
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
return new EmptyVisitor();
}
public AnnotationVisitor visitArray(String name) {
return new EmptyVisitor();
}
public void visitEnd() {
}
};
@Override
public void visit(String attributeName, Object attributeValue) {
Object newValue = attributeValue;
Object existingValue = this.attributes.get(this.attributeName);
if (existingValue != null) {
newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue);
}
else {
Object[] newArray = (Object[]) Array.newInstance(newValue.getClass(), 1);
newArray[0] = newValue;
newValue = newArray;
}
this.attributes.put(this.attributeName, newValue);
}
@Override
public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) {
String annotationType = Type.getType(asmTypeDescriptor).getClassName();
AnnotationAttributes nestedAttributes = new AnnotationAttributes();
this.allNestedAttributes.add(nestedAttributes);
return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader);
}
public void visitEnd() {
this.attributesMap.put(this.annotationType, this.localAttributes);
if (!this.allNestedAttributes.isEmpty()) {
this.attributes.put(this.attributeName, this.allNestedAttributes.toArray(
new AnnotationAttributes[this.allNestedAttributes.size()]));
}
}
}
/**
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1.1
*/
class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor {
private final String annotationType;
public RecursiveAnnotationAttributesVisitor(
String annotationType, AnnotationAttributes attributes, ClassLoader classLoader) {
super(classLoader, attributes);
this.annotationType = annotationType;
}
public final void visitEnd() {
try {
Class<?> annotationClass = this.classLoader.loadClass(this.annotationType);
// Check declared default values of attributes in the annotation type.
Method[] annotationAttributes = annotationClass.getMethods();
for (Method annotationAttribute : annotationAttributes) {
String attributeName = annotationAttribute.getName();
Object defaultValue = annotationAttribute.getDefaultValue();
if (defaultValue != null && !this.localAttributes.containsKey(attributeName)) {
this.localAttributes.put(attributeName, defaultValue);
}
}
// Register annotations that the annotation type is annotated with.
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>();
for (Annotation metaAnnotation : annotationClass.getAnnotations()) {
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
if (!this.attributesMap.containsKey(metaAnnotation.annotationType().getName())) {
this.attributesMap.put(metaAnnotation.annotationType().getName(),
AnnotationUtils.getAnnotationAttributes(metaAnnotation, true));
}
for (Annotation metaMetaAnnotation : metaAnnotation.annotationType().getAnnotations()) {
metaAnnotationTypeNames.add(metaMetaAnnotation.annotationType().getName());
}
}
if (this.metaAnnotationMap != null) {
this.metaAnnotationMap.put(this.annotationType, metaAnnotationTypeNames);
}
this.doVisitEnd(annotationClass);
}
catch (ClassNotFoundException ex) {
// Class not found - can't determine meta-annotations.
logNonFatalException(ex);
}
}
protected void doVisitEnd(Class<?> annotationClass) {
registerDefaultValues(annotationClass);
}
private void registerDefaultValues(Class<?> annotationClass) {
// Check declared default values of attributes in the annotation type.
Method[] annotationAttributes = annotationClass.getMethods();
for (Method annotationAttribute : annotationAttributes) {
String attributeName = annotationAttribute.getName();
Object defaultValue = annotationAttribute.getDefaultValue();
if (defaultValue != null && !this.attributes.containsKey(attributeName)) {
if (defaultValue instanceof Annotation) {
defaultValue = AnnotationAttributes.fromMap(
AnnotationUtils.getAnnotationAttributes((Annotation)defaultValue, false, true));
}
else if (defaultValue instanceof Annotation[]) {
Annotation[] realAnnotations = (Annotation[]) defaultValue;
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
for (int i = 0; i < realAnnotations.length; i++) {
mappedAnnotations[i] = AnnotationAttributes.fromMap(
AnnotationUtils.getAnnotationAttributes(realAnnotations[i], false, true));
}
defaultValue = mappedAnnotations;
}
this.attributes.put(attributeName, defaultValue);
}
}
}
}
/**
* ASM visitor which looks for the annotations defined on a class or method, including
* tracking meta-annotations.
*
* <p>As of Spring 3.1.1, this visitor is fully recursive, taking into account any nested
* annotations or nested annotation arrays. These annotations are in turn read into
* {@link AnnotationAttributes} map structures.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 3.0
*/
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
private final String annotationType;
private final Map<String, AnnotationAttributes> attributesMap;
private final Map<String, Set<String>> metaAnnotationMap;
public AnnotationAttributesReadingVisitor(
String annotationType, Map<String, AnnotationAttributes> attributesMap,
Map<String, Set<String>> metaAnnotationMap, ClassLoader classLoader) {
super(annotationType, new AnnotationAttributes(), classLoader);
this.annotationType = annotationType;
this.attributesMap = attributesMap;
this.metaAnnotationMap = metaAnnotationMap;
}
@Override
public void doVisitEnd(Class<?> annotationClass) {
super.doVisitEnd(annotationClass);
this.attributesMap.put(this.annotationType, this.attributes);
registerMetaAnnotations(annotationClass);
}
private void registerMetaAnnotations(Class<?> annotationClass) {
// Register annotations that the annotation type is annotated with.
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>();
for (Annotation metaAnnotation : annotationClass.getAnnotations()) {
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
if (!this.attributesMap.containsKey(metaAnnotation.annotationType().getName())) {
this.attributesMap.put(metaAnnotation.annotationType().getName(),
AnnotationUtils.getAnnotationAttributes(metaAnnotation, true, true));
}
for (Annotation metaMetaAnnotation : metaAnnotation.annotationType().getAnnotations()) {
metaAnnotationTypeNames.add(metaMetaAnnotation.annotationType().getName());
}
}
if (this.metaAnnotationMap != null) {
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
}
}
}

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.
@@ -26,6 +26,7 @@ import java.util.Set;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.CollectionUtils;
@@ -50,7 +51,7 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>(4);
private final Map<String, Map<String, Object>> attributeMap = new LinkedHashMap<String, Map<String, Object>>(4);
private final Map<String, AnnotationAttributes> attributeMap = new LinkedHashMap<String, AnnotationAttributes>(4);
private final MultiValueMap<String, MethodMetadata> methodMetadataMap = new LinkedMultiValueMap<String, MethodMetadata>();
@@ -99,20 +100,41 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
return this.attributeMap.containsKey(annotationType);
}
public Map<String, Object> getAnnotationAttributes(String annotationType) {
public AnnotationAttributes getAnnotationAttributes(String annotationType) {
return getAnnotationAttributes(annotationType, false);
}
public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
Map<String, Object> raw = this.attributeMap.get(annotationType);
if (raw == null) {
public AnnotationAttributes getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
return getAnnotationAttributes(annotationType, classValuesAsString, false);
}
public AnnotationAttributes getAnnotationAttributes(
String annotationType, boolean classValuesAsString, boolean nestedAttributesAsMap) {
AnnotationAttributes raw = this.attributeMap.get(annotationType);
return convertClassValues(raw, classValuesAsString, nestedAttributesAsMap);
}
private AnnotationAttributes convertClassValues(
AnnotationAttributes original, boolean classValuesAsString, boolean nestedAttributesAsMap) {
if (original == null) {
return null;
}
Map<String, Object> result = new LinkedHashMap<String, Object>(raw.size());
for (Map.Entry<String, Object> entry : raw.entrySet()) {
AnnotationAttributes result = new AnnotationAttributes(original.size());
for (Map.Entry<String, Object> entry : original.entrySet()) {
try {
Object value = entry.getValue();
if (value instanceof Type) {
if (value instanceof AnnotationAttributes) {
value = convertClassValues((AnnotationAttributes)value, classValuesAsString, nestedAttributesAsMap);
}
else if (value instanceof AnnotationAttributes[]) {
AnnotationAttributes[] values = (AnnotationAttributes[])value;
for (int i = 0; i < values.length; i++) {
values[i] = convertClassValues(values[i], classValuesAsString, nestedAttributesAsMap);
}
}
else if (value instanceof Type) {
value = (classValuesAsString ? ((Type) value).getClassName() :
this.classLoader.loadClass(((Type) value).getClassName()));
}
@@ -127,10 +149,10 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
}
else if (classValuesAsString) {
if (value instanceof Class) {
value = ((Class) value).getName();
value = ((Class<?>) value).getName();
}
else if (value instanceof Class[]) {
Class[] clazzArray = (Class[]) value;
Class<?>[] clazzArray = (Class[]) value;
String[] newValue = new String[clazzArray.length];
for (int i = 0; i < clazzArray.length; i++) {
newValue[i] = clazzArray[i].getName();

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.
@@ -24,6 +24,7 @@ import org.springframework.asm.MethodAdapter;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.asm.commons.EmptyVisitor;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.MultiValueMap;
@@ -35,6 +36,7 @@ import org.springframework.util.MultiValueMap;
* @author Juergen Hoeller
* @author Mark Pollack
* @author Costin Leau
* @author Chris Beams
* @since 3.0
*/
final class MethodMetadataReadingVisitor extends MethodAdapter implements MethodMetadata {
@@ -49,7 +51,7 @@ final class MethodMetadataReadingVisitor extends MethodAdapter implements Method
private final MultiValueMap<String, MethodMetadata> methodMetadataMap;
private final Map<String, Map<String, Object>> attributeMap = new LinkedHashMap<String, Map<String, Object>>(2);
private final Map<String, AnnotationAttributes> attributeMap = new LinkedHashMap<String, AnnotationAttributes>(2);
public MethodMetadataReadingVisitor(String name, int access, String declaringClassName, ClassLoader classLoader,
MultiValueMap<String, MethodMetadata> methodMetadataMap) {
@@ -88,7 +90,7 @@ final class MethodMetadataReadingVisitor extends MethodAdapter implements Method
return this.attributeMap.containsKey(annotationType);
}
public Map<String, Object> getAnnotationAttributes(String annotationType) {
public AnnotationAttributes getAnnotationAttributes(String annotationType) {
return this.attributeMap.get(annotationType);
}

View File

@@ -22,6 +22,7 @@ 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;
@@ -712,6 +713,9 @@ public abstract class ClassUtils {
* Call {@link org.springframework.core.BridgeMethodResolver#findBridgedMethod}
* if bridge method resolution is desirable (e.g. for obtaining metadata from
* the original method definition).
* <p><b>NOTE:</b>Since Spring 3.1.1, if java security settings disallow reflective
* access (e.g. calls to {@code Class#getDeclaredMethods} etc, this implementation
* will fall back to returning the originally provided method.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation.
* May be <code>null</code> or may not even implement the method.
@@ -722,7 +726,12 @@ public abstract class ClassUtils {
Method specificMethod = null;
if (method != null && isOverridable(method, targetClass) &&
targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
try {
specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
} catch (AccessControlException ex) {
// security settings are disallowing reflective access; leave
// 'specificMethod' null and fall back to 'method' below
}
}
return (specificMethod != null ? specificMethod : method);
}

View File

@@ -0,0 +1,110 @@
/*
* 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.core.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 org.junit.Test;
/**
* Unit tests for {@link AnnotationAttributes}.
*
* @author Chris Beams
* @since 3.1.1
*/
public class AnnotationAttributesTests {
enum Color { RED, WHITE, BLUE }
@Test
public void testTypeSafeAttributeAccess() {
AnnotationAttributes a = new AnnotationAttributes();
a.put("name", "dave");
a.put("names", new String[] { "dave", "frank", "hal" });
a.put("bool1", true);
a.put("bool2", false);
a.put("color", Color.RED);
a.put("clazz", Integer.class);
a.put("classes", new Class<?>[] { Number.class, Short.class, Integer.class });
a.put("number", 42);
a.put("numbers", new int[] { 42, 43 });
AnnotationAttributes anno = new AnnotationAttributes();
anno.put("value", 10);
anno.put("name", "algernon");
a.put("anno", anno);
a.put("annoArray", new AnnotationAttributes[] { anno });
assertThat(a.getString("name"), equalTo("dave"));
assertThat(a.getStringArray("names"), equalTo(new String[] { "dave", "frank", "hal" }));
assertThat(a.getBoolean("bool1"), equalTo(true));
assertThat(a.getBoolean("bool2"), equalTo(false));
assertThat(a.<Color>getEnum("color"), equalTo(Color.RED));
assertTrue(a.getClass("clazz").equals(Integer.class));
assertThat(a.getClassArray("classes"), equalTo(new Class[] { Number.class, Short.class, Integer.class }));
assertThat(a.<Integer>getNumber("number"), equalTo(42));
assertThat(a.getAnnotation("anno").<Integer>getNumber("value"), equalTo(10));
assertThat(a.getAnnotationArray("annoArray")[0].getString("name"), equalTo("algernon"));
}
@Test
public void getEnum_emptyAttributeName() {
AnnotationAttributes a = new AnnotationAttributes();
a.put("color", "RED");
try {
a.getEnum("");
fail();
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), equalTo("attributeName must not be null or empty"));
}
try {
a.getEnum(null);
fail();
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), equalTo("attributeName must not be null or empty"));
}
}
@Test
public void getEnum_notFound() {
AnnotationAttributes a = new AnnotationAttributes();
a.put("color", "RED");
try {
a.getEnum("colour");
fail();
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), equalTo("Attribute 'colour' not found"));
}
}
@Test
public void getEnum_typeMismatch() {
AnnotationAttributes a = new AnnotationAttributes();
a.put("color", "RED");
try {
a.getEnum("color");
fail();
} catch (IllegalArgumentException ex) {
String expected =
"Attribute 'color' is of type [String], but [Enum] was expected";
assertThat(ex.getMessage().substring(0, expected.length()), equalTo(expected));
}
}
}

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,98 +16,203 @@
package org.springframework.core.type;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import java.util.Set;
import junit.framework.TestCase;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.stereotype.Component;
/**
* Unit tests demonstrating that the reflection-based {@link StandardAnnotationMetadata}
* and ASM-based {@code AnnotationMetadataReadingVisitor} produce identical output.
*
* @author Juergen Hoeller
* @author Chris Beams
*/
public class AnnotationMetadataTests extends TestCase {
public class AnnotationMetadataTests {
@Test
public void testStandardAnnotationMetadata() throws IOException {
StandardAnnotationMetadata annInfo = new StandardAnnotationMetadata(AnnotatedComponent.class);
doTestAnnotationInfo(annInfo);
doTestMethodAnnotationInfo(annInfo);
AnnotationMetadata metadata = new StandardAnnotationMetadata(AnnotatedComponent.class, true);
doTestAnnotationInfo(metadata);
doTestMethodAnnotationInfo(metadata);
}
@Test
public void testAsmAnnotationMetadata() throws IOException {
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(AnnotatedComponent.class.getName());
doTestAnnotationInfo(metadataReader.getAnnotationMetadata());
doTestMethodAnnotationInfo(metadataReader.getAnnotationMetadata());
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
doTestAnnotationInfo(metadata);
doTestMethodAnnotationInfo(metadata);
}
/**
* In order to preserve backward-compatibility, {@link StandardAnnotationMetadata}
* defaults to return nested annotations and annotation arrays as actual
* Annotation instances. It is recommended for compatibility with ASM-based
* AnnotationMetadata implementations to set the 'nestedAnnotationsAsMap' flag to
* 'true' as is done in the main test above.
*/
@Test
public void testStandardAnnotationMetadata_nestedAnnotationsAsMap_false() throws IOException {
AnnotationMetadata metadata = new StandardAnnotationMetadata(AnnotatedComponent.class);
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName());
Annotation[] nestedAnnoArray = (Annotation[])specialAttrs.get("nestedAnnoArray");
assertThat(nestedAnnoArray[0], instanceOf(NestedAnno.class));
}
private void doTestAnnotationInfo(AnnotationMetadata metadata) {
assertEquals(AnnotatedComponent.class.getName(), metadata.getClassName());
assertFalse(metadata.isInterface());
assertFalse(metadata.isAbstract());
assertTrue(metadata.isConcrete());
assertTrue(metadata.hasSuperClass());
assertEquals(Object.class.getName(), metadata.getSuperClassName());
assertEquals(1, metadata.getInterfaceNames().length);
assertEquals(Serializable.class.getName(), metadata.getInterfaceNames()[0]);
assertThat(metadata.getClassName(), is(AnnotatedComponent.class.getName()));
assertThat(metadata.isInterface(), is(false));
assertThat(metadata.isAbstract(), is(false));
assertThat(metadata.isConcrete(), is(true));
assertThat(metadata.hasSuperClass(), is(true));
assertThat(metadata.getSuperClassName(), is(Object.class.getName()));
assertThat(metadata.getInterfaceNames().length, is(1));
assertThat(metadata.getInterfaceNames()[0], is(Serializable.class.getName()));
assertTrue(metadata.hasAnnotation(Component.class.getName()));
assertTrue(metadata.hasAnnotation(Scope.class.getName()));
assertTrue(metadata.hasAnnotation(SpecialAttr.class.getName()));
assertEquals(3, metadata.getAnnotationTypes().size());
assertTrue(metadata.getAnnotationTypes().contains(Component.class.getName()));
assertTrue(metadata.getAnnotationTypes().contains(Scope.class.getName()));
assertTrue(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()));
assertThat(metadata.hasAnnotation(Component.class.getName()), is(true));
assertThat(metadata.hasAnnotation(Scope.class.getName()), is(true));
assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().size(), is(3));
assertThat(metadata.getAnnotationTypes().contains(Component.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().contains(Scope.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()), is(true));
Map<String, Object> compAttrs = metadata.getAnnotationAttributes(Component.class.getName());
assertEquals(1, compAttrs.size());
assertEquals("myName", compAttrs.get("value"));
Map<String, Object> scopeAttrs = metadata.getAnnotationAttributes(Scope.class.getName());
assertEquals(1, scopeAttrs.size());
assertEquals("myScope", scopeAttrs.get("value"));
Map<String, Object> specialAttrs = metadata.getAnnotationAttributes(SpecialAttr.class.getName());
assertEquals(2, specialAttrs.size());
assertEquals(String.class, specialAttrs.get("clazz"));
assertEquals(Thread.State.NEW, specialAttrs.get("state"));
Map<String, Object> specialAttrsString = metadata.getAnnotationAttributes(SpecialAttr.class.getName(), true);
assertEquals(String.class.getName(), specialAttrsString .get("clazz"));
assertEquals(Thread.State.NEW, specialAttrsString.get("state"));
AnnotationAttributes compAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Component.class.getName());
assertThat(compAttrs.size(), is(1));
assertThat(compAttrs.getString("value"), is("myName"));
AnnotationAttributes scopeAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Scope.class.getName());
assertThat(scopeAttrs.size(), is(1));
assertThat(scopeAttrs.getString("value"), is("myScope"));
{ // perform tests with classValuesAsString = false (the default)
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName());
assertThat(specialAttrs.size(), is(6));
assertTrue(String.class.isAssignableFrom(specialAttrs.getClass("clazz")));
assertTrue(specialAttrs.getEnum("state").equals(Thread.State.NEW));
AnnotationAttributes nestedAnno = specialAttrs.getAnnotation("nestedAnno");
assertThat("na", is(nestedAnno.getString("value")));
assertTrue(nestedAnno.getEnum("anEnum").equals(SomeEnum.LABEL1));
assertArrayEquals(new Class[]{String.class}, (Class[])nestedAnno.get("classArray"));
AnnotationAttributes[] nestedAnnoArray = specialAttrs.getAnnotationArray("nestedAnnoArray");
assertThat(nestedAnnoArray.length, is(2));
assertThat(nestedAnnoArray[0].getString("value"), is("default"));
assertTrue(nestedAnnoArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT));
assertArrayEquals(new Class[]{Void.class}, (Class[])nestedAnnoArray[0].get("classArray"));
assertThat(nestedAnnoArray[1].getString("value"), is("na1"));
assertTrue(nestedAnnoArray[1].getEnum("anEnum").equals(SomeEnum.LABEL2));
assertArrayEquals(new Class[]{Number.class}, (Class[])nestedAnnoArray[1].get("classArray"));
assertArrayEquals(new Class[]{Number.class}, nestedAnnoArray[1].getClassArray("classArray"));
AnnotationAttributes optional = specialAttrs.getAnnotation("optional");
assertThat(optional.getString("value"), is("optional"));
assertTrue(optional.getEnum("anEnum").equals(SomeEnum.DEFAULT));
assertArrayEquals(new Class[]{Void.class}, (Class[])optional.get("classArray"));
assertArrayEquals(new Class[]{Void.class}, optional.getClassArray("classArray"));
AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray");
assertThat(optionalArray.length, is(1));
assertThat(optionalArray[0].getString("value"), is("optional"));
assertTrue(optionalArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT));
assertArrayEquals(new Class[]{Void.class}, (Class[])optionalArray[0].get("classArray"));
assertArrayEquals(new Class[]{Void.class}, optionalArray[0].getClassArray("classArray"));
}
{ // perform tests with classValuesAsString = true
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName(), true);
assertThat(specialAttrs.size(), is(6));
assertThat(specialAttrs.get("clazz"), is((Object)String.class.getName()));
assertThat(specialAttrs.getString("clazz"), is(String.class.getName()));
AnnotationAttributes nestedAnno = specialAttrs.getAnnotation("nestedAnno");
assertArrayEquals(new String[]{String.class.getName()}, (String[])nestedAnno.getStringArray("classArray"));
assertArrayEquals(new String[]{String.class.getName()}, nestedAnno.getStringArray("classArray"));
AnnotationAttributes[] nestedAnnoArray = specialAttrs.getAnnotationArray("nestedAnnoArray");
assertArrayEquals(new String[]{Void.class.getName()}, (String[])nestedAnnoArray[0].get("classArray"));
assertArrayEquals(new String[]{Void.class.getName()}, nestedAnnoArray[0].getStringArray("classArray"));
assertArrayEquals(new String[]{Number.class.getName()}, (String[])nestedAnnoArray[1].get("classArray"));
assertArrayEquals(new String[]{Number.class.getName()}, nestedAnnoArray[1].getStringArray("classArray"));
AnnotationAttributes optional = specialAttrs.getAnnotation("optional");
assertArrayEquals(new String[]{Void.class.getName()}, (String[])optional.get("classArray"));
assertArrayEquals(new String[]{Void.class.getName()}, optional.getStringArray("classArray"));
AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray");
assertArrayEquals(new String[]{Void.class.getName()}, (String[])optionalArray[0].get("classArray"));
assertArrayEquals(new String[]{Void.class.getName()}, optionalArray[0].getStringArray("classArray"));
}
}
private void doTestMethodAnnotationInfo(AnnotationMetadata classMetadata) {
Set<MethodMetadata> methods = classMetadata.getAnnotatedMethods(Autowired.class.getName());
assertEquals(1, methods.size());
assertThat(methods.size(), is(1));
for (MethodMetadata methodMetadata : methods) {
assertTrue(methodMetadata.isAnnotated(Autowired.class.getName()));
assertThat(methodMetadata.isAnnotated(Autowired.class.getName()), is(true));
}
}
public static enum SomeEnum {
LABEL1, LABEL2, DEFAULT;
}
@Target({})
@Retention(RetentionPolicy.RUNTIME)
public @interface NestedAnno {
String value() default "default";
SomeEnum anEnum() default SomeEnum.DEFAULT;
Class<?>[] classArray() default Void.class;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpecialAttr {
Class clazz();
Class<?> clazz();
Thread.State state();
NestedAnno nestedAnno();
NestedAnno[] nestedAnnoArray();
NestedAnno optional() default @NestedAnno(value="optional", anEnum=SomeEnum.DEFAULT, classArray=Void.class);
NestedAnno[] optionalArray() default {@NestedAnno(value="optional", anEnum=SomeEnum.DEFAULT, classArray=Void.class)};
}
@Component("myName")
@Scope("myScope")
@SpecialAttr(clazz = String.class, state = Thread.State.NEW)
@SpecialAttr(clazz = String.class, state = Thread.State.NEW,
nestedAnno = @NestedAnno(value = "na", anEnum = SomeEnum.LABEL1, classArray = {String.class}),
nestedAnnoArray = {
@NestedAnno,
@NestedAnno(value = "na1", anEnum = SomeEnum.LABEL2, classArray = {Number.class})
})
@SuppressWarnings({"serial", "unused"})
private static class AnnotatedComponent implements Serializable {
@Autowired

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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.
@@ -38,27 +38,30 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.CollectionUtils;
/**
* A method resolver that uses reflection to locate the method that should be invoked.
* Reflection-based {@link MethodResolver} used by default in
* {@link StandardEvaluationContext} unless explicit method resolvers have been specified.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Chris Beams
* @since 3.0
* @see StandardEvaluationContext#addMethodResolver(MethodResolver)
*/
public class ReflectiveMethodResolver implements MethodResolver {
private static Method[] NO_METHODS = new Method[0];
private Map<Class<?>, MethodFilter> filters = null;
// Using distance will ensure a more accurate match is discovered,
// more closely following the Java rules.
private boolean useDistance = false;
public ReflectiveMethodResolver() {
}
/**
* This constructors allows the ReflectiveMethodResolver to be configured such that it will
* use a distance computation to check which is the better of two close matches (when there
@@ -71,7 +74,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
public ReflectiveMethodResolver(boolean useDistance) {
this.useDistance = useDistance;
}
/**
* Locate a method on a type. There are three kinds of match that might occur:
* <ol>
@@ -87,15 +90,15 @@ public class ReflectiveMethodResolver implements MethodResolver {
try {
TypeConverter typeConverter = context.getTypeConverter();
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
Method[] methods = type.getMethods();
Method[] methods = getMethods(type);
// If a filter is registered for this type, call it
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
if (filter != null) {
List<Method> methodsForFiltering = new ArrayList<Method>();
for (Method method: methods) {
methodsForFiltering.add(method);
}
List<Method> methodsForFiltering = new ArrayList<Method>();
for (Method method: methods) {
methodsForFiltering.add(method);
}
List<Method> methodsFiltered = filter.filter(methodsForFiltering);
if (CollectionUtils.isEmpty(methodsFiltered)) {
methods = NO_METHODS;
@@ -124,7 +127,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
continue;
}
if (method.getName().equals(name)) {
Class[] paramTypes = method.getParameterTypes();
Class<?>[] paramTypes = method.getParameterTypes();
List<TypeDescriptor> paramDescriptors = new ArrayList<TypeDescriptor>(paramTypes.length);
for (int i = 0; i < paramTypes.length; i++) {
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i)));
@@ -194,4 +197,16 @@ public class ReflectiveMethodResolver implements MethodResolver {
}
}
/**
* Return the set of methods for this type. The default implementation returns the
* result of Class#getMethods for the given {@code type}, but subclasses may override
* in order to alter the results, e.g. specifying static methods declared elsewhere.
*
* @param type the class for which to return the methods
* @since 3.1.1
*/
protected Method[] getMethods(Class<?> type) {
return type.getMethods();
}
}

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.
@@ -181,8 +181,13 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
setGeneratedKeysColumnNameArraySupported(false);
}
else {
logger.debug("GeneratedKeysColumnNameArray is supported for " + databaseProductName);
setGeneratedKeysColumnNameArraySupported(true);
if (isGetGeneratedKeysSupported()) {
logger.debug("GeneratedKeysColumnNameArray is supported for " + databaseProductName);
setGeneratedKeysColumnNameArraySupported(true);
}
else {
setGeneratedKeysColumnNameArraySupported(false);
}
}
}
catch (SQLException se) {
@@ -307,7 +312,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
tmd.setTableName(tables.getString("TABLE_NAME"));
tmd.setType(tables.getString("TABLE_TYPE"));
if (tmd.getSchemaName() == null) {
tableMeta.put(userName.toUpperCase(), tmd);
tableMeta.put(userName != null ? userName.toUpperCase() : "", tmd);
}
else {
tableMeta.put(tmd.getSchemaName().toUpperCase(), tmd);
@@ -335,7 +340,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
if (schemaName == null) {
tmd = tableMeta.get(getDefaultSchema());
if (tmd == null) {
tmd = tableMeta.get(userName.toUpperCase());
tmd = tableMeta.get(userName != null ? userName.toUpperCase() : "");
}
if (tmd == null) {
tmd = tableMeta.get("PUBLIC");

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.
@@ -85,12 +85,18 @@ public abstract class NamedParameterUtils {
int escapes = 0;
int i = 0;
while (i < statement.length) {
int skipToPosition = skipCommentsAndQuotes(statement, i);
if (i != skipToPosition) {
if (skipToPosition >= statement.length) {
int skipToPosition = i;
while (i < statement.length) {
skipToPosition = skipCommentsAndQuotes(statement, i);
if (i == skipToPosition) {
break;
}
i = skipToPosition;
else {
i = skipToPosition;
}
}
if (i >= statement.length) {
break;
}
char c = statement[i];
if (c == ':' || c == '&') {
@@ -252,6 +258,9 @@ public abstract class NamedParameterUtils {
actualSql.append(originalSql.substring(lastIndex, startIndex));
if (paramSource != null && paramSource.hasValue(paramName)) {
Object value = paramSource.getValue(paramName);
if (value instanceof SqlParameterValue) {
value = ((SqlParameterValue) value).getValue();
}
if (value instanceof Collection) {
Iterator entryIter = ((Collection) value).iterator();
int k = 0;

View File

@@ -181,9 +181,10 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
for (String statement : statements) {
lineNumber++;
try {
int rowsAffected = stmt.executeUpdate(statement);
stmt.execute(statement);
int rowsAffected = stmt.getUpdateCount();
if (logger.isDebugEnabled()) {
logger.debug(rowsAffected + " rows affected by SQL: " + statement);
logger.debug(rowsAffected + " returned as updateCount for SQL: " + statement);
}
}
catch (SQLException ex) {

View File

@@ -0,0 +1,68 @@
/*
* 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.jdbc.support;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* Registry for registering custom {@link org.springframework.jdbc.support.SQLExceptionTranslator}.
*
* @author Thomas Risberg
* @since 3.1
*/
public class CustomSQLExceptionTranslatorRegistrar implements InitializingBean {
private static final Log logger = LogFactory.getLog(CustomSQLExceptionTranslatorRegistrar.class);
/**
* Map registry to hold custom translators specific databases.
* Key is the database product name as defined in the
* {@link org.springframework.jdbc.support.SQLErrorCodesFactory}.
*/
private final Map<String, SQLExceptionTranslator> sqlExceptionTranslators =
new HashMap<String, SQLExceptionTranslator>();
/**
* Setter for a Map of translators where the key must be the database name as defined in the
* sql-error-codes.xml file. This method is used when this registry is used in an application context.
* <p>Note that any existing translators will remain unless there is a match in the database name at which
* point the new translator will replace the existing one.
*
* @param sqlExceptionTranslators
*/
public void setSqlExceptionTranslators(Map<String, SQLExceptionTranslator> sqlExceptionTranslators) {
this.sqlExceptionTranslators.putAll(sqlExceptionTranslators);
}
public void afterPropertiesSet() throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Registering custom SQL exception translators for database(s): " +
sqlExceptionTranslators.keySet());
}
for (String dbName : sqlExceptionTranslators.keySet()) {
CustomSQLExceptionTranslatorRegistry.getInstance()
.registerSqlExceptionTranslator(dbName, sqlExceptionTranslators.get(dbName));
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.jdbc.support;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
/**
* Registry for custom {@link org.springframework.jdbc.support.SQLExceptionTranslator} instances associated with
* specific databases allowing for overriding translation based on values contained in the configuration file
* named "sql-error-codes.xml".
*
* @author Thomas Risberg
* @since 3.1
* @see SQLErrorCodesFactory
*/
public class CustomSQLExceptionTranslatorRegistry {
private static final Log logger = LogFactory.getLog(CustomSQLExceptionTranslatorRegistry.class);
/**
* Map registry to hold custom translators specific databases.
* Key is the database product name as defined in the
* {@link org.springframework.jdbc.support.SQLErrorCodesFactory}.
*/
private final Map<String, SQLExceptionTranslator> sqlExceptionTranslatorRegistry =
new HashMap<String, SQLExceptionTranslator>();
/**
* Keep track of a single instance so we can return it to classes that request it.
*/
private static final CustomSQLExceptionTranslatorRegistry instance = new CustomSQLExceptionTranslatorRegistry();
/**
* Return the singleton instance.
*/
public static CustomSQLExceptionTranslatorRegistry getInstance() {
return instance;
}
/**
* Create a new instance of the {@link org.springframework.jdbc.support.CustomSQLExceptionTranslatorRegistry} class.
* <p>Not public to enforce Singleton design pattern.
*/
private CustomSQLExceptionTranslatorRegistry() {
}
/**
* Register a new custom translator for the specified database name.
*
* @param dbName the database name
* @param sqlExceptionTranslator the custom translator
*/
public void registerSqlExceptionTranslator(String dbName, SQLExceptionTranslator sqlExceptionTranslator) {
SQLExceptionTranslator replaced = sqlExceptionTranslatorRegistry.put(dbName, sqlExceptionTranslator);
if (replaced != null) {
logger.warn("Replacing custom translator '" + replaced +
"' for database " + dbName +
" with '" + sqlExceptionTranslator + "'");
}
else {
logger.info("Adding custom translator '" + sqlExceptionTranslator.getClass().getSimpleName() +
"' for database " + dbName);
}
}
public SQLExceptionTranslator findSqlExceptionTranslatorForDatabase(String dbName) {
return sqlExceptionTranslatorRegistry.get(dbName);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 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.
@@ -104,6 +104,10 @@ public class SQLErrorCodes {
return customSqlExceptionTranslator;
}
public void setCustomSqlExceptionTranslator(SQLExceptionTranslator customSqlExceptionTranslator) {
this.customSqlExceptionTranslator = customSqlExceptionTranslator;
}
public void setCustomSqlExceptionTranslatorClass(Class customSqlExceptionTranslatorClass) {
if (customSqlExceptionTranslatorClass != null) {
try {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -17,6 +17,7 @@
package org.springframework.jdbc.support;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import javax.sql.DataSource;
@@ -130,7 +131,7 @@ public class SQLErrorCodesFactory {
logger.warn("Error loading SQL error codes from config file", ex);
errorCodes = Collections.emptyMap();
}
this.errorCodesMap = errorCodes;
}
@@ -159,7 +160,7 @@ public class SQLErrorCodesFactory {
*/
public SQLErrorCodes getErrorCodes(String dbName) {
Assert.notNull(dbName, "Database product name must not be null");
SQLErrorCodes sec = this.errorCodesMap.get(dbName);
if (sec == null) {
for (SQLErrorCodes candidate : this.errorCodesMap.values()) {
@@ -170,6 +171,7 @@ public class SQLErrorCodesFactory {
}
}
if (sec != null) {
checkSqlExceptionTranslatorRegistry(dbName, sec);
if (logger.isDebugEnabled()) {
logger.debug("SQL error codes for '" + dbName + "' found");
}
@@ -246,4 +248,24 @@ public class SQLErrorCodesFactory {
}
}
private void checkSqlExceptionTranslatorRegistry(String dbName, SQLErrorCodes dbCodes) {
// Check the custom sql exception translator registry for any entries
SQLExceptionTranslator customTranslator =
CustomSQLExceptionTranslatorRegistry.getInstance().findSqlExceptionTranslatorForDatabase(dbName);
if (customTranslator != null) {
if (dbCodes.getCustomSqlExceptionTranslator() != null) {
logger.warn("Overriding already defined custom translator '" +
dbCodes.getCustomSqlExceptionTranslator().getClass().getSimpleName() +
" with '" + customTranslator.getClass().getSimpleName() +
"' found in the CustomSQLExceptionTranslatorRegistry for database " + dbName);
}
else {
logger.info("Using custom translator '" + customTranslator.getClass().getSimpleName() +
"' found in the CustomSQLExceptionTranslatorRegistry for database " + dbName);
}
dbCodes.setCustomSqlExceptionTranslator(customTranslator);
}
}
}

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.
@@ -268,4 +268,30 @@ public class NamedParameterUtilsTests {
assertEquals(expectedSql, newSql);
}
/*
* SPR-8280
*/
@Test
public void parseSqlStatementWithQuotedSingleQuote() {
String sql = "SELECT ':foo'':doo', :xxx FROM DUAL";
ParsedSql psql = NamedParameterUtils.parseSqlStatement(sql);
assertEquals(1, psql.getTotalParameterCount());
assertEquals("xxx", psql.getParameterNames().get(0));
}
@Test
public void parseSqlStatementWithQuotesAndCommentBefore() {
String sql = "SELECT /*:doo*/':foo', :xxx FROM DUAL";
ParsedSql psql = NamedParameterUtils.parseSqlStatement(sql);
assertEquals(1, psql.getTotalParameterCount());
assertEquals("xxx", psql.getParameterNames().get(0));
}
@Test
public void parseSqlStatementWithQuotesAndCommentAfter() {
String sql2 = "SELECT ':foo'/*:doo*/, :xxx FROM DUAL";
ParsedSql psql2 = NamedParameterUtils.parseSqlStatement(sql2);
assertEquals(1, psql2.getTotalParameterCount());
assertEquals("xxx", psql2.getParameterNames().get(0));
}
}

View File

@@ -20,8 +20,6 @@ import static org.junit.Assert.assertEquals;
import java.sql.Connection;
import javax.sql.DataSource;
import org.junit.After;
import org.junit.Test;
import org.springframework.core.io.ClassRelativeResourceLoader;
@@ -49,7 +47,7 @@ public class DatabasePopulatorTests {
assertEquals(name, jdbcTemplate.queryForObject("select NAME from T_TEST", String.class));
}
private void assertUsersDatabaseCreated(DataSource db) {
private void assertUsersDatabaseCreated() {
assertEquals("Sam", jdbcTemplate.queryForObject("select first_name from users where last_name = 'Brannen'",
String.class));
}
@@ -191,7 +189,22 @@ public class DatabasePopulatorTests {
connection.close();
}
assertUsersDatabaseCreated(db);
assertUsersDatabaseCreated();
}
@Test
public void testBuildWithSelectStatements() throws Exception {
databasePopulator.addScript(resourceLoader.getResource("db-schema.sql"));
databasePopulator.addScript(resourceLoader.getResource("db-test-data-select.sql"));
Connection connection = db.getConnection();
try {
databasePopulator.populate(connection);
} finally {
connection.close();
}
assertEquals(1, jdbcTemplate.queryForInt("select COUNT(NAME) from T_TEST where NAME='Keith'"));
assertEquals(1, jdbcTemplate.queryForInt("select COUNT(NAME) from T_TEST where NAME='Dave'"));
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.jdbc.support;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.jdbc.BadSqlGrammarException;
import static org.junit.Assert.*;
/**
* Tests for custom translator.
*
* @author Thomas Risberg
*/
public class CustomSQLExceptionTranslatorRegistrarTests {
@Before
public void setUp() {
new ClassPathXmlApplicationContext("test-custom-translators-context.xml",
CustomSQLExceptionTranslatorRegistrarTests.class);
}
@Test
public void testCustomErrorCodeTranslation() {
SQLErrorCodes codes = SQLErrorCodesFactory.getInstance().getErrorCodes("H2");
SQLErrorCodeSQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator();
sext.setSqlErrorCodes(codes);
DataAccessException exFor4200 = sext.doTranslate("", "", new SQLException("Ouch", "42000", 42000));
assertNotNull("Should have been translated", exFor4200);
assertTrue("Should have been instance of BadSqlGrammarException",
BadSqlGrammarException.class.isAssignableFrom(exFor4200.getClass()));
DataAccessException exFor2 = sext.doTranslate("", "", new SQLException("Ouch", "42000", 2));
assertNotNull("Should have been translated", exFor2);
assertTrue("Should have been instance of TransientDataAccessResourceException",
TransientDataAccessResourceException.class.isAssignableFrom(exFor2.getClass()));
DataAccessException exFor3 = sext.doTranslate("", "", new SQLException("Ouch", "42000", 3));
assertNull("Should not have been translated", exFor3);
}
}

View File

@@ -0,0 +1,3 @@
insert into T_TEST (NAME) values ('Keith');
insert into T_TEST (NAME) values ('Dave');
select NAME from T_TEST where NAME = 'Keith';

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">
<jdbc:embedded-database id="dataSource" type="H2"/>
<bean class="org.springframework.jdbc.support.CustomSQLExceptionTranslatorRegistrar">
<property name="sqlExceptionTranslators">
<map>
<entry key="H2">
<bean class="org.springframework.jdbc.support.CustomSqlExceptionTranslator"/>
</entry>
</map>
</property>
</bean>
</beans>

View File

@@ -2,15 +2,15 @@
/**
*
* Package providing integration of
* <a href="http://www.hibernate.org">Hibernate3</a>
* <a href="http://www.hibernate.org">Hibernate 3.x</a>
* with Spring concepts.
*
*
* <p>Contains SessionFactory helper classes, a template plus callback
* for Hibernate access, and an implementation of Spring's transaction SPI
* for local Hibernate transactions.
*
*
* <p><b>This package supports Hibernate 3.x only.</b>
* See the org.springframework.orm.hibernate package for Hibernate 2.1 support.
* See the <code>org.springframework.orm.hibernate4</code> package for Hibernate 4.x support.
*
*/
package org.springframework.orm.hibernate3;

View File

@@ -2,15 +2,15 @@
/**
*
* Package providing integration of
* <a href="http://www.hibernate.org">Hibernate 4.0</a>
* <a href="http://www.hibernate.org">Hibernate 4.x</a>
* with Spring concepts.
*
*
* <p>Contains an implementation of Spring's transaction SPI for local Hibernate transactions.
* This package is intentionally rather minimal, relying on native Hibernate builder APIs
* for building a SessionFactory (for example in an @Bean method in a @Configuration class).
* This package is intentionally rather minimal, with no template classes or the like,
* in order to follow native Hibernate recommendations as closely as possible.
*
* <p><b>This package supports Hibernate 4.x only.</b>
* See the org.springframework.orm.hibernate3 package for Hibernate 3.x support.
* See the <code>org.springframework.orm.hibernate3</code> package for Hibernate 3.x support.
*
*/
package org.springframework.orm.hibernate4;

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.
@@ -306,7 +306,7 @@ public abstract class EntityManagerFactoryUtils {
return new JpaObjectRetrievalFailureException((EntityNotFoundException) ex);
}
if (ex instanceof NoResultException) {
return new EmptyResultDataAccessException(ex.getMessage(), 1);
return new EmptyResultDataAccessException(ex.getMessage(), 1, ex);
}
if (ex instanceof NonUniqueResultException) {
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, 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.
@@ -123,6 +123,17 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
this.internalPersistenceUnitManager.setPersistenceXmlLocation(persistenceXmlLocation);
}
/**
* Uses the specified persistence unit name as the name of the default
* persistence unit, if applicable.
* <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
*/
@Override
public void setPersistenceUnitName(String persistenceUnitName) {
super.setPersistenceUnitName(persistenceUnitName);
this.internalPersistenceUnitManager.setDefaultPersistenceUnitName(persistenceUnitName);
}
/**
* Set whether to use Spring-based scanning for entity classes in the classpath
* instead of using JPA's standard scanning of jar files with <code>persistence.xml</code>

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.
@@ -55,6 +55,7 @@ import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.jdbc.datasource.lookup.MapDataSourceLookup;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils;
/**
* Default implementation of the {@link PersistenceUnitManager} interface.
@@ -328,7 +329,7 @@ public class DefaultPersistenceUnitManager
/**
* Prepare the PersistenceUnitInfos according to the configuration
* of this manager: scanning for <code>persistence.xml</code> files,
* parsing all matching files, configurating and post-processing them.
* parsing all matching files, configuring and post-processing them.
* <p>PersistenceUnitInfos cannot be obtained before this preparation
* method has been invoked.
* @see #obtainDefaultPersistenceUnitInfo()
@@ -404,6 +405,12 @@ public class DefaultPersistenceUnitManager
String className = reader.getClassMetadata().getClassName();
if (matchesFilter(reader, readerFactory)) {
scannedUnit.addManagedClassName(className);
if (scannedUnit.getPersistenceUnitRootUrl() == null) {
URL url = resource.getURL();
if (ResourceUtils.isJarURL(url)) {
scannedUnit.setPersistenceUnitRootUrl(ResourceUtils.extractJarFileURL(url));
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -43,7 +43,7 @@ import org.springframework.util.xml.DomUtils;
import org.springframework.util.xml.SimpleSaxErrorHandler;
/**
* Internal helper class for reading <code>persistence.xml</code> files.
* Internal helper class for reading JPA-compliant <code>persistence.xml</code> files.
*
* @author Costin Leau
* @author Juergen Hoeller
@@ -227,7 +227,7 @@ class PersistenceUnitReader {
/**
* Parse the unit info DOM element.
*/
protected SpringPersistenceUnitInfo parsePersistenceUnitInfo(Element persistenceUnit, String version) throws IOException { // JC: Changed
protected SpringPersistenceUnitInfo parsePersistenceUnitInfo(Element persistenceUnit, String version) throws IOException {
SpringPersistenceUnitInfo unitInfo = new SpringPersistenceUnitInfo();
// set JPA version (1.0 or 2.0)

View File

@@ -5,6 +5,10 @@
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="org/springframework/orm/jpa/domain/persistence.xml"/>
<!--
<property name="persistenceUnitName" value="Person"/>
<property name="packagesToScan" value="org.springframework.orm.jpa.domain"/>
-->
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">

View File

@@ -0,0 +1,57 @@
/*
* 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.test.context.junit4.profile.importresource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.Employee;
import org.springframework.beans.Pet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import static org.junit.Assert.*;
/**
* @author Juergen Hoeller
* @since 3.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DefaultProfileConfig.class, loader = AnnotationConfigContextLoader.class)
public class DefaultProfileAnnotationConfigTests {
@Autowired
protected Pet pet;
@Autowired(required = false)
protected Employee employee;
@Test
public void pet() {
assertNotNull(pet);
assertEquals("Fido", pet.getName());
}
@Test
public void employee() {
assertNull("employee bean should not be created for the default profile", employee);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.test.context.junit4.profile.importresource;
import org.springframework.beans.Pet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
/**
* @author Juergen Hoeller
* @since 3.1
*/
@Configuration
@ImportResource("org/springframework/test/context/junit4/profile/importresource/import.xml")
public class DefaultProfileConfig {
@Bean
public Pet pet() {
return new Pet("Fido");
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.test.context.junit4.profile.importresource;
import org.junit.Test;
import org.springframework.test.context.ActiveProfiles;
import static org.junit.Assert.*;
/**
* @author Juergen Hoeller
* @since 3.1
*/
@ActiveProfiles("dev")
public class DevProfileAnnotationConfigTests extends DefaultProfileAnnotationConfigTests {
@Test
@Override
public void employee() {
assertNotNull("employee bean should be loaded for the 'dev' profile", employee);
assertEquals("John Smith", employee.getName());
}
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<beans profile="dev">
<bean id="employee" class="org.springframework.beans.Employee">
<property name="name" value="John Smith" />
<property name="age" value="42" />
<property name="company" value="Acme Widgets, Inc." />
</bean>
</beans>
</beans>

View File

@@ -0,0 +1,49 @@
/*
* 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.dao;
/**
* Exception to be thrown on a query timeout. This could have different causes depending on
* the database API in use but most likely thrown after the database interrupts or stops
* the processing of a query before it has completed.
*
* <p>This exception can be thrown by user code trapping the native database exception or
* by exception translation.
*
* @author Thomas Risberg
* @since 3.1
*/
public class QueryTimeoutException extends TransientDataAccessException {
/**
* Constructor for QueryTimeoutException.
* @param msg the detail message
*/
public QueryTimeoutException(String msg) {
super(msg);
}
/**
* Constructor for QueryTimeoutException.
* @param msg the detail message
* @param cause the root cause from the data access API in use
*/
public QueryTimeoutException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2006 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.
@@ -43,4 +43,14 @@ public class EmptyResultDataAccessException extends IncorrectResultSizeDataAcces
super(msg, expectedSize, 0);
}
/**
* Constructor for EmptyResultDataAccessException.
* @param msg the detail message
* @param expectedSize the expected result size
* @param ex the wrapped exception
*/
public EmptyResultDataAccessException(String msg, int expectedSize, Throwable ex) {
super(msg, expectedSize, 0, ex);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2006 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.
@@ -68,8 +68,8 @@ public class IncorrectResultSizeDataAccessException extends DataRetrievalFailure
/**
* Constructor for IncorrectResultSizeDataAccessException.
* @param msg the detail message
* @param ex the wrapped exception
* @param expectedSize the expected result size
* @param ex the wrapped exception
*/
public IncorrectResultSizeDataAccessException(String msg, int expectedSize, Throwable ex) {
super(msg, ex);
@@ -89,19 +89,32 @@ public class IncorrectResultSizeDataAccessException extends DataRetrievalFailure
this.actualSize = actualSize;
}
/**
* Constructor for IncorrectResultSizeDataAccessException.
* @param msg the detail message
* @param expectedSize the expected result size
* @param actualSize the actual result size (or -1 if unknown)
* @param ex the wrapped exception
*/
public IncorrectResultSizeDataAccessException(String msg, int expectedSize, int actualSize, Throwable ex) {
super(msg, ex);
this.expectedSize = expectedSize;
this.actualSize = actualSize;
}
/**
* Return the expected result size.
*/
public int getExpectedSize() {
return expectedSize;
return this.expectedSize;
}
/**
* Return the actual result size (or -1 if unknown).
*/
public int getActualSize() {
return actualSize;
return this.actualSize;
}
}

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.
@@ -17,11 +17,11 @@
package org.springframework.transaction.annotation;
import java.util.Collection;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.Assert;
@@ -37,11 +37,12 @@ import org.springframework.util.Assert;
@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
protected Map<String, Object> enableTx;
protected AnnotationAttributes enableTx;
protected PlatformTransactionManager txManager;
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableTx = importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false);
this.enableTx = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
Assert.notNull(this.enableTx,
"@EnableTransactionManagement is not present on importing class " +
importMetadata.getClassName());

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.
@@ -44,7 +44,7 @@ public class ProxyTransactionManagementConfiguration extends AbstractTransaction
new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource());
advisor.setAdvice(transactionInterceptor());
advisor.setOrder(((Integer)this.enableTx.get("order")));
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
return advisor;
}

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.
@@ -450,6 +450,14 @@ public class MediaType implements Comparable<MediaType> {
return this.parameters.get(name);
}
/**
* Return all generic parameter values.
* @return a read-only map, possibly empty, never <code>null</code>
*/
public Map<String, String> getParameters() {
return parameters;
}
/**
* Indicate whether this {@code MediaType} includes the given media type.
* <p>For instance, {@code text/*} includes {@code text/plain} and {@code text/html}, and {@code application/*+xml}

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.
@@ -25,15 +25,19 @@ import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
/**
@@ -90,14 +94,31 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
public HttpHeaders getHeaders() {
if (this.headers == null) {
this.headers = new HttpHeaders();
for (Enumeration headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
for (Enumeration<?> headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
String headerName = (String) headerNames.nextElement();
for (Enumeration headerValues = this.servletRequest.getHeaders(headerName);
for (Enumeration<?> headerValues = this.servletRequest.getHeaders(headerName);
headerValues.hasMoreElements();) {
String headerValue = (String) headerValues.nextElement();
this.headers.add(headerName, headerValue);
}
}
// HttpServletRequest exposes some headers as properties: we should include those if not already present
if (this.headers.getContentType() == null && this.servletRequest.getContentType() != null) {
MediaType contentType = MediaType.parseMediaType(this.servletRequest.getContentType());
this.headers.setContentType(contentType);
}
if (this.headers.getContentType() != null && this.headers.getContentType().getCharSet() == null &&
this.servletRequest.getCharacterEncoding() != null) {
MediaType oldContentType = this.headers.getContentType();
Charset charSet = Charset.forName(this.servletRequest.getCharacterEncoding());
Map<String, String> params = new HashMap<String, String>(oldContentType.getParameters());
params.put("charset", charSet.toString());
MediaType newContentType = new MediaType(oldContentType.getType(), oldContentType.getSubtype(), params);
this.headers.setContentType(newContentType);
}
if (this.headers.getContentLength() == -1 && this.servletRequest.getContentLength() != -1) {
this.headers.setContentLength(this.servletRequest.getContentLength());
}
}
return this.headers;
}

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.
@@ -83,6 +83,14 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
this.servletResponse.addHeader(headerName, headerValue);
}
}
// HttpServletResponse exposes some headers as properties: we should include those if not already present
if (this.servletResponse.getContentType() == null && this.headers.getContentType() != null) {
this.servletResponse.setContentType(this.headers.getContentType().toString());
}
if (this.servletResponse.getCharacterEncoding() == null && this.headers.getContentType() != null &&
this.headers.getContentType().getCharSet() != null) {
this.servletResponse.setCharacterEncoding(this.headers.getContentType().getCharSet().name());
}
this.headersWritten = true;
}
}

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.
@@ -24,17 +24,18 @@ import java.lang.annotation.Target;
/**
* Annotation for mapping web requests onto specific handler classes and/or
* handler methods. Provides consistent style between Servlet and Portlet
* handler methods. Provides a consistent style between Servlet and Portlet
* environments, with the semantics adapting to the concrete environment.
*
* <p><b>NOTE:</b> Method-level mappings are only allowed to narrow the mapping
* expressed at the class level (if any). In the Servlet case, an HTTP path needs to
* uniquely map onto one specific handler bean (not spread across multiple handler beans);
* the remaining mapping parameters and conditions are effectively assertions only.
* In the Portlet case, a portlet mode in combination with specific parameter conditions
* needs to uniquely map onto one specific handler bean, with all conditions evaluated
* for mapping purposes. It is strongly recommended to co-locate related handler methods
* into the same bean and therefore keep the mappings simple and intuitive.
* <p><b>NOTE:</b> The set of features supported for Servlets is a superset
* of the set of features supported for Portlets. The places where this applies
* are marked with the label "Servlet-only" in this source file. For Servlet
* environments there are some further distinctions depending on whether an
* application is configured with {@literal "@MVC 3.0"} or
* {@literal "@MVC 3.1"} support classes. The places where this applies are
* marked with {@literal "@MVC 3.1-only"} in this source file. For more
* details see the note on the new support classes added in Spring MVC 3.1
* further below.
*
* <p>Handler methods which are annotated with this annotation are allowed
* to have very flexible signatures. They may have arguments of the following
@@ -71,8 +72,8 @@ import java.lang.annotation.Target;
* <li>{@link java.io.OutputStream} / {@link java.io.Writer} for generating
* the response's content. This will be the raw OutputStream/Writer as
* exposed by the Servlet/Portlet API.
* <li>{@link PathVariable @PathVariable} annotated parameters for access to
* URI template values (i.e. /hotels/{hotel}). Variable values will be
* <li>{@link PathVariable @PathVariable} annotated parameters (Servlet-only)
* for access to URI template values (i.e. /hotels/{hotel}). Variable values will be
* converted to the declared method argument type. By default, the URI template
* will match against the regular expression {@code [^\.]*} (i.e. any character
* other than period), but this can be changed by specifying another regular
@@ -90,30 +91,43 @@ import java.lang.annotation.Target;
* {@link org.springframework.util.MultiValueMap MultiValueMap&lt;String, String&gt;}, or
* {@link org.springframework.http.HttpHeaders HttpHeaders} method parameter to
* gain access to all request headers.
* <li>{@link RequestBody @RequestBody} annotated parameters for access to
* the Servlet request HTTP contents. The request stream will be
* converted to the declared method argument type using
* <li>{@link RequestBody @RequestBody} annotated parameters (Servlet-only)
* for access to the Servlet request HTTP contents. The request stream will be
* converted to the declared method argument type using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
* converters}. Such parameters may optionally be annotated with {@code @Valid}.
* <li>{@link RequestPart @RequestPart} annotated parameters for access to the content
* converters}. Such parameters may optionally be annotated with {@code @Valid}
* but do not support access to validation results through a
* {@link org.springframework.validation.Errors} /
* {@link org.springframework.validation.BindingResult} argument.
* Instead a {@link org.springframework.web.servlet.mvc.method.annotation.MethodArgumentNotValidException}
* exception is raised.
* <li>{@link RequestPart @RequestPart} annotated parameters
* (Servlet-only, {@literal @MVC 3.1-only})
* for access to the content
* of a part of "multipart/form-data" request. The request part stream will be
* converted to the declared method argument type using
* converted to the declared method argument type using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
* converters}. Such parameters may optionally be annotated with {@code @Valid}.
* converters}. Such parameters may optionally be annotated with {@code @Valid}
* but do not support access to validation results through a
* {@link org.springframework.validation.Errors} /
* {@link org.springframework.validation.BindingResult} argument.
* Instead a {@link org.springframework.web.servlet.mvc.method.annotation.MethodArgumentNotValidException}
* exception is raised.
* <li>{@link org.springframework.http.HttpEntity HttpEntity&lt;?&gt;} parameters
* for access to the Servlet request HTTP headers and contents. The request stream will be
* converted to the entity body using
* (Servlet-only) for access to the Servlet request HTTP headers and contents.
* The request stream will be converted to the entity body using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
* converters}.
* <li>{@link java.util.Map} / {@link org.springframework.ui.Model} /
* {@link org.springframework.ui.ModelMap} for enriching the implicit model
* that will be exposed to the web view.
* <li>{@link org.springframework.web.servlet.mvc.support.RedirectAttributes}
* to specify the exact set of attributes to use in case of a redirect
* and also to add flash attributes (attributes stored temporarily on the
* server-side to make them available to the request after the redirect).
* {@code RedirectAttributes} is used instead of the implicit model if the
* method returns a "redirect:" prefixed view name or {@code RedirectView}.
* <li>{@link org.springframework.web.servlet.mvc.support.RedirectAttributes}
* (Servlet-only, {@literal @MVC 3.1-only}) to specify the exact set of attributes
* to use in case of a redirect and also to add flash attributes (attributes
* stored temporarily on the server-side to make them available to the request
* after the redirect). {@code RedirectAttributes} is used instead of the
* implicit model if the method returns a "redirect:" prefixed view name or
* {@code RedirectView}.
* <li>Command/form objects to bind parameters to: as bean properties or fields,
* with customizable type conversion, depending on {@link InitBinder} methods
* and/or the HandlerAdapter configuration - see the "webBindingInitializer"
@@ -130,9 +144,10 @@ import java.lang.annotation.Target;
* for marking form processing as complete (triggering the cleanup of session
* attributes that have been indicated by the {@link SessionAttributes} annotation
* at the handler type level).
* <li>{@link org.springframework.web.util.UriComponentsBuilder} a builder for
* preparing a URL relative to the current request's host, port, scheme, context
* path, and the literal part of the servlet mapping.
* <li>{@link org.springframework.web.util.UriComponentsBuilder}
* (Servlet-only, {@literal @MVC 3.1-only})
* for preparing a URL relative to the current request's host, port, scheme,
* context path, and the literal part of the servlet mapping.
* </ul>
*
* <p>The following return types are supported for handler methods:
@@ -160,15 +175,15 @@ import java.lang.annotation.Target;
* The handler method may also programmatically enrich the model by
* declaring a {@link org.springframework.ui.ModelMap} argument
* (see above).
* <li>{@link ResponseBody @ResponseBody} annotated methods for access to
* the Servlet response HTTP contents. The return value will be converted
* to the response stream using
* <li>{@link ResponseBody @ResponseBody} annotated methods (Servlet-only)
* for access to the Servlet response HTTP contents. The return value will
* be converted to the response stream using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
* converters}.
* <li>A {@link org.springframework.http.HttpEntity HttpEntity&lt;?&gt;} or
* {@link org.springframework.http.ResponseEntity ResponseEntity&lt;?&gt;} object
* to access to the Servlet response HTTP headers and contents. The entity body will
* be converted to the response stream using
* (Servlet-only) to access to the Servlet response HTTP headers and contents.
* The entity body will be converted to the response stream using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
* converters}.
* <li><code>void</code> if the method handles the response itself (by
@@ -187,16 +202,24 @@ import java.lang.annotation.Target;
* {@link ModelAttribute} annotated reference data accessor methods.
* </ul>
*
* <p><b>NOTE: <code>@RequestMapping</code> will only be processed if a
* corresponding <code>HandlerMapping</code> (for type level annotations)
* and/or <code>HandlerAdapter</code> (for method level annotations) is
* present in the dispatcher.</b> This is the case by default in both
* <code>DispatcherServlet</code> and <code>DispatcherPortlet</code>.
* <p><b>NOTE:</b> <code>@RequestMapping</code> will only be processed if an
* an appropriate <code>HandlerMapping</code>-<code>HandlerAdapter</code> pair
* is configured. This is the case by default in both the
* <code>DispatcherServlet</code> and the <code>DispatcherPortlet</code>.
* However, if you are defining custom <code>HandlerMappings</code> or
* <code>HandlerAdapters</code>, then you need to make sure that a
* corresponding custom <code>RequestMappingHandlerMethodMapping</code>
* and/or <code>RequestMappingHandlerMethodAdapter</code> is defined as well
* - provided that you intend to use <code>@RequestMapping</code>.
* <code>HandlerAdapters</code>, then you need to add
* <code>DefaultAnnotationHandlerMapping</code> and
* <code>AnnotationMethodHandlerAdapter</code> to your configuration.</code>.
*
* <p><b>NOTE:</b> Spring 3.1 introduced a new set of support classes for
* <code>@RequestMapping</code> methods in Servlet environments called
* <code>RequestMappingHandlerMapping</code> and
* <code>RequestMappingHandlerAdapter</code>. They are recommended for use and
* even required to take advantage of new features in Spring MVC 3.1 (search
* {@literal "@MVC 3.1-only"} in this source file) and going forward.
* The new support classes are enabled by default from the MVC namespace and
* with use of the MVC Java config (<code>@EnableWebMvc</code>) but must be
* configured explicitly if using neither.
*
* <p><b>NOTE:</b> When using controller interfaces (e.g. for AOP proxying),
* make sure to consistently put <i>all</i> your mapping annotations - such as
@@ -234,20 +257,6 @@ public @interface RequestMapping {
* <p><b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit
* this primary mapping, narrowing it for a specific handler method.
* <p>In case of Servlet-based handler methods, the method names are
* taken into account for narrowing if no path was specified explicitly,
* according to the specified
* {@link org.springframework.web.servlet.mvc.multiaction.MethodNameResolver}
* (by default an
* {@link org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver}).
* Note that this only applies in case of ambiguous annotation mappings
* that do not specify a path mapping explicitly. In other words,
* the method name is only used for narrowing among a set of matching
* methods; it does not constitute a primary path mapping itself.
* <p>If you have a single default method (without explicit path mapping),
* then all requests without a more specific mapped method found will
* be dispatched to it. If you have multiple such default methods, then
* the method name will be taken into account for choosing between them.
*/
String[] value() default {};
@@ -309,7 +318,7 @@ public @interface RequestMapping {
* and against PortletRequest properties in a Portlet 2.0 environment.
* @see org.springframework.http.MediaType
*/
String[] headers() default {};
String[] headers() default {};
/**
* The consumable media types of the mapped request, narrowing the primary mapping.

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.
@@ -23,11 +23,11 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
@@ -44,6 +44,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Performs the actual initialization work for the root application context.
@@ -463,11 +464,18 @@ public class ContextLoader {
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
*/
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(servletContext);
if (initializerClasses.size() == 0) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
}
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass :
determineContextInitializerClasses(servletContext)) {
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> contextClass = applicationContext.getClass();
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
@@ -480,6 +488,13 @@ public class ContextLoader {
Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
// eagerly attempt to initialize servlet property sources in case initializers
// below depend on accessing context-params via the Environment API. Note that
// depending on application context implementation, this initialization will be
// attempted again during context refresh.
WebApplicationContextUtils.initServletPropertySources(
applicationContext.getEnvironment().getPropertySources(), servletContext);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
initializer.initialize(applicationContext);
}

Some files were not shown because too many files have changed in this diff Show More