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

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