@Configuration parsing fully relies on Spring's MetadataReader abstraction now
This commit is contained in:
parent
37e1333a41
commit
b5d21108da
|
|
@ -18,7 +18,6 @@ package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Inherited;
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
@ -27,7 +26,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that a class declares one or more {@link Bean} methods and may be processed
|
* Indicates that a class declares one or more {@link Bean} methods and may be processed
|
||||||
* by the Spring container to generate bean definitions and service requests for those beans
|
* by the Spring container to generate bean definitions and service requests for those beans
|
||||||
|
|
@ -59,7 +57,6 @@ import org.springframework.stereotype.Component;
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.TYPE})
|
@Target({ElementType.TYPE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
|
||||||
@Documented
|
@Documented
|
||||||
@Component
|
@Component
|
||||||
public @interface Configuration {
|
public @interface Configuration {
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,19 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.beans.BeanMetadataElement;
|
|
||||||
import org.springframework.beans.factory.parsing.Location;
|
import org.springframework.beans.factory.parsing.Location;
|
||||||
import org.springframework.beans.factory.parsing.Problem;
|
import org.springframework.beans.factory.parsing.Problem;
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.DescriptiveResource;
|
||||||
import org.springframework.util.Assert;
|
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.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -45,75 +44,42 @@ import org.springframework.util.ClassUtils;
|
||||||
* @see ConfigurationClassMethod
|
* @see ConfigurationClassMethod
|
||||||
* @see ConfigurationClassParser
|
* @see ConfigurationClassParser
|
||||||
*/
|
*/
|
||||||
final class ConfigurationClass implements BeanMetadataElement {
|
final class ConfigurationClass {
|
||||||
|
|
||||||
private String name;
|
private final AnnotationMetadata metadata;
|
||||||
|
|
||||||
private ConfigurationClass declaringClass;
|
private final Resource resource;
|
||||||
|
|
||||||
private Object source;
|
|
||||||
|
|
||||||
private String beanName;
|
private String beanName;
|
||||||
|
|
||||||
private int modifiers;
|
|
||||||
|
|
||||||
private final Set<Annotation> annotations = new HashSet<Annotation>();
|
|
||||||
|
|
||||||
private final Set<ConfigurationClassMethod> methods = new LinkedHashSet<ConfigurationClassMethod>();
|
private final Set<ConfigurationClassMethod> methods = new LinkedHashSet<ConfigurationClassMethod>();
|
||||||
|
|
||||||
private final Map<String, Integer> overloadedMethodMap = new LinkedHashMap<String, Integer>();
|
private final Map<String, Integer> overloadedMethodMap = new LinkedHashMap<String, Integer>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
public ConfigurationClass(MetadataReader metadataReader, String beanName) {
|
||||||
* Sets the fully-qualified name of this class.
|
this.metadata = metadataReader.getAnnotationMetadata();
|
||||||
*/
|
this.resource = metadataReader.getResource();
|
||||||
public void setName(String className) {
|
this.beanName = beanName;
|
||||||
this.name = className;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public ConfigurationClass(Class clazz, String beanName) {
|
||||||
* Returns the fully-qualified name of this class.
|
this.metadata = new StandardAnnotationMetadata(clazz);
|
||||||
*/
|
this.resource = new DescriptiveResource(clazz.toString());
|
||||||
public String getName() {
|
this.beanName = beanName;
|
||||||
return name;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public AnnotationMetadata getMetadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource getResource() {
|
||||||
|
return this.resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'.
|
|
||||||
*/
|
|
||||||
public String getSimpleName() {
|
public String getSimpleName() {
|
||||||
return name == null ? null : ClassUtils.getShortName(name);
|
return ClassUtils.getShortName(getMetadata().getClassName());
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeclaringClass(ConfigurationClass configurationClass) {
|
|
||||||
this.declaringClass = configurationClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigurationClass getDeclaringClass() {
|
|
||||||
return declaringClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the source location for this class. Must be a resource-path formatted string.
|
|
||||||
* @param source resource path to the .java file that declares this class.
|
|
||||||
*/
|
|
||||||
public void setSource(Object source) {
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a resource path-formatted representation of the .java file that declares this
|
|
||||||
* class
|
|
||||||
*/
|
|
||||||
public Object getSource() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location getLocation() {
|
|
||||||
if (getName() == null) {
|
|
||||||
throw new IllegalStateException("'name' property is null. Call setName() before calling getLocation()");
|
|
||||||
}
|
|
||||||
return new Location(new ClassPathResource(ClassUtils.convertClassNameToResourcePath(getName())), getSource());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBeanName(String beanName) {
|
public void setBeanName(String beanName) {
|
||||||
|
|
@ -121,71 +87,29 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBeanName() {
|
public String getBeanName() {
|
||||||
return beanName;
|
return this.beanName;
|
||||||
}
|
|
||||||
|
|
||||||
public void setModifiers(int modifiers) {
|
|
||||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
|
|
||||||
this.modifiers = modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getModifiers() {
|
|
||||||
return modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addAnnotation(Annotation annotation) {
|
|
||||||
this.annotations.add(annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the annotation on this class matching <var>annoType</var> or
|
|
||||||
* {@literal null} if not present.
|
|
||||||
* @see #getRequiredAnnotation(Class)
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <A extends Annotation> A getAnnotation(Class<A> annoType) {
|
|
||||||
for (Annotation annotation : annotations) {
|
|
||||||
if (annotation.annotationType().equals(annoType)) {
|
|
||||||
return (A) annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the annotation on this class matching <var>annoType</var>
|
|
||||||
* @throws {@link IllegalStateException} if not present
|
|
||||||
* @see #getAnnotation(Class)
|
|
||||||
*/
|
|
||||||
public <A extends Annotation> A getRequiredAnnotation(Class<A> annoType) {
|
|
||||||
A anno = getAnnotation(annoType);
|
|
||||||
if (anno == null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
String.format("Required annotation %s is not present on %s", annoType.getSimpleName(), this));
|
|
||||||
}
|
|
||||||
return anno;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationClass addMethod(ConfigurationClassMethod method) {
|
public ConfigurationClass addMethod(ConfigurationClassMethod method) {
|
||||||
method.setDeclaringClass(this);
|
this.methods.add(method);
|
||||||
methods.add(method);
|
String name = method.getMetadata().getMethodName();
|
||||||
Integer count = overloadedMethodMap.get(method.getName());
|
Integer count = this.overloadedMethodMap.get(name);
|
||||||
if (count != null) {
|
if (count != null) {
|
||||||
overloadedMethodMap.put(method.getName(), count + 1);
|
this.overloadedMethodMap.put(name, count + 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
overloadedMethodMap.put(method.getName(), 1);
|
this.overloadedMethodMap.put(name, 1);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ConfigurationClassMethod> getBeanMethods() {
|
public Set<ConfigurationClassMethod> getConfigurationMethods() {
|
||||||
return methods;
|
return this.methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(ProblemReporter problemReporter) {
|
public void validate(ProblemReporter problemReporter) {
|
||||||
// No overloading of factory methods allowed
|
// No overloading of factory methods allowed
|
||||||
for (Map.Entry<String, Integer> entry : overloadedMethodMap.entrySet()) {
|
for (Map.Entry<String, Integer> entry : this.overloadedMethodMap.entrySet()) {
|
||||||
String methodName = entry.getKey();
|
String methodName = entry.getKey();
|
||||||
int count = entry.getValue();
|
int count = entry.getValue();
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
|
|
@ -194,11 +118,11 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A configuration class may not be final (CGLIB limitation)
|
// A configuration class may not be final (CGLIB limitation)
|
||||||
if (getAnnotation(Configuration.class) != null) {
|
if (getMetadata().hasAnnotation(Configuration.class.getName())) {
|
||||||
if (Modifier.isFinal(modifiers)) {
|
if (getMetadata().isFinal()) {
|
||||||
problemReporter.error(new FinalConfigurationProblem());
|
problemReporter.error(new FinalConfigurationProblem());
|
||||||
}
|
}
|
||||||
for (ConfigurationClassMethod method : methods) {
|
for (ConfigurationClassMethod method : this.methods) {
|
||||||
method.validate(problemReporter);
|
method.validate(problemReporter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -210,7 +134,7 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||||
|
|
||||||
public FinalConfigurationProblem() {
|
public FinalConfigurationProblem() {
|
||||||
super(String.format("@Configuration class '%s' may not be final. Remove the final modifier to continue.",
|
super(String.format("@Configuration class '%s' may not be final. Remove the final modifier to continue.",
|
||||||
getSimpleName()), ConfigurationClass.this.getLocation());
|
getSimpleName()), new Location(getResource()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,9 +145,8 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||||
public OverloadedMethodProblem(String methodName, int count) {
|
public OverloadedMethodProblem(String methodName, int count) {
|
||||||
super(String.format("@Configuration class '%s' has %s overloaded factory methods of name '%s'. " +
|
super(String.format("@Configuration class '%s' has %s overloaded factory methods of name '%s'. " +
|
||||||
"Only one factory method of the same name allowed.",
|
"Only one factory method of the same name allowed.",
|
||||||
getSimpleName(), count, methodName), ConfigurationClass.this.getLocation());
|
getSimpleName(), count, methodName), new Location(getResource()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 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 java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface used when dynamically creating mutable instances of annotations associated
|
|
||||||
* with {@link Configuration} class processing. This functionality is necessary given
|
|
||||||
* that parsing of Configuration classes is done with ASM. Annotation metadata (including
|
|
||||||
* attributes) is parsed from the class files, and instances of those annotations are
|
|
||||||
* then created using this interface and its associated utilities. The annotation
|
|
||||||
* instances are attached to the configuration model objects at runtime, namely
|
|
||||||
* {@link ConfigurationClassMethod}. This approach is better than the alternative of
|
|
||||||
* creating fine-grained model representations of all annotations and attributes.
|
|
||||||
* It is better to simply attach annotation instances and read them as needed.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
* @author Juergen Hoeller
|
|
||||||
* @since 3.0
|
|
||||||
* @see ConfigurationClassAnnotationVisitor
|
|
||||||
* @see ConfigurationClassReaderUtils#createMutableAnnotation
|
|
||||||
*/
|
|
||||||
public interface ConfigurationClassAnnotation extends Annotation {
|
|
||||||
|
|
||||||
void setAttributeValue(String attribName, Object attribValue);
|
|
||||||
|
|
||||||
Class<?> getAttributeType(String attributeName);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 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 java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
|
||||||
import org.springframework.asm.Type;
|
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ASM {@link AnnotationVisitor} that populates a given {@link ConfigurationClassAnnotation} instance
|
|
||||||
* with its attributes.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
* @author Juergen Hoeller
|
|
||||||
* @since 3.0
|
|
||||||
* @see ConfigurationClassAnnotation
|
|
||||||
* @see ConfigurationClassReaderUtils#createMutableAnnotation
|
|
||||||
*/
|
|
||||||
class ConfigurationClassAnnotationVisitor implements AnnotationVisitor {
|
|
||||||
|
|
||||||
protected final ConfigurationClassAnnotation mutableAnno;
|
|
||||||
|
|
||||||
private final ClassLoader classLoader;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link ConfigurationClassAnnotationVisitor} instance that will populate the the
|
|
||||||
* attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation} instead of
|
|
||||||
* {@link ConfigurationClassAnnotation} to avoid the need for callers to typecast.
|
|
||||||
* @param mutableAnno {@link ConfigurationClassAnnotation} instance to visit and populate
|
|
||||||
* @see ConfigurationClassReaderUtils#createMutableAnnotation
|
|
||||||
*/
|
|
||||||
public ConfigurationClassAnnotationVisitor(ConfigurationClassAnnotation mutableAnno, ClassLoader classLoader) {
|
|
||||||
this.mutableAnno = mutableAnno;
|
|
||||||
this.classLoader = classLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visit(String attribName, Object attribValue) {
|
|
||||||
Class<?> attribReturnType = mutableAnno.getAttributeType(attribName);
|
|
||||||
if (attribReturnType.equals(Class.class)) {
|
|
||||||
// the attribute type is Class -> load it and set it.
|
|
||||||
String className = ((Type) attribValue).getClassName();
|
|
||||||
try {
|
|
||||||
Class<?> classVal = classLoader.loadClass(className);
|
|
||||||
if (classVal != null) {
|
|
||||||
mutableAnno.setAttributeValue(attribName, classVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
throw new IllegalStateException("Cannot resolve attribute type [" + className + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// otherwise, assume the value can be set literally
|
|
||||||
mutableAnno.setAttributeValue(attribName, attribValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
|
|
||||||
String enumTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(enumTypeDescriptor);
|
|
||||||
try {
|
|
||||||
Class<? extends Enum> enumType = (Class<? extends Enum>) classLoader.loadClass(enumTypeName);
|
|
||||||
if (enumType == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Enum enumValue = Enum.valueOf(enumType, strEnumValue);
|
|
||||||
mutableAnno.setAttributeValue(attribName, enumValue);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
throw new IllegalStateException("Cannot resolve enum type [" + enumTypeName + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitAnnotation(String attribName, String annoTypeDesc) {
|
|
||||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadAnnotationType(annoTypeDesc, classLoader);
|
|
||||||
if (annoClass == null) {
|
|
||||||
return new EmptyVisitor();
|
|
||||||
}
|
|
||||||
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
|
|
||||||
try {
|
|
||||||
Field attribute = mutableAnno.getClass().getField(attribName);
|
|
||||||
attribute.set(mutableAnno, anno);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new IllegalStateException("Could not reflectively set annotation field", ex);
|
|
||||||
}
|
|
||||||
return new ConfigurationClassAnnotationVisitor(anno, classLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitArray(final String attribName) {
|
|
||||||
return new MutableAnnotationArrayVisitor(mutableAnno, attribName, classLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnd() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ASM {@link AnnotationVisitor} that visits any annotation array values while populating
|
|
||||||
* a new {@link ConfigurationClassAnnotation} instance.
|
|
||||||
*/
|
|
||||||
private static class MutableAnnotationArrayVisitor implements AnnotationVisitor {
|
|
||||||
|
|
||||||
private final List<Object> values = new ArrayList<Object>();
|
|
||||||
|
|
||||||
private final ConfigurationClassAnnotation mutableAnno;
|
|
||||||
|
|
||||||
private final String attribName;
|
|
||||||
|
|
||||||
private final ClassLoader classLoader;
|
|
||||||
|
|
||||||
public MutableAnnotationArrayVisitor(ConfigurationClassAnnotation mutableAnno, String attribName, ClassLoader classLoader) {
|
|
||||||
this.mutableAnno = mutableAnno;
|
|
||||||
this.attribName = attribName;
|
|
||||||
this.classLoader = classLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visit(String na, Object value) {
|
|
||||||
values.add(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnum(String s, String s1, String s2) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
|
|
||||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadAnnotationType(annoTypeDesc, classLoader);
|
|
||||||
if (annoClass == null) {
|
|
||||||
return new EmptyVisitor();
|
|
||||||
}
|
|
||||||
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
|
|
||||||
values.add(anno);
|
|
||||||
return new ConfigurationClassAnnotationVisitor(anno, classLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitArray(String s) {
|
|
||||||
return new EmptyVisitor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnd() {
|
|
||||||
Class<?> arrayType = mutableAnno.getAttributeType(attribName);
|
|
||||||
Object[] array = (Object[]) Array.newInstance(arrayType.getComponentType(), 0);
|
|
||||||
mutableAnno.setAttributeValue(attribName, values.toArray(array));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -20,6 +20,7 @@ import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
|
@ -35,6 +36,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
|
@ -78,7 +80,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
*/
|
*/
|
||||||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
||||||
doLoadBeanDefinitionForConfigurationClass(configClass);
|
doLoadBeanDefinitionForConfigurationClass(configClass);
|
||||||
for (ConfigurationClassMethod method : configClass.getBeanMethods()) {
|
for (ConfigurationClassMethod method : configClass.getConfigurationMethods()) {
|
||||||
loadBeanDefinitionsForModelMethod(method);
|
loadBeanDefinitionsForModelMethod(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +91,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
|
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
|
||||||
if (configClass.getBeanName() == null) {
|
if (configClass.getBeanName() == null) {
|
||||||
GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
|
GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
|
||||||
configBeanDef.setBeanClassName(configClass.getName());
|
configBeanDef.setBeanClassName(configClass.getMetadata().getClassName());
|
||||||
String configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(configBeanDef, registry);
|
String configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(configBeanDef, registry);
|
||||||
configClass.setBeanName(configBeanName);
|
configClass.setBeanName(configBeanName);
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
|
|
@ -103,24 +105,26 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
* {@link #registry} based on its contents.
|
* {@link #registry} based on its contents.
|
||||||
*/
|
*/
|
||||||
private void loadBeanDefinitionsForModelMethod(ConfigurationClassMethod method) {
|
private void loadBeanDefinitionsForModelMethod(ConfigurationClassMethod method) {
|
||||||
|
MethodMetadata metadata = method.getMetadata();
|
||||||
|
|
||||||
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
|
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
|
||||||
ConfigurationClass configClass = method.getDeclaringClass();
|
ConfigurationClass configClass = method.getDeclaringClass();
|
||||||
beanDef.setFactoryBeanName(configClass.getBeanName());
|
beanDef.setFactoryBeanName(configClass.getBeanName());
|
||||||
beanDef.setUniqueFactoryMethodName(method.getName());
|
beanDef.setUniqueFactoryMethodName(metadata.getMethodName());
|
||||||
beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||||
|
|
||||||
// consider name and any aliases
|
// consider name and any aliases
|
||||||
Bean bean = method.getRequiredAnnotation(Bean.class);
|
Map<String, Object> beanAttributes = metadata.getAnnotationAttributes(Bean.class.getName());
|
||||||
List<String> names = new ArrayList<String>(Arrays.asList(bean.name()));
|
List<String> names = new ArrayList<String>(Arrays.asList((String[]) beanAttributes.get("name")));
|
||||||
String beanName = (names.size() > 0) ? names.remove(0) : method.getName();
|
String beanName = (names.size() > 0 ? names.remove(0) : method.getMetadata().getMethodName());
|
||||||
for (String alias : bean.name()) {
|
for (String alias : names) {
|
||||||
registry.registerAlias(beanName, alias);
|
registry.registerAlias(beanName, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
// has this already been overriden (i.e.: via XML)?
|
// has this already been overriden (i.e.: via XML)?
|
||||||
if (registry.containsBeanDefinition(beanName)) {
|
if (registry.containsBeanDefinition(beanName)) {
|
||||||
BeanDefinition existingBeanDef = registry.getBeanDefinition(beanName);
|
BeanDefinition existingBeanDef = registry.getBeanDefinition(beanName);
|
||||||
// is the existing bean definition one that was created by JavaConfig?
|
// is the existing bean definition one that was created from a configuration class?
|
||||||
if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
|
if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
|
||||||
// no -> then it's an external override, probably XML
|
// no -> then it's an external override, probably XML
|
||||||
// overriding is legal, return immediately
|
// overriding is legal, return immediately
|
||||||
|
|
@ -132,48 +136,46 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method.getAnnotation(Primary.class) != null) {
|
if (metadata.hasAnnotation(Primary.class.getName())) {
|
||||||
beanDef.setPrimary(true);
|
beanDef.setPrimary(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// is this bean to be instantiated lazily?
|
// is this bean to be instantiated lazily?
|
||||||
Lazy lazy = method.getAnnotation(Lazy.class);
|
if (metadata.hasAnnotation(Lazy.class.getName())) {
|
||||||
if (lazy != null) {
|
beanDef.setLazyInit((Boolean) metadata.getAnnotationAttributes(Lazy.class.getName()).get("value"));
|
||||||
beanDef.setLazyInit(lazy.value());
|
|
||||||
}
|
}
|
||||||
else {
|
else if (configClass.getMetadata().hasAnnotation(Lazy.class.getName())){
|
||||||
Lazy defaultLazy = configClass.getAnnotation(Lazy.class);
|
beanDef.setLazyInit((Boolean) configClass.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value"));
|
||||||
if (defaultLazy != null) {
|
}
|
||||||
beanDef.setLazyInit(defaultLazy.value());
|
|
||||||
|
if (metadata.hasAnnotation(DependsOn.class.getName())) {
|
||||||
|
String[] dependsOn = (String[]) metadata.getAnnotationAttributes(DependsOn.class.getName()).get("value");
|
||||||
|
if (dependsOn.length > 0) {
|
||||||
|
beanDef.setDependsOn(dependsOn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DependsOn dependsOn = method.getAnnotation(DependsOn.class);
|
Autowire autowire = (Autowire) beanAttributes.get("autowire");
|
||||||
if (dependsOn != null && dependsOn.value().length > 0) {
|
|
||||||
beanDef.setDependsOn(dependsOn.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
Autowire autowire = bean.autowire();
|
|
||||||
if (autowire.isAutowire()) {
|
if (autowire.isAutowire()) {
|
||||||
beanDef.setAutowireMode(autowire.value());
|
beanDef.setAutowireMode(autowire.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
String initMethodName = bean.initMethod();
|
String initMethodName = (String) beanAttributes.get("initMethod");
|
||||||
if (StringUtils.hasText(initMethodName)) {
|
if (StringUtils.hasText(initMethodName)) {
|
||||||
beanDef.setInitMethodName(initMethodName);
|
beanDef.setInitMethodName(initMethodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
String destroyMethodName = bean.destroyMethod();
|
String destroyMethodName = (String) beanAttributes.get("destroyMethod");
|
||||||
if (StringUtils.hasText(destroyMethodName)) {
|
if (StringUtils.hasText(destroyMethodName)) {
|
||||||
beanDef.setDestroyMethodName(destroyMethodName);
|
beanDef.setDestroyMethodName(destroyMethodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// consider scoping
|
// consider scoping
|
||||||
Scope scope = method.getAnnotation(Scope.class);
|
|
||||||
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
|
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
|
||||||
if (scope != null) {
|
if (metadata.hasAnnotation(Scope.class.getName())) {
|
||||||
beanDef.setScope(scope.value());
|
Map<String, Object> scopeAttributes = metadata.getAnnotationAttributes(Scope.class.getName());
|
||||||
proxyMode = scope.proxyMode();
|
beanDef.setScope((String) scopeAttributes.get("value"));
|
||||||
|
proxyMode = (ScopedProxyMode) scopeAttributes.get("proxyMode");
|
||||||
if (proxyMode == ScopedProxyMode.DEFAULT) {
|
if (proxyMode == ScopedProxyMode.DEFAULT) {
|
||||||
proxyMode = ScopedProxyMode.NO;
|
proxyMode = ScopedProxyMode.NO;
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +190,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getName(), beanName));
|
logger.debug(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getMetadata().getClassName(), beanName));
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.registerBeanDefinition(beanName, beanDefToRegister);
|
registry.registerBeanDefinition(beanName, beanDefToRegister);
|
||||||
|
|
@ -196,11 +198,11 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition created
|
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
|
||||||
* by JavaConfig as opposed to any other configuration source. Used in bean overriding cases
|
* created from a configuration class as opposed to any other configuration source.
|
||||||
* where it's necessary to determine whether the bean definition was created externally.
|
* Used in bean overriding cases where it's necessary to determine whether the bean
|
||||||
|
* definition was created externally.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
|
||||||
private class ConfigurationClassBeanDefinition extends RootBeanDefinition {
|
private class ConfigurationClassBeanDefinition extends RootBeanDefinition {
|
||||||
|
|
||||||
public ConfigurationClassBeanDefinition() {
|
public ConfigurationClassBeanDefinition() {
|
||||||
|
|
|
||||||
|
|
@ -60,17 +60,14 @@ class ConfigurationClassEnhancer {
|
||||||
*/
|
*/
|
||||||
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
|
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
|
||||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||||
|
this.callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
||||||
callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
this.callbackInstances.add(NoOp.INSTANCE);
|
||||||
callbackInstances.add(NoOp.INSTANCE);
|
for (Callback callback : this.callbackInstances) {
|
||||||
|
this.callbackTypes.add(callback.getClass());
|
||||||
for (Callback callback : callbackInstances) {
|
|
||||||
callbackTypes.add(callback.getClass());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the callback filter to return the index of the BeanMethodInterceptor when
|
// Set up the callback filter to return the index of the BeanMethodInterceptor when
|
||||||
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
||||||
callbackFilter = new CallbackFilter() {
|
this.callbackFilter = new CallbackFilter() {
|
||||||
public int accept(Method candidateMethod) {
|
public int accept(Method candidateMethod) {
|
||||||
return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null) ? 0 : 1;
|
return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
@ -84,12 +81,12 @@ class ConfigurationClassEnhancer {
|
||||||
* @return fully-qualified name of the enhanced subclass
|
* @return fully-qualified name of the enhanced subclass
|
||||||
*/
|
*/
|
||||||
public Class enhance(Class configClass) {
|
public Class enhance(Class configClass) {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.info("Enhancing " + configClass.getName());
|
logger.debug("Enhancing " + configClass.getName());
|
||||||
}
|
}
|
||||||
Class<?> enhancedClass = createClass(newEnhancer(configClass));
|
Class<?> enhancedClass = createClass(newEnhancer(configClass));
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.info(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
||||||
configClass.getName(), enhancedClass.getName()));
|
configClass.getName(), enhancedClass.getName()));
|
||||||
}
|
}
|
||||||
return enhancedClass;
|
return enhancedClass;
|
||||||
|
|
@ -100,17 +97,14 @@ class ConfigurationClassEnhancer {
|
||||||
*/
|
*/
|
||||||
private Enhancer newEnhancer(Class<?> superclass) {
|
private Enhancer newEnhancer(Class<?> superclass) {
|
||||||
Enhancer enhancer = new Enhancer();
|
Enhancer enhancer = new Enhancer();
|
||||||
|
// Because callbackFilter and callbackTypes are dynamically populated
|
||||||
// because callbackFilter and callbackTypes are dynamically populated
|
|
||||||
// there's no opportunity for caching. This does not appear to be causing
|
// there's no opportunity for caching. This does not appear to be causing
|
||||||
// any performance problem.
|
// any performance problem.
|
||||||
enhancer.setUseCache(false);
|
enhancer.setUseCache(false);
|
||||||
|
|
||||||
enhancer.setSuperclass(superclass);
|
enhancer.setSuperclass(superclass);
|
||||||
enhancer.setUseFactory(false);
|
enhancer.setUseFactory(false);
|
||||||
enhancer.setCallbackFilter(callbackFilter);
|
enhancer.setCallbackFilter(this.callbackFilter);
|
||||||
enhancer.setCallbackTypes(callbackTypes.toArray(new Class[callbackTypes.size()]));
|
enhancer.setCallbackTypes(this.callbackTypes.toArray(new Class[this.callbackTypes.size()]));
|
||||||
|
|
||||||
return enhancer;
|
return enhancer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,7 +114,7 @@ class ConfigurationClassEnhancer {
|
||||||
*/
|
*/
|
||||||
private Class<?> createClass(Enhancer enhancer) {
|
private Class<?> createClass(Enhancer enhancer) {
|
||||||
Class<?> subclass = enhancer.createClass();
|
Class<?> subclass = enhancer.createClass();
|
||||||
Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[callbackInstances.size()]));
|
Enhancer.registerCallbacks(subclass, this.callbackInstances.toArray(new Callback[this.callbackInstances.size()]));
|
||||||
return subclass;
|
return subclass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,7 +136,6 @@ class ConfigurationClassEnhancer {
|
||||||
this.beanFactory = beanFactory;
|
this.beanFactory = beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
|
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
|
||||||
* existence of this bean object.
|
* existence of this bean object.
|
||||||
|
|
@ -161,7 +154,7 @@ class ConfigurationClassEnhancer {
|
||||||
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
||||||
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
|
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
|
||||||
String scopedBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
|
String scopedBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
|
||||||
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
|
if (this.beanFactory.isCurrentlyInCreation(scopedBeanName)) {
|
||||||
beanName = scopedBeanName;
|
beanName = scopedBeanName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +163,7 @@ class ConfigurationClassEnhancer {
|
||||||
// container for already cached instances
|
// container for already cached instances
|
||||||
if (factoryContainsBean(beanName)) {
|
if (factoryContainsBean(beanName)) {
|
||||||
// we have an already existing cached instance of this bean -> retrieve it
|
// we have an already existing cached instance of this bean -> retrieve it
|
||||||
Object cachedBean = beanFactory.getBean(beanName);
|
Object cachedBean = this.beanFactory.getBean(beanName);
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Returning cached object [%s] for @Bean method %s.%s",
|
logger.debug(String.format("Returning cached object [%s] for @Bean method %s.%s",
|
||||||
cachedBean, method.getDeclaringClass().getSimpleName(), beanName));
|
cachedBean, method.getDeclaringClass().getSimpleName(), beanName));
|
||||||
|
|
@ -185,18 +178,18 @@ class ConfigurationClassEnhancer {
|
||||||
/**
|
/**
|
||||||
* Check the beanFactory to see whether the bean named <var>beanName</var> already
|
* Check the beanFactory to see whether the bean named <var>beanName</var> already
|
||||||
* exists. Accounts for the fact that the requested bean may be "in creation", i.e.:
|
* exists. Accounts for the fact that the requested bean may be "in creation", i.e.:
|
||||||
* we're in the middle of servicing the initial request for this bean. From JavaConfig's
|
* we're in the middle of servicing the initial request for this bean. From an enhanced
|
||||||
* perspective, this means that the bean does not actually yet exist, and that it is now
|
* factory method's perspective, this means that the bean does not actually yet exist,
|
||||||
* our job to create it for the first time by executing the logic in the corresponding
|
* and that it is now our job to create it for the first time by executing the logic
|
||||||
* Bean method.
|
* in the corresponding factory method.
|
||||||
* <p>Said another way, this check repurposes
|
* <p>Said another way, this check repurposes
|
||||||
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
|
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
|
||||||
* the container is calling this method or the user is calling this method.
|
* the container is calling this method or the user is calling this method.
|
||||||
* @param beanName name of bean to check for
|
* @param beanName name of bean to check for
|
||||||
* @return true if <var>beanName</var> already exists in the factory
|
* @return whether <var>beanName</var> already exists in the factory
|
||||||
*/
|
*/
|
||||||
private boolean factoryContainsBean(String beanName) {
|
private boolean factoryContainsBean(String beanName) {
|
||||||
return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName);
|
return (this.beanFactory.containsBean(beanName) && !this.beanFactory.isCurrentlyInCreation(beanName));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeanMetadataElement;
|
|
||||||
import org.springframework.beans.factory.parsing.Location;
|
import org.springframework.beans.factory.parsing.Location;
|
||||||
import org.springframework.beans.factory.parsing.Problem;
|
import org.springframework.beans.factory.parsing.Problem;
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
|
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
|
||||||
|
|
@ -38,213 +31,46 @@ import org.springframework.core.io.ClassPathResource;
|
||||||
* @see ConfigurationClassParser
|
* @see ConfigurationClassParser
|
||||||
* @see ConfigurationClassBeanDefinitionReader
|
* @see ConfigurationClassBeanDefinitionReader
|
||||||
*/
|
*/
|
||||||
final class ConfigurationClassMethod implements BeanMetadataElement {
|
final class ConfigurationClassMethod {
|
||||||
|
|
||||||
private final String name;
|
private final MethodMetadata metadata;
|
||||||
|
|
||||||
private final int modifiers;
|
private final ConfigurationClass declaringClass;
|
||||||
|
|
||||||
private final ReturnType returnType;
|
|
||||||
|
|
||||||
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
|
||||||
|
|
||||||
private transient ConfigurationClass declaringClass;
|
|
||||||
|
|
||||||
private transient Object source;
|
|
||||||
|
|
||||||
|
|
||||||
public ConfigurationClassMethod(String name, int modifiers, ReturnType returnType, Annotation... annotations) {
|
public ConfigurationClassMethod(MethodMetadata metadata, ConfigurationClass declaringClass) {
|
||||||
Assert.hasText(name);
|
this.metadata = metadata;
|
||||||
this.name = name;
|
|
||||||
|
|
||||||
Assert.notNull(annotations);
|
|
||||||
for (Annotation annotation : annotations) {
|
|
||||||
this.annotations.add(annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers);
|
|
||||||
this.modifiers = modifiers;
|
|
||||||
|
|
||||||
Assert.notNull(returnType);
|
|
||||||
this.returnType = returnType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReturnType getReturnType() {
|
|
||||||
return returnType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.lang.reflect.Modifier
|
|
||||||
*/
|
|
||||||
public int getModifiers() {
|
|
||||||
return modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the annotation on this method matching <var>annoType</var> or
|
|
||||||
* {@literal null} if not present.
|
|
||||||
* @see #getRequiredAnnotation(Class)
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <A extends Annotation> A getAnnotation(Class<A> annoType) {
|
|
||||||
for (Annotation anno : annotations)
|
|
||||||
if (anno.annotationType().equals(annoType))
|
|
||||||
return (A) anno;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the annotation on this method matching <var>annoType</var>
|
|
||||||
* @throws {@link IllegalStateException} if not present
|
|
||||||
* @see #getAnnotation(Class)
|
|
||||||
*/
|
|
||||||
public <T extends Annotation> T getRequiredAnnotation(Class<T> annoType) {
|
|
||||||
T anno = getAnnotation(annoType);
|
|
||||||
if (anno == null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
String.format("required annotation %s is not present on %s", annoType.getSimpleName(), this));
|
|
||||||
}
|
|
||||||
return anno;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up a bi-directional relationship between this method and its declaring class.
|
|
||||||
*
|
|
||||||
* @see ConfigurationClass#addMethod(ConfigurationClassMethod)
|
|
||||||
*/
|
|
||||||
public void setDeclaringClass(ConfigurationClass declaringClass) {
|
|
||||||
this.declaringClass = declaringClass;
|
this.declaringClass = declaringClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public MethodMetadata getMetadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
public ConfigurationClass getDeclaringClass() {
|
public ConfigurationClass getDeclaringClass() {
|
||||||
return declaringClass;
|
return this.declaringClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSource(Object source) {
|
public Location getResourceLocation() {
|
||||||
this.source = source;
|
return new Location(this.declaringClass.getResource(), this.metadata.getMethodName());
|
||||||
}
|
|
||||||
|
|
||||||
public Object getSource() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location getLocation() {
|
|
||||||
if (declaringClass == null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"declaringClass property is null. Call setDeclaringClass() before calling getLocation()");
|
|
||||||
}
|
|
||||||
return new Location(declaringClass.getLocation().getResource(), getSource());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(ProblemReporter problemReporter) {
|
public void validate(ProblemReporter problemReporter) {
|
||||||
if (Modifier.isPrivate(getModifiers())) {
|
if (this.declaringClass.getMetadata().hasAnnotation(Configuration.class.getName()) && !getMetadata().isOverridable()) {
|
||||||
problemReporter.error(new PrivateMethodError());
|
problemReporter.error(new NonOverridableMethodError());
|
||||||
}
|
|
||||||
if (Modifier.isFinal(getModifiers())) {
|
|
||||||
problemReporter.error(new FinalMethodError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static class ReturnType implements BeanMetadataElement {
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private boolean isInterface;
|
|
||||||
|
|
||||||
private transient Object source;
|
|
||||||
|
|
||||||
|
|
||||||
public ReturnType(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the fully-qualified name of this class.
|
|
||||||
*/
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the fully-qualified name of this class.
|
|
||||||
*/
|
|
||||||
public void setName(String className) {
|
|
||||||
this.name = className;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'.
|
|
||||||
*/
|
|
||||||
public String getSimpleName() {
|
|
||||||
return name == null ? null : ClassUtils.getShortName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the class represented by this ModelClass instance is an interface.
|
|
||||||
*/
|
|
||||||
public boolean isInterface() {
|
|
||||||
return isInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signifies that this class is (true) or is not (false) an interface.
|
|
||||||
*/
|
|
||||||
public void setInterface(boolean isInterface) {
|
|
||||||
this.isInterface = isInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a resource path-formatted representation of the .java file that declares this
|
|
||||||
* class
|
|
||||||
*/
|
|
||||||
public Object getSource() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the source location for this class. Must be a resource-path formatted string.
|
|
||||||
*
|
|
||||||
* @param source resource path to the .java file that declares this class.
|
|
||||||
*/
|
|
||||||
public void setSource(Object source) {
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location getLocation() {
|
|
||||||
if (getName() == null) {
|
|
||||||
throw new IllegalStateException("'name' property is null. Call setName() before calling getLocation()");
|
|
||||||
}
|
|
||||||
return new Location(new ClassPathResource(ClassUtils.convertClassNameToResourcePath(getName())), getSource());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Bean} methods must be non-private in order to accommodate CGLIB.
|
* {@link Bean} methods must be overridable in order to accommodate CGLIB.
|
||||||
*/
|
*/
|
||||||
private class PrivateMethodError extends Problem {
|
private class NonOverridableMethodError extends Problem {
|
||||||
|
|
||||||
public PrivateMethodError() {
|
public NonOverridableMethodError() {
|
||||||
super(String.format("Method '%s' must not be private; increase the method's visibility to continue",
|
super(String.format("Method '%s' must not be private, final or static; change the method's modifiers to continue",
|
||||||
getName()), ConfigurationClassMethod.this.getLocation());
|
getMetadata().getMethodName()), getResourceLocation());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link Bean} methods must be non-final in order to accommodate CGLIB.
|
|
||||||
*/
|
|
||||||
private class FinalMethodError extends Problem {
|
|
||||||
|
|
||||||
public FinalMethodError() {
|
|
||||||
super(String.format("Method '%s' must not be final; remove the final modifier to continue",
|
|
||||||
getName()), ConfigurationClassMethod.this.getLocation());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,23 @@
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
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.parsing.ProblemReporter;
|
||||||
import org.springframework.core.type.classreading.SimpleMetadataReader;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||||
|
import org.springframework.core.type.classreading.MetadataReader;
|
||||||
|
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a {@link Configuration} class definition, populating a configuration model.
|
* Parses a {@link Configuration} class definition, populating a configuration model.
|
||||||
|
|
@ -41,19 +51,20 @@ import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||||
*/
|
*/
|
||||||
class ConfigurationClassParser {
|
class ConfigurationClassParser {
|
||||||
|
|
||||||
private final SimpleMetadataReaderFactory metadataReaderFactory;
|
private final MetadataReaderFactory metadataReaderFactory;
|
||||||
|
|
||||||
private final ProblemReporter problemReporter;
|
private final ProblemReporter problemReporter;
|
||||||
|
|
||||||
private final Set<ConfigurationClass> model;
|
private final Set<ConfigurationClass> model;
|
||||||
|
|
||||||
|
private final Stack<ConfigurationClass> importStack = new ImportStack();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link ConfigurationClassParser} instance that will be used to populate a
|
* Create a new {@link ConfigurationClassParser} instance that will be used
|
||||||
* configuration model.
|
* to populate a configuration model.
|
||||||
* @param model model to be populated by each successive call to {@link #parse}
|
|
||||||
*/
|
*/
|
||||||
public ConfigurationClassParser(SimpleMetadataReaderFactory metadataReaderFactory, ProblemReporter problemReporter) {
|
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory, ProblemReporter problemReporter) {
|
||||||
this.metadataReaderFactory = metadataReaderFactory;
|
this.metadataReaderFactory = metadataReaderFactory;
|
||||||
this.problemReporter = problemReporter;
|
this.problemReporter = problemReporter;
|
||||||
this.model = new LinkedHashSet<ConfigurationClass>();
|
this.model = new LinkedHashSet<ConfigurationClass>();
|
||||||
|
|
@ -67,17 +78,80 @@ class ConfigurationClassParser {
|
||||||
* (assumes that this configuration class was configured via XML)
|
* (assumes that this configuration class was configured via XML)
|
||||||
*/
|
*/
|
||||||
public void parse(String className, String beanName) throws IOException {
|
public void parse(String className, String beanName) throws IOException {
|
||||||
SimpleMetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
|
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
|
||||||
ConfigurationClass configClass = new ConfigurationClass();
|
processConfigurationClass(new ConfigurationClass(reader, beanName));
|
||||||
configClass.setBeanName(beanName);
|
}
|
||||||
reader.getClassReader().accept(new ConfigurationClassVisitor(configClass, model, problemReporter, metadataReaderFactory), false);
|
|
||||||
|
/**
|
||||||
|
* Parse the specified {@link Configuration @Configuration} class.
|
||||||
|
* @param clazz the Clazz to parse
|
||||||
|
* @param beanName may be null, but if populated represents the bean id
|
||||||
|
* (assumes that this configuration class was configured via XML)
|
||||||
|
*/
|
||||||
|
public void parse(Class clazz, String beanName) throws IOException {
|
||||||
|
processConfigurationClass(new ConfigurationClass(clazz, beanName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
|
||||||
|
AnnotationMetadata metadata = configClass.getMetadata();
|
||||||
|
while (metadata != null) {
|
||||||
|
doProcessConfigurationClass(configClass, metadata);
|
||||||
|
String superClassName = metadata.getSuperClassName();
|
||||||
|
if (superClassName != null && !Object.class.getName().equals(superClassName)) {
|
||||||
|
if (metadata instanceof StandardAnnotationMetadata) {
|
||||||
|
Class clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
|
||||||
|
metadata = new StandardAnnotationMetadata(clazz.getSuperclass());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superClassName);
|
||||||
|
metadata = reader.getAnnotationMetadata();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
metadata = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
model.add(configClass);
|
model.add(configClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
|
||||||
|
if (metadata.hasAnnotation(Import.class.getName())) {
|
||||||
|
processImport(configClass, (String[]) metadata.getAnnotationAttributes(Import.class.getName()).get("value"));
|
||||||
|
}
|
||||||
|
Set<MethodMetadata> methods = metadata.getAnnotatedMethods(Bean.class.getName());
|
||||||
|
for (MethodMetadata methodMetadata : methods) {
|
||||||
|
configClass.addMethod(new ConfigurationClassMethod(methodMetadata, configClass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processImport(ConfigurationClass configClass, String[] classesToImport) throws IOException {
|
||||||
|
if (this.importStack.contains(configClass)) {
|
||||||
|
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.importStack.push(configClass);
|
||||||
|
for (String classToImport : classesToImport) {
|
||||||
|
processClassToImport(classToImport);
|
||||||
|
}
|
||||||
|
this.importStack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processClassToImport(String classToImport) throws IOException {
|
||||||
|
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(classToImport);
|
||||||
|
AnnotationMetadata metadata = reader.getAnnotationMetadata();
|
||||||
|
if (!metadata.hasAnnotation(Configuration.class.getName())) {
|
||||||
|
this.problemReporter.error(
|
||||||
|
new NonAnnotatedConfigurationProblem(metadata.getClassName(), reader.getResource()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
processConfigurationClass(new ConfigurationClass(reader, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recurse through the model validating each {@link ConfigurationClass}.
|
* Recurse through the model validating each {@link ConfigurationClass}.
|
||||||
* @param problemReporter {@link ProblemReporter} against which any validation errors
|
|
||||||
* will be registered
|
|
||||||
* @see ConfigurationClass#validate
|
* @see ConfigurationClass#validate
|
||||||
*/
|
*/
|
||||||
public void validate() {
|
public void validate() {
|
||||||
|
|
@ -90,4 +164,78 @@ class ConfigurationClassParser {
|
||||||
return this.model;
|
return this.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private static class ImportStack extends Stack<ConfigurationClass> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplified contains() implementation that tests to see if any {@link ConfigurationClass}
|
||||||
|
* exists within this stack that has the same name as <var>elem</var>. Elem must be of
|
||||||
|
* type ConfigurationClass.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object elem) {
|
||||||
|
ConfigurationClass configClass = (ConfigurationClass) elem;
|
||||||
|
Comparator<ConfigurationClass> comparator = new Comparator<ConfigurationClass>() {
|
||||||
|
public int compare(ConfigurationClass first, ConfigurationClass second) {
|
||||||
|
return first.getMetadata().getClassName().equals(second.getMetadata().getClassName()) ? 0 : 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (Collections.binarySearch(this, configClass, comparator) != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a stack containing (in order)
|
||||||
|
* <ol>
|
||||||
|
* <li>com.acme.Foo</li>
|
||||||
|
* <li>com.acme.Bar</li>
|
||||||
|
* <li>com.acme.Baz</li>
|
||||||
|
* </ol>
|
||||||
|
* Returns "Foo->Bar->Baz". In the case of an empty stack, returns empty string.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
Iterator<ConfigurationClass> iterator = iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
builder.append(iterator.next().getSimpleName());
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
builder.append("->");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration classes must be annotated with {@link Configuration @Configuration}.
|
||||||
|
*/
|
||||||
|
private static class NonAnnotatedConfigurationProblem extends Problem {
|
||||||
|
|
||||||
|
public NonAnnotatedConfigurationProblem(String className, Resource resource) {
|
||||||
|
super(String.format("%s was imported as a @Configuration class but was not actually annotated " +
|
||||||
|
"with @Configuration. Annotate the class or do not attempt to process it.", className),
|
||||||
|
new Location(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Problem} registered upon detection of a circular {@link Import}.
|
||||||
|
*/
|
||||||
|
private static class CircularImportProblem extends Problem {
|
||||||
|
|
||||||
|
public CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> importStack) {
|
||||||
|
super(String.format("A circular @Import has been detected: " +
|
||||||
|
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
|
||||||
|
"already present in the current import stack [%s]",
|
||||||
|
importStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
|
||||||
|
attemptedImport.getSimpleName(), importStack),
|
||||||
|
new Location(importStack.peek().getResource())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,23 +35,23 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
import org.springframework.core.Conventions;
|
import org.springframework.core.Conventions;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||||
import org.springframework.core.type.classreading.MetadataReader;
|
import org.springframework.core.type.classreading.MetadataReader;
|
||||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
||||||
* {@link Configuration @Configuration} classes.
|
* {@link Configuration @Configuration} classes.
|
||||||
* <p>
|
*
|
||||||
* Registered by default when using {@literal <context:annotation-config/>} or
|
* <p>Registered by default when using {@literal <context:annotation-config/>} or
|
||||||
* {@literal <context:component-scan/>}. Otherwise, may be declared manually as
|
* {@literal <context:component-scan/>}. Otherwise, may be declared manually as
|
||||||
* with any other BeanFactoryPostProcessor.
|
* with any other BeanFactoryPostProcessor.
|
||||||
* <p>
|
*
|
||||||
* This post processor is {@link Ordered#HIGHEST_PRECEDENCE} as it's important
|
* <p>This post processor is {@link Ordered#HIGHEST_PRECEDENCE} as it is important
|
||||||
* that any {@link Bean} methods declared in Configuration classes have their
|
* that any {@link Bean} methods declared in Configuration classes have their
|
||||||
* respective bean definitions registered before any other BeanFactoryPostProcessor
|
* respective bean definitions registered before any other BeanFactoryPostProcessor
|
||||||
* executes.
|
* executes.
|
||||||
|
|
@ -62,32 +62,33 @@ import org.springframework.util.ClassUtils;
|
||||||
*/
|
*/
|
||||||
public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
|
public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
|
||||||
|
|
||||||
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
||||||
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
|
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
|
||||||
|
|
||||||
|
private static final String CONFIGURATION_CLASS_FULL = "full";
|
||||||
|
|
||||||
|
private static final String CONFIGURATION_CLASS_LITE = "lite";
|
||||||
|
|
||||||
|
|
||||||
/** Whether the CGLIB2 library is present on the classpath */
|
/** Whether the CGLIB2 library is present on the classpath */
|
||||||
private static final boolean cglibAvailable = ClassUtils.isPresent(
|
private static final boolean cglibAvailable = ClassUtils.isPresent(
|
||||||
"net.sf.cglib.proxy.Enhancer", ConfigurationClassPostProcessor.class.getClassLoader());
|
"net.sf.cglib.proxy.Enhancer", ConfigurationClassPostProcessor.class.getClassLoader());
|
||||||
|
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(ConfigurationClassPostProcessor.class);
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to register any problems detected with {@link Configuration} or {@link Bean}
|
|
||||||
* declarations. For instance, a Bean method marked as {@literal final} is illegal
|
|
||||||
* and would be reported as a problem. Defaults to {@link FailFastProblemReporter},
|
|
||||||
* but is overridable with {@link #setProblemReporter}
|
|
||||||
*/
|
|
||||||
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
||||||
|
|
||||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||||
|
|
||||||
private SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
|
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the default {@link ProblemReporter}.
|
* Set the {@link ProblemReporter} to use.
|
||||||
* @param problemReporter custom problem reporter
|
* <p>Used to register any problems detected with {@link Configuration} or {@link Bean}
|
||||||
|
* declarations. For instance, a Bean method marked as {@literal final} is illegal
|
||||||
|
* and would be reported as a problem. Defaults to {@link FailFastProblemReporter}.
|
||||||
*/
|
*/
|
||||||
public void setProblemReporter(ProblemReporter problemReporter) {
|
public void setProblemReporter(ProblemReporter problemReporter) {
|
||||||
this.problemReporter = problemReporter;
|
this.problemReporter = problemReporter;
|
||||||
|
|
@ -116,34 +117,38 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||||
enhanceConfigurationClasses(beanFactory);
|
enhanceConfigurationClasses(beanFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build and validate a configuration model based on the registry of
|
* Build and validate a configuration model based on the registry of
|
||||||
* {@link Configuration} classes.
|
* {@link Configuration} classes.
|
||||||
*/
|
*/
|
||||||
protected final void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
protected final void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
||||||
Set<BeanDefinitionHolder> configBeanDefs = new LinkedHashSet<BeanDefinitionHolder>();
|
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
|
||||||
for (String beanName : registry.getBeanDefinitionNames()) {
|
for (String beanName : registry.getBeanDefinitionNames()) {
|
||||||
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
||||||
if (checkConfigurationClassBeanDefinition(beanDef)) {
|
if (checkConfigurationClassCandidate(beanDef)) {
|
||||||
configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
|
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return immediately if no @Configuration classes were found
|
// Return immediately if no @Configuration classes were found
|
||||||
if (configBeanDefs.isEmpty()) {
|
if (configCandidates.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate a new configuration model by parsing each @Configuration classes
|
// Populate a new configuration model by parsing each @Configuration classes
|
||||||
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter);
|
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter);
|
||||||
for (BeanDefinitionHolder holder : configBeanDefs) {
|
for (BeanDefinitionHolder holder : configCandidates) {
|
||||||
String beanClassName = holder.getBeanDefinition().getBeanClassName();
|
BeanDefinition bd = holder.getBeanDefinition();
|
||||||
try {
|
try {
|
||||||
parser.parse(beanClassName, holder.getBeanName());
|
if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
|
||||||
|
parser.parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parser.parse(bd.getBeanClassName(), holder.getBeanName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IOException ex) {
|
catch (IOException ex) {
|
||||||
throw new BeanDefinitionStoreException("Failed to load bean class [" + beanClassName + "]", ex);
|
throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parser.validate();
|
parser.validate();
|
||||||
|
|
@ -152,6 +157,42 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||||
new ConfigurationClassBeanDefinitionReader(registry).loadBeanDefinitions(parser.getModel());
|
new ConfigurationClassBeanDefinitionReader(registry).loadBeanDefinitions(parser.getModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkConfigurationClassCandidate(BeanDefinition beanDef) {
|
||||||
|
AnnotationMetadata metadata;
|
||||||
|
|
||||||
|
// 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()) {
|
||||||
|
Class beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
|
||||||
|
metadata = new StandardAnnotationMetadata(beanClass);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String className = beanDef.getBeanClassName();
|
||||||
|
try {
|
||||||
|
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(className);
|
||||||
|
metadata = metadataReader.getAnnotationMetadata();
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Could not find class file for introspecting factory methods: " + className, ex);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.hasAnnotation(Configuration.class.getName())) {
|
||||||
|
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (metadata.hasAnnotation(Component.class.getName())) {
|
||||||
|
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
|
* Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
|
||||||
* any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
|
* any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
|
||||||
|
|
@ -162,7 +203,7 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||||
Set<BeanDefinitionHolder> configBeanDefs = new LinkedHashSet<BeanDefinitionHolder>();
|
Set<BeanDefinitionHolder> configBeanDefs = new LinkedHashSet<BeanDefinitionHolder>();
|
||||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||||
if ("full".equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE))) {
|
if (CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE))) {
|
||||||
configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
|
configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,48 +238,4 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether the BeanDefinition's beanClass (or its ancestry) is
|
|
||||||
* {@link Configuration}-annotated, false if no beanClass is specified.
|
|
||||||
*/
|
|
||||||
private boolean checkConfigurationClassBeanDefinition(BeanDefinition beanDef) {
|
|
||||||
// accommodating SPR-5655
|
|
||||||
if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
|
|
||||||
if (AnnotationUtils.findAnnotation(
|
|
||||||
((AbstractBeanDefinition) beanDef).getBeanClass(), Configuration.class) != null) {
|
|
||||||
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "full");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (AnnotationUtils.findAnnotation(
|
|
||||||
((AbstractBeanDefinition) beanDef).getBeanClass(), Component.class) != null) {
|
|
||||||
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "lite");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String className = beanDef.getBeanClassName();
|
|
||||||
while (className != null && !(className.equals(Object.class.getName()))) {
|
|
||||||
try {
|
|
||||||
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(className);
|
|
||||||
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
|
|
||||||
if (metadata.hasAnnotation(Configuration.class.getName())) {
|
|
||||||
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "full");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (metadata.hasAnnotation(Component.class.getName())) {
|
|
||||||
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "lite");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
className = metadata.getSuperClassName();
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new BeanDefinitionStoreException("Failed to load class file [" + className + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,282 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 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 static java.lang.String.*;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Various utility methods commonly used when interacting with ASM, classloading
|
|
||||||
* and creating {@link ConfigurationClassAnnotation} instances.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
class ConfigurationClassReaderUtils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a type descriptor to a classname suitable for classloading with
|
|
||||||
* Class.forName().
|
|
||||||
*
|
|
||||||
* @param typeDescriptor see ASM guide section 2.1.3
|
|
||||||
*/
|
|
||||||
public static String convertAsmTypeDescriptorToClassName(String typeDescriptor) {
|
|
||||||
final String internalName; // See ASM guide section 2.1.2
|
|
||||||
|
|
||||||
if ("V".equals(typeDescriptor))
|
|
||||||
return Void.class.getName();
|
|
||||||
if ("I".equals(typeDescriptor))
|
|
||||||
return Integer.class.getName();
|
|
||||||
if ("Z".equals(typeDescriptor))
|
|
||||||
return Boolean.class.getName();
|
|
||||||
|
|
||||||
// strip the leading array/object/primitive identifier
|
|
||||||
if (typeDescriptor.startsWith("[["))
|
|
||||||
internalName = typeDescriptor.substring(3);
|
|
||||||
else if (typeDescriptor.startsWith("["))
|
|
||||||
internalName = typeDescriptor.substring(2);
|
|
||||||
else
|
|
||||||
internalName = typeDescriptor.substring(1);
|
|
||||||
|
|
||||||
// convert slashes to dots
|
|
||||||
String className = internalName.replace('/', '.');
|
|
||||||
|
|
||||||
// and strip trailing semicolon (if present)
|
|
||||||
if (className.endsWith(";"))
|
|
||||||
className = className.substring(0, internalName.length() - 1);
|
|
||||||
|
|
||||||
return className;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param methodDescriptor see ASM guide section 2.1.4
|
|
||||||
*/
|
|
||||||
public static String getReturnTypeFromAsmMethodDescriptor(String methodDescriptor) {
|
|
||||||
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1);
|
|
||||||
return convertAsmTypeDescriptorToClassName(returnTypeDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static Class<? extends Annotation> loadAnnotationType(String annoTypeDesc, ClassLoader classLoader) {
|
|
||||||
String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
|
||||||
try {
|
|
||||||
return (Class<? extends Annotation>) classLoader.loadClass(annoTypeName);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
throw new IllegalStateException("Could not load annotation type [" + annoTypeName + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link ConfigurationClassAnnotation} for {@code annoType}. JDK dynamic proxies are used,
|
|
||||||
* and the returned proxy implements both {@link ConfigurationClassAnnotation} and the annotation type.
|
|
||||||
* @param annoType annotation type that must be supplied and returned
|
|
||||||
* @param annoType type of annotation to create
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static ConfigurationClassAnnotation createMutableAnnotation(Class<? extends Annotation> annoType, ClassLoader classLoader) {
|
|
||||||
MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType);
|
|
||||||
Class<?>[] interfaces = new Class<?>[] {annoType, ConfigurationClassAnnotation.class};
|
|
||||||
return (ConfigurationClassAnnotation) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles calls to {@link ConfigurationClassAnnotation} attribute methods at runtime. Essentially
|
|
||||||
* emulates what JDK annotation dynamic proxies do.
|
|
||||||
*/
|
|
||||||
private static final class MutableAnnotationInvocationHandler implements InvocationHandler {
|
|
||||||
|
|
||||||
private final Class<? extends Annotation> annoType;
|
|
||||||
private final Map<String, Object> attributes = new HashMap<String, Object>();
|
|
||||||
private final Map<String, Class<?>> attributeTypes = new HashMap<String, Class<?>>();
|
|
||||||
|
|
||||||
public MutableAnnotationInvocationHandler(Class<? extends Annotation> annoType) {
|
|
||||||
// pre-populate the attributes hash will all the names
|
|
||||||
// and default values of the attributes defined in 'annoType'
|
|
||||||
Method[] attribs = annoType.getDeclaredMethods();
|
|
||||||
for (Method attrib : attribs) {
|
|
||||||
this.attributes.put(attrib.getName(), AnnotationUtils.getDefaultValue(annoType, attrib.getName()));
|
|
||||||
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.annoType = annoType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
||||||
Assert.isInstanceOf(Annotation.class, proxy);
|
|
||||||
|
|
||||||
String methodName = method.getName();
|
|
||||||
|
|
||||||
// first -> check to see if this method is an attribute on our annotation
|
|
||||||
if (attributes.containsKey(methodName))
|
|
||||||
return attributes.get(methodName);
|
|
||||||
|
|
||||||
|
|
||||||
// second -> is it a method from java.lang.annotation.Annotation?
|
|
||||||
if (methodName.equals("annotationType"))
|
|
||||||
return annoType;
|
|
||||||
|
|
||||||
|
|
||||||
// third -> is it a method from java.lang.Object?
|
|
||||||
if (methodName.equals("toString"))
|
|
||||||
return format("@%s(%s)", annoType.getName(), getAttribs());
|
|
||||||
|
|
||||||
if (methodName.equals("equals"))
|
|
||||||
return isEqualTo(proxy, args[0]);
|
|
||||||
|
|
||||||
if (methodName.equals("hashCode"))
|
|
||||||
return calculateHashCode(proxy);
|
|
||||||
|
|
||||||
|
|
||||||
// finally -> is it a method specified by MutableAnno?
|
|
||||||
if (methodName.equals("setAttributeValue")) {
|
|
||||||
attributes.put((String) args[0], args[1]);
|
|
||||||
return null; // setAttributeValue has a 'void' return type
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodName.equals("getAttributeType"))
|
|
||||||
return attributeTypes.get(args[0]);
|
|
||||||
|
|
||||||
throw new UnsupportedOperationException("this proxy does not support method: " + methodName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Conforms to the hashCode() specification for Annotation.
|
|
||||||
*
|
|
||||||
* @see Annotation#hashCode()
|
|
||||||
*/
|
|
||||||
private Object calculateHashCode(Object proxy) {
|
|
||||||
int sum = 0;
|
|
||||||
|
|
||||||
for (String attribName : attributes.keySet()) {
|
|
||||||
Object attribValue = attributes.get(attribName);
|
|
||||||
|
|
||||||
final int attribNameHashCode = attribName.hashCode();
|
|
||||||
final int attribValueHashCode;
|
|
||||||
|
|
||||||
if (attribValue == null)
|
|
||||||
// memberValue may be null when a mutable annotation is being added to a
|
|
||||||
// collection
|
|
||||||
// and before it has actually been visited (and populated) by
|
|
||||||
// MutableAnnotationVisitor
|
|
||||||
attribValueHashCode = 0;
|
|
||||||
else if (attribValue.getClass().isArray())
|
|
||||||
attribValueHashCode = Arrays.hashCode((Object[]) attribValue);
|
|
||||||
else
|
|
||||||
attribValueHashCode = attribValue.hashCode();
|
|
||||||
|
|
||||||
sum += (127 * attribNameHashCode) ^ attribValueHashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares <var>proxy</var> object and <var>other</var> object by comparing the return
|
|
||||||
* values of the methods specified by their common {@link Annotation} ancestry.
|
|
||||||
* <p/>
|
|
||||||
* <var>other</var> must be the same type as or a subtype of <var>proxy</var>. Will
|
|
||||||
* return false otherwise.
|
|
||||||
* <p/>
|
|
||||||
* Eagerly returns true if {@code proxy} == <var>other</var>
|
|
||||||
* </p>
|
|
||||||
* <p/>
|
|
||||||
* Conforms strictly to the equals() specification for Annotation
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @see Annotation#equals(Object)
|
|
||||||
*/
|
|
||||||
private Object isEqualTo(Object proxy, Object other) {
|
|
||||||
if (proxy == other)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (other == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!annoType.isAssignableFrom(other.getClass()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (String attribName : attributes.keySet()) {
|
|
||||||
Object thisVal;
|
|
||||||
Object thatVal;
|
|
||||||
|
|
||||||
try {
|
|
||||||
thisVal = attributes.get(attribName);
|
|
||||||
thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((thisVal == null) && (thatVal != null))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ((thatVal == null) && (thisVal != null))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (thatVal.getClass().isArray()) {
|
|
||||||
if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (thisVal instanceof Double) {
|
|
||||||
if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal)))
|
|
||||||
return false;
|
|
||||||
} else if (thisVal instanceof Float) {
|
|
||||||
if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal)))
|
|
||||||
return false;
|
|
||||||
} else if (!thisVal.equals(thatVal)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getAttribs() {
|
|
||||||
List<String> attribs = new ArrayList<String>();
|
|
||||||
for (String attribName : attributes.keySet()) {
|
|
||||||
attribs.add(format("%s=%s", attribName, attributes.get(attribName)));
|
|
||||||
}
|
|
||||||
return StringUtils.collectionToDelimitedString(attribs, ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the type of the given annotation attribute.
|
|
||||||
*/
|
|
||||||
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) {
|
|
||||||
try {
|
|
||||||
return annotationType.getDeclaredMethod(attributeName).getReturnType();
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new IllegalStateException("Could not introspect return type", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,445 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 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 java.io.IOException;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
|
||||||
import org.springframework.asm.Attribute;
|
|
||||||
import org.springframework.asm.ClassAdapter;
|
|
||||||
import org.springframework.asm.ClassReader;
|
|
||||||
import org.springframework.asm.ClassVisitor;
|
|
||||||
import org.springframework.asm.FieldVisitor;
|
|
||||||
import org.springframework.asm.Label;
|
|
||||||
import org.springframework.asm.MethodAdapter;
|
|
||||||
import org.springframework.asm.MethodVisitor;
|
|
||||||
import org.springframework.asm.Opcodes;
|
|
||||||
import org.springframework.asm.Type;
|
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
|
||||||
import org.springframework.beans.factory.parsing.Location;
|
|
||||||
import org.springframework.beans.factory.parsing.Problem;
|
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.core.type.classreading.SimpleMetadataReader;
|
|
||||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ASM {@link ClassVisitor} that visits a {@link Configuration} class, populating a
|
|
||||||
* {@link ConfigurationClass} instance with information gleaned along the way.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
* @author Juergen Hoeller
|
|
||||||
* @since 3.0
|
|
||||||
* @see ConfigurationClassParser
|
|
||||||
* @see ConfigurationClass
|
|
||||||
*/
|
|
||||||
class ConfigurationClassVisitor implements ClassVisitor {
|
|
||||||
|
|
||||||
private static final String OBJECT_DESC = ClassUtils.convertClassNameToResourcePath(Object.class.getName());
|
|
||||||
|
|
||||||
private final ConfigurationClass configClass;
|
|
||||||
|
|
||||||
private final Set<ConfigurationClass> model;
|
|
||||||
|
|
||||||
private final ProblemReporter problemReporter;
|
|
||||||
|
|
||||||
private final SimpleMetadataReaderFactory metadataReaderFactory;
|
|
||||||
|
|
||||||
private final Stack<ConfigurationClass> importStack;
|
|
||||||
|
|
||||||
|
|
||||||
public ConfigurationClassVisitor(ConfigurationClass configClass, Set<ConfigurationClass> model,
|
|
||||||
ProblemReporter problemReporter, SimpleMetadataReaderFactory metadataReaderFactory) {
|
|
||||||
|
|
||||||
this.configClass = configClass;
|
|
||||||
this.model = model;
|
|
||||||
this.problemReporter = problemReporter;
|
|
||||||
this.metadataReaderFactory = metadataReaderFactory;
|
|
||||||
this.importStack = new ImportStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConfigurationClassVisitor(ConfigurationClass configClass, Set<ConfigurationClass> model,
|
|
||||||
ProblemReporter problemReporter, SimpleMetadataReaderFactory metadataReaderFactory,
|
|
||||||
Stack<ConfigurationClass> importStack) {
|
|
||||||
this.configClass = configClass;
|
|
||||||
this.model = model;
|
|
||||||
this.problemReporter = problemReporter;
|
|
||||||
this.metadataReaderFactory = metadataReaderFactory;
|
|
||||||
this.importStack = importStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3, String superTypeDesc, String[] arg5) {
|
|
||||||
visitSuperType(superTypeDesc);
|
|
||||||
|
|
||||||
configClass.setName(ClassUtils.convertResourcePathToClassName(classTypeDesc));
|
|
||||||
|
|
||||||
// ASM always adds ACC_SUPER to the opcodes/modifiers for class definitions.
|
|
||||||
// Unknown as to why (JavaDoc is silent on the matter), but it should be
|
|
||||||
// eliminated in order to comply with java.lang.reflect.Modifier values.
|
|
||||||
configClass.setModifiers(modifiers - Opcodes.ACC_SUPER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void visitSuperType(String superTypeDesc) {
|
|
||||||
// traverse up the type hierarchy unless the next ancestor is java.lang.Object
|
|
||||||
if (OBJECT_DESC.equals(superTypeDesc)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, problemReporter, metadataReaderFactory, importStack);
|
|
||||||
String superClassName = ClassUtils.convertResourcePathToClassName(superTypeDesc);
|
|
||||||
try {
|
|
||||||
SimpleMetadataReader reader = this.metadataReaderFactory.getMetadataReader(superClassName);
|
|
||||||
reader.getClassReader().accept(visitor, false);
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new BeanDefinitionStoreException("Failed to load bean super class [" + superClassName + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitSource(String sourceFile, String debug) {
|
|
||||||
String resourcePath =
|
|
||||||
ClassUtils.convertClassNameToResourcePath(configClass.getName())
|
|
||||||
.substring(0, configClass.getName().lastIndexOf('.') + 1).concat(sourceFile);
|
|
||||||
|
|
||||||
configClass.setSource(resourcePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitOuterClass(String s, String s1, String s2) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visits a class level annotation on a {@link Configuration @Configuration} class.
|
|
||||||
* <p>Upon encountering such an annotation, updates the {@link #configClass} model
|
|
||||||
* object appropriately, and then returns an {@link AnnotationVisitor} implementation
|
|
||||||
* that can populate the annotation appropriately with its attribute data as parsed
|
|
||||||
* by ASM.
|
|
||||||
* @see ConfigurationClassAnnotation
|
|
||||||
* @see Configuration
|
|
||||||
* @see Lazy
|
|
||||||
* @see Import
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
|
||||||
ClassLoader classLoader = metadataReaderFactory.getResourceLoader().getClassLoader();
|
|
||||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadAnnotationType(annoTypeDesc, classLoader);
|
|
||||||
if (annoClass == null) {
|
|
||||||
// annotation was unable to be loaded -> probably Spring IDE unable to load a user-defined annotation
|
|
||||||
return new EmptyVisitor();
|
|
||||||
}
|
|
||||||
if (Import.class.equals(annoClass)) {
|
|
||||||
if (!importStack.contains(configClass)) {
|
|
||||||
importStack.push(configClass);
|
|
||||||
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
|
|
||||||
}
|
|
||||||
problemReporter.error(new CircularImportProblem(configClass, importStack));
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigurationClassAnnotation mutableAnnotation = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
|
|
||||||
configClass.addAnnotation(mutableAnnotation);
|
|
||||||
return new ConfigurationClassAnnotationVisitor(mutableAnnotation, classLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitAttribute(Attribute attribute) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitInnerClass(String s, String s1, String s2, int i) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public FieldVisitor visitField(int i, String s, String s1, String s2, Object o) {
|
|
||||||
return new EmptyVisitor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delegates all {@link Configuration @Configuration} class method parsing to
|
|
||||||
* {@link ConfigurationClassMethodVisitor}.
|
|
||||||
*/
|
|
||||||
public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, String arg3, String[] arg4) {
|
|
||||||
ClassLoader classLoader = metadataReaderFactory.getResourceLoader().getClassLoader();
|
|
||||||
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers, classLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnd() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ASM {@link MethodVisitor} that visits a single method declared in a given
|
|
||||||
* {@link Configuration} class. Determines whether the method is a {@link Bean}
|
|
||||||
* method and if so, adds it to the {@link ConfigurationClass}.
|
|
||||||
*/
|
|
||||||
private class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|
||||||
|
|
||||||
private final ConfigurationClass configClass;
|
|
||||||
private final String methodName;
|
|
||||||
private final int modifiers;
|
|
||||||
private final ConfigurationClassMethod.ReturnType returnType;
|
|
||||||
private final List<Annotation> annotations = new ArrayList<Annotation>();
|
|
||||||
private final ClassLoader classLoader;
|
|
||||||
|
|
||||||
private int lineNumber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link ConfigurationClassMethodVisitor} instance.
|
|
||||||
* @param configClass model object to which this method will be added
|
|
||||||
* @param methodName name of the method declared in the {@link Configuration} class
|
|
||||||
* @param methodDescriptor ASM representation of the method signature
|
|
||||||
* @param modifiers modifiers for this method
|
|
||||||
*/
|
|
||||||
public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName,
|
|
||||||
String methodDescriptor, int modifiers, ClassLoader classLoader) {
|
|
||||||
super(new EmptyVisitor());
|
|
||||||
this.configClass = configClass;
|
|
||||||
this.methodName = methodName;
|
|
||||||
this.classLoader = classLoader;
|
|
||||||
this.modifiers = modifiers;
|
|
||||||
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visits a single annotation on this method. Will be called once for each annotation
|
|
||||||
* present (regardless of its RetentionPolicy).
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
|
||||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadAnnotationType(annoTypeDesc, classLoader);
|
|
||||||
if (annoClass == null) {
|
|
||||||
return super.visitAnnotation(annoTypeDesc, visible);
|
|
||||||
}
|
|
||||||
ConfigurationClassAnnotation annotation = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
|
|
||||||
annotations.add(annotation);
|
|
||||||
return new ConfigurationClassAnnotationVisitor(annotation, classLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the line number of this method within its declaring class. In reality, this
|
|
||||||
* number is always inaccurate - <var>lineNo</var> represents the line number of the
|
|
||||||
* first instruction in this method. Method declaration line numbers are not in any way
|
|
||||||
* tracked in the bytecode. Any tooling or output that reads this value will have to
|
|
||||||
* compensate and estimate where the actual method declaration is.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitLineNumber(int lineNo, Label start) {
|
|
||||||
this.lineNumber = lineNo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses through all {@link #annotations} on this method in order to determine whether
|
|
||||||
* it is a {@link Bean} method and if so adds it to the enclosing {@link #configClass}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitEnd() {
|
|
||||||
for (Annotation anno : annotations) {
|
|
||||||
if (Bean.class.equals(anno.annotationType())) {
|
|
||||||
// this method is annotated with @Bean -> add it to the ConfigurationClass model
|
|
||||||
Annotation[] annoArray = annotations.toArray(new Annotation[annotations.size()]);
|
|
||||||
ConfigurationClassMethod method = new ConfigurationClassMethod(methodName, modifiers, returnType, annoArray);
|
|
||||||
method.setSource(lineNumber);
|
|
||||||
configClass.addMethod(method);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines return type from ASM <var>methodDescriptor</var> and determines whether
|
|
||||||
* that type is an interface.
|
|
||||||
*/
|
|
||||||
private ConfigurationClassMethod.ReturnType initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
|
||||||
final ConfigurationClassMethod.ReturnType returnType = new ConfigurationClassMethod.ReturnType(ConfigurationClassReaderUtils.getReturnTypeFromAsmMethodDescriptor(methodDescriptor));
|
|
||||||
// detect whether the return type is an interface
|
|
||||||
try {
|
|
||||||
metadataReaderFactory.getMetadataReader(returnType.getName()).getClassReader().accept(
|
|
||||||
new ClassAdapter(new EmptyVisitor()) {
|
|
||||||
@Override
|
|
||||||
public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) {
|
|
||||||
returnType.setInterface((arg1 & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
return returnType;
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new BeanDefinitionStoreException("Failed to load bean return type [" + returnType.getName() + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ASM {@link AnnotationVisitor} implementation that reads an {@link Import} annotation
|
|
||||||
* for all its specified classes and then one by one processes each class with a new
|
|
||||||
* {@link ConfigurationClassVisitor}.
|
|
||||||
*/
|
|
||||||
private class ImportAnnotationVisitor implements AnnotationVisitor{
|
|
||||||
|
|
||||||
private final Set<ConfigurationClass> model;
|
|
||||||
|
|
||||||
private final ProblemReporter problemReporter;
|
|
||||||
|
|
||||||
private final List<String> classesToImport = new ArrayList<String>();
|
|
||||||
|
|
||||||
public ImportAnnotationVisitor(
|
|
||||||
Set<ConfigurationClass> model, ProblemReporter problemReporter, ClassLoader classLoader) {
|
|
||||||
|
|
||||||
this.model = model;
|
|
||||||
this.problemReporter = problemReporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visit(String s, Object o) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnum(String s, String s1, String s2) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitAnnotation(String s, String s1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitArray(String attribName) {
|
|
||||||
Assert.isTrue("value".equals(attribName));
|
|
||||||
return new AnnotationVisitor() {
|
|
||||||
public void visit(String na, Object type) {
|
|
||||||
Assert.isInstanceOf(Type.class, type);
|
|
||||||
classesToImport.add(((Type) type).getClassName());
|
|
||||||
}
|
|
||||||
public void visitEnum(String s, String s1, String s2) {
|
|
||||||
}
|
|
||||||
public AnnotationVisitor visitAnnotation(String s, String s1) {
|
|
||||||
return new EmptyVisitor();
|
|
||||||
}
|
|
||||||
public AnnotationVisitor visitArray(String s) {
|
|
||||||
return new EmptyVisitor();
|
|
||||||
}
|
|
||||||
public void visitEnd() {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnd() {
|
|
||||||
for (String classToImport : classesToImport) {
|
|
||||||
processClassToImport(classToImport);
|
|
||||||
}
|
|
||||||
importStack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processClassToImport(String classToImport) {
|
|
||||||
ConfigurationClass configClass = new ConfigurationClass();
|
|
||||||
try {
|
|
||||||
ClassReader reader = metadataReaderFactory.getMetadataReader(classToImport).getClassReader();
|
|
||||||
reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, metadataReaderFactory, importStack), false);
|
|
||||||
if (configClass.getAnnotation(Configuration.class) == null) {
|
|
||||||
problemReporter.error(new NonAnnotatedConfigurationProblem(configClass.getName(), configClass.getLocation()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model.add(configClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new BeanDefinitionStoreException("Failed to load imported configuration class [" + classToImport + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
private static class ImportStack extends Stack<ConfigurationClass> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simplified contains() implementation that tests to see if any {@link ConfigurationClass}
|
|
||||||
* exists within this stack that has the same name as <var>elem</var>. Elem must be of
|
|
||||||
* type ConfigurationClass.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean contains(Object elem) {
|
|
||||||
if (!(elem instanceof ConfigurationClass)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ConfigurationClass configClass = (ConfigurationClass) elem;
|
|
||||||
Comparator<ConfigurationClass> comparator = new Comparator<ConfigurationClass>() {
|
|
||||||
public int compare(ConfigurationClass first, ConfigurationClass second) {
|
|
||||||
return first.getName().equals(second.getName()) ? 0 : 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (Collections.binarySearch(this, configClass, comparator) != -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a stack containing (in order)
|
|
||||||
* <ol>
|
|
||||||
* <li>com.acme.Foo</li>
|
|
||||||
* <li>com.acme.Bar</li>
|
|
||||||
* <li>com.acme.Baz</li>
|
|
||||||
* </ol>
|
|
||||||
* Returns "Foo->Bar->Baz". In the case of an empty stack, returns empty string.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public synchronized String toString() {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
Iterator<ConfigurationClass> iterator = iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
builder.append(iterator.next().getSimpleName());
|
|
||||||
if (iterator.hasNext()) {
|
|
||||||
builder.append("->");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Configuration classes must be annotated with {@link Configuration @Configuration}. */
|
|
||||||
private static class NonAnnotatedConfigurationProblem extends Problem {
|
|
||||||
|
|
||||||
public NonAnnotatedConfigurationProblem(String className, Location location) {
|
|
||||||
super(String.format("%s was imported as a @Configuration class but was not actually annotated " +
|
|
||||||
"with @Configuration. Annotate the class or do not attempt to process it.", className), location);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link Problem} registered upon detection of a circular {@link Import}.
|
|
||||||
* @see Import
|
|
||||||
*/
|
|
||||||
private static class CircularImportProblem extends Problem {
|
|
||||||
|
|
||||||
public CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> importStack) {
|
|
||||||
super(String.format("A circular @Import has been detected: " +
|
|
||||||
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
|
|
||||||
"already present in the current import stack [%s]",
|
|
||||||
importStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
|
|
||||||
attemptedImport.getSimpleName(), importStack),
|
|
||||||
new Location(new ClassPathResource(
|
|
||||||
ClassUtils.convertClassNameToResourcePath(importStack.peek().getName())),
|
|
||||||
importStack.peek().getSource())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 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 java.lang.reflect.Modifier;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
|
||||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
|
||||||
import org.springframework.beans.factory.parsing.Location;
|
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
|
||||||
import static org.springframework.context.annotation.ConfigurationClassReaderUtils.*;
|
|
||||||
import static org.springframework.context.annotation.ScopedProxyMode.*;
|
|
||||||
import static org.springframework.context.annotation.StandardScopes.*;
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Chris Beams
|
|
||||||
*/
|
|
||||||
public class BeanMethodTests {
|
|
||||||
|
|
||||||
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
|
||||||
private String beanName = "foo";
|
|
||||||
private Bean beanAnno = (Bean) createMutableAnnotation(Bean.class, ClassUtils.getDefaultClassLoader());
|
|
||||||
private ConfigurationClassMethod.ReturnType returnType = new ConfigurationClassMethod.ReturnType("FooType");
|
|
||||||
private ConfigurationClass declaringClass = new ConfigurationClass();
|
|
||||||
{ declaringClass.setName("test.Config"); }
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWellFormedMethod() {
|
|
||||||
ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, 0, returnType, beanAnno);
|
|
||||||
|
|
||||||
assertThat(beanMethod.getName(), sameInstance(beanName));
|
|
||||||
assertThat(beanMethod.getModifiers(), equalTo(0));
|
|
||||||
assertThat(beanMethod.getReturnType(), sameInstance(returnType));
|
|
||||||
assertThat(beanMethod.getAnnotation(Bean.class), sameInstance(beanAnno));
|
|
||||||
assertThat(beanMethod.getAnnotation(Override.class), nullValue());
|
|
||||||
assertThat(beanMethod.getRequiredAnnotation(Bean.class), sameInstance(beanAnno));
|
|
||||||
try {
|
|
||||||
beanMethod.getRequiredAnnotation(Override.class);
|
|
||||||
fail("expected IllegalStateException ex");
|
|
||||||
} catch (IllegalStateException ex) { /* expected */ }
|
|
||||||
|
|
||||||
// must call setDeclaringClass() before calling getLocation()
|
|
||||||
try {
|
|
||||||
beanMethod.getLocation();
|
|
||||||
fail("expected IllegalStateException ex");
|
|
||||||
} catch (IllegalStateException ex) { /* expected */ }
|
|
||||||
|
|
||||||
|
|
||||||
beanMethod.setDeclaringClass(declaringClass);
|
|
||||||
assertThat(beanMethod.getDeclaringClass(), sameInstance(declaringClass));
|
|
||||||
|
|
||||||
beanMethod.setSource(12); // indicating a line number
|
|
||||||
assertEquals(beanMethod.getSource(), 12);
|
|
||||||
|
|
||||||
Location location = beanMethod.getLocation();
|
|
||||||
assertEquals(location.getResource(), new ClassPathResource("test/Config"));
|
|
||||||
assertEquals(location.getSource(), 12);
|
|
||||||
|
|
||||||
// should validate without throwing as this is a well-formed method
|
|
||||||
beanMethod.validate(problemReporter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void finalMethodsAreIllegal() {
|
|
||||||
ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, Modifier.FINAL, returnType, beanAnno);
|
|
||||||
beanMethod.setDeclaringClass(declaringClass);
|
|
||||||
try {
|
|
||||||
beanMethod.validate(problemReporter);
|
|
||||||
fail("should have failed due to final bean method");
|
|
||||||
} catch (BeanDefinitionParsingException ex) {
|
|
||||||
assertTrue(ex.getMessage().contains("remove the final modifier"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void privateMethodsAreIllegal() {
|
|
||||||
ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, Modifier.PRIVATE, returnType, beanAnno);
|
|
||||||
beanMethod.setDeclaringClass(declaringClass);
|
|
||||||
try {
|
|
||||||
beanMethod.validate(problemReporter);
|
|
||||||
fail("should have failed due to private bean method");
|
|
||||||
} catch (BeanDefinitionParsingException ex) {
|
|
||||||
assertTrue(ex.getMessage().contains("increase the method's visibility"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void singletonsSansProxyAreLegal() {
|
|
||||||
Scope scope = SingletonNoProxy.class.getAnnotation(Scope.class);
|
|
||||||
ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, 0, returnType, beanAnno, scope);
|
|
||||||
beanMethod.setDeclaringClass(declaringClass);
|
|
||||||
beanMethod.validate(problemReporter); // should validate without problems - it's legal
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sessionInterfaceScopedProxiesAreLegal() {
|
|
||||||
Scope scope = SessionInterfaceProxy.class.getAnnotation(Scope.class);
|
|
||||||
ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, 0, returnType, beanAnno, scope);
|
|
||||||
beanMethod.setDeclaringClass(declaringClass);
|
|
||||||
beanMethod.validate(problemReporter); // should validate without problems - it's legal
|
|
||||||
}
|
|
||||||
|
|
||||||
@Scope(value=SINGLETON, proxyMode=INTERFACES)
|
|
||||||
private class SingletonInterfaceProxy { }
|
|
||||||
|
|
||||||
@Scope(value=SINGLETON, proxyMode=TARGET_CLASS)
|
|
||||||
private class SingletonTargetClassProxy { }
|
|
||||||
|
|
||||||
@Scope(value=SINGLETON, proxyMode=NO)
|
|
||||||
private class SingletonNoProxy { }
|
|
||||||
|
|
||||||
@Scope(value=PROTOTYPE, proxyMode=INTERFACES)
|
|
||||||
private class PrototypeInterfaceProxy { }
|
|
||||||
|
|
||||||
@Scope(value=SESSION, proxyMode=INTERFACES)
|
|
||||||
private class SessionInterfaceProxy { }
|
|
||||||
}
|
|
||||||
|
|
@ -34,12 +34,10 @@ import org.springframework.core.io.ClassPathResource;
|
||||||
import test.beans.Colour;
|
import test.beans.Colour;
|
||||||
import test.beans.TestBean;
|
import test.beans.TestBean;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* System tests covering use of {@link Autowired} and {@link Value} within
|
* System tests covering use of {@link Autowired} and {@link Value} within
|
||||||
* {@link Configuration} classes.
|
* {@link Configuration} classes.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
public class AutowiredConfigurationTests {
|
public class AutowiredConfigurationTests {
|
||||||
|
|
@ -73,6 +71,7 @@ public class AutowiredConfigurationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Autowired} constructors are not supported on {@link Configuration} classes
|
* {@link Autowired} constructors are not supported on {@link Configuration} classes
|
||||||
* due to CGLIB constraints
|
* due to CGLIB constraints
|
||||||
|
|
@ -96,6 +95,7 @@ public class AutowiredConfigurationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValueInjection() {
|
public void testValueInjection() {
|
||||||
System.setProperty("myProp", "foo");
|
System.setProperty("myProp", "foo");
|
||||||
|
|
@ -119,6 +119,7 @@ public class AutowiredConfigurationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomProperties() {
|
public void testCustomProperties() {
|
||||||
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext(
|
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext(
|
||||||
|
|
@ -143,4 +144,5 @@ public class AutowiredConfigurationTests {
|
||||||
return new TestBean(hostname);
|
return new TestBean(hostname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,15 +74,6 @@ public class ConfigurationClassProcessingTests {
|
||||||
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
|
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class ConfigWithBeanWithCustomName {
|
|
||||||
static TestBean testBean = new TestBean();
|
|
||||||
@Bean(name="customName")
|
|
||||||
public TestBean methodName() {
|
|
||||||
return testBean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void aliasesAreRespected() {
|
public void aliasesAreRespected() {
|
||||||
BeanFactory factory = initBeanFactory(ConfigWithBeanWithAliases.class);
|
BeanFactory factory = initBeanFactory(ConfigWithBeanWithAliases.class);
|
||||||
|
|
@ -98,27 +89,11 @@ public class ConfigurationClassProcessingTests {
|
||||||
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
|
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class ConfigWithBeanWithAliases {
|
|
||||||
static TestBean testBean = new TestBean();
|
|
||||||
@Bean(name={"name1", "alias1", "alias2", "alias3"})
|
|
||||||
public TestBean methodName() {
|
|
||||||
return testBean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected=BeanDefinitionParsingException.class)
|
@Test(expected=BeanDefinitionParsingException.class)
|
||||||
public void testFinalBeanMethod() {
|
public void testFinalBeanMethod() {
|
||||||
initBeanFactory(ConfigWithFinalBean.class);
|
initBeanFactory(ConfigWithFinalBean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class ConfigWithFinalBean {
|
|
||||||
public final @Bean TestBean testBean() {
|
|
||||||
return new TestBean();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simplestPossibleConfiguration() {
|
public void simplestPossibleConfiguration() {
|
||||||
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
|
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
|
||||||
|
|
@ -139,6 +114,24 @@ public class ConfigurationClassProcessingTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class ConfigWithBeanWithCustomName {
|
||||||
|
static TestBean testBean = new TestBean();
|
||||||
|
@Bean(name="customName")
|
||||||
|
public TestBean methodName() {
|
||||||
|
return testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class ConfigWithFinalBean {
|
||||||
|
public final @Bean TestBean testBean() {
|
||||||
|
return new TestBean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class SimplestPossibleConfig {
|
static class SimplestPossibleConfig {
|
||||||
public @Bean String stringBean() {
|
public @Bean String stringBean() {
|
||||||
|
|
@ -147,6 +140,16 @@ public class ConfigurationClassProcessingTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class ConfigWithBeanWithAliases {
|
||||||
|
static TestBean testBean = new TestBean();
|
||||||
|
@Bean(name={"name1", "alias1", "alias2", "alias3"})
|
||||||
|
public TestBean methodName() {
|
||||||
|
return testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class ConfigWithPrototypeBean {
|
static class ConfigWithPrototypeBean {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,14 @@ package org.springframework.context.annotation.configuration;
|
||||||
import java.lang.annotation.Inherited;
|
import java.lang.annotation.Inherited;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import test.beans.TestBean;
|
||||||
|
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||||
|
|
||||||
import test.beans.TestBean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that polymorphic Configuration classes need not explicitly redeclare the
|
* Tests that polymorphic Configuration classes need not explicitly redeclare the
|
||||||
* {@link Configuration} annotation. This respects the {@link Inherited} nature
|
* {@link Configuration} annotation. This respects the {@link Inherited} nature
|
||||||
|
|
@ -37,7 +37,7 @@ import test.beans.TestBean;
|
||||||
public class PolymorphicConfigurationTests {
|
public class PolymorphicConfigurationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void subclassNeedNotDeclareConfigurationAnnotation() {
|
public void beanMethodsDetectedOnSuperClass() {
|
||||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
|
||||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
|
@ -56,6 +56,7 @@ public class PolymorphicConfigurationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
static class Config extends SuperConfig {
|
static class Config extends SuperConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -231,19 +231,46 @@ public abstract class AnnotationUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the given annotation's attributes as a Map.
|
* Retrieve the given annotation's attributes as a Map,
|
||||||
|
* preserving all attribute types as-is.
|
||||||
* @param annotation the annotation to retrieve the attributes for
|
* @param annotation the annotation to retrieve the attributes for
|
||||||
* @return the Map of annotation attributes, with attribute names as keys
|
* @return the Map of annotation attributes, with attribute names as keys
|
||||||
* and corresponding attribute values as values
|
* and corresponding attribute values as values
|
||||||
*/
|
*/
|
||||||
public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
|
public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
|
||||||
|
return getAnnotationAttributes(annotation, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the given annotation's attributes as a Map.
|
||||||
|
* @param annotation the annotation to retrieve the attributes for
|
||||||
|
* @param filterClasses 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 filterClasses) {
|
||||||
Map<String, Object> attrs = new HashMap<String, Object>();
|
Map<String, Object> attrs = new HashMap<String, Object>();
|
||||||
Method[] methods = annotation.annotationType().getDeclaredMethods();
|
Method[] methods = annotation.annotationType().getDeclaredMethods();
|
||||||
for (int j = 0; j < methods.length; j++) {
|
for (Method method : methods) {
|
||||||
Method method = methods[j];
|
|
||||||
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
|
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
|
||||||
try {
|
try {
|
||||||
attrs.put(method.getName(), method.invoke(annotation));
|
Object value = method.invoke(annotation);
|
||||||
|
if (filterClasses) {
|
||||||
|
if (value instanceof Class) {
|
||||||
|
value = ((Class) value).getName();
|
||||||
|
}
|
||||||
|
else if (value instanceof Class[]) {
|
||||||
|
Class[] clazzArray = (Class[]) value;
|
||||||
|
String[] newValue = new String[clazzArray.length];
|
||||||
|
for (int i = 0; i < clazzArray.length; i++) {
|
||||||
|
newValue[i] = clazzArray[i].getName();
|
||||||
|
}
|
||||||
|
value = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attrs.put(method.getName(), value);
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
throw new IllegalStateException("Could not obtain annotation attribute values", ex);
|
throw new IllegalStateException("Could not obtain annotation attribute values", ex);
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,20 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||||
*/
|
*/
|
||||||
boolean hasAnnotation(String annotationType);
|
boolean hasAnnotation(String annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the attributes of the annotation of the given type,
|
||||||
|
* if any (i.e. if defined on the underlying class).
|
||||||
|
* @param annotationType the annotation type to look for
|
||||||
|
* @return a Map of attributes, with the attribute name as key (e.g. "value")
|
||||||
|
* and the defined attribute value as Map value. This return value will be
|
||||||
|
* <code>null</code> if no matching annotation is defined.
|
||||||
|
*/
|
||||||
|
Map<String, Object> getAnnotationAttributes(String annotationType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the names of all meta-annotation types defined on the
|
* Return the names of all meta-annotation types defined on the
|
||||||
* given annotation type of the underlying class.
|
* given annotation type of the underlying class.
|
||||||
|
* @param annotationType the meta-annotation type to look for
|
||||||
* @return the meta-annotation type names
|
* @return the meta-annotation type names
|
||||||
*/
|
*/
|
||||||
Set<String> getMetaAnnotationTypes(String annotationType);
|
Set<String> getMetaAnnotationTypes(String annotationType);
|
||||||
|
|
@ -61,26 +72,20 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||||
boolean hasMetaAnnotation(String metaAnnotationType);
|
boolean hasMetaAnnotation(String metaAnnotationType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the attributes of the annotation of the given type,
|
* Retrieve the method metadata for all methods that are annotated
|
||||||
* if any (i.e. if defined on the underlying class).
|
* with at least one annotation type.
|
||||||
* @param annotationType the annotation type to look for
|
* @return a Set of {@link MethodMetadata} for methods that have annotations.
|
||||||
* @return a Map of attributes, with the attribute name as key
|
* The return value will be an empty set if no annotated methods are found.
|
||||||
* (e.g. "value") and the defined attribute value as Map value.
|
|
||||||
* This return value will be <code>null</code> if no matching
|
|
||||||
* annotation is defined.
|
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getAnnotationAttributes(String annotationType);
|
Set<MethodMetadata> getAnnotatedMethods();
|
||||||
|
|
||||||
|
|
||||||
// TODO return null would be more consistent with other methods if no match is found
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the method meta-data for all methods that have the
|
* Retrieve the method metadata for all methods that have the
|
||||||
* given annotation type.
|
* given annotation type.
|
||||||
* @param annotationType the annotation type to look for
|
* @param annotationType the annotation type to look for
|
||||||
* @return a Set of (@link MethodMetadata) for methods that
|
* @return a Set of {@link MethodMetadata} for methods that have a matching
|
||||||
* have a matching annotation. The return value will be an
|
* annotation. The return value will be an empty set if no methods match
|
||||||
* empty set if no methods match the annotation type.
|
* the annotation type.
|
||||||
*/
|
*/
|
||||||
Set<MethodMetadata> getAnnotatedMethods(String annotationType);
|
Set<MethodMetadata> getAnnotatedMethods(String annotationType);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -49,6 +49,11 @@ public interface ClassMetadata {
|
||||||
*/
|
*/
|
||||||
boolean isConcrete();
|
boolean isConcrete();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the underlying class is marked as 'final'.
|
||||||
|
*/
|
||||||
|
boolean isFinal();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the underlying class is independent,
|
* Determine whether the underlying class is independent,
|
||||||
* i.e. whether it is a top-level class or a nested class
|
* i.e. whether it is a top-level class or a nested class
|
||||||
|
|
|
||||||
|
|
@ -20,41 +20,60 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Interface that defines abstract access to the annotations of a specific
|
||||||
|
* class, in a form that does not require that class to be loaded yet.
|
||||||
|
*
|
||||||
* @author Mark Pollack
|
* @author Mark Pollack
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
* @see StandardMethodMetadata
|
||||||
|
* @see AnnotationMetadata#getAnnotatedMethods
|
||||||
*/
|
*/
|
||||||
public interface MethodMetadata {
|
public interface MethodMetadata {
|
||||||
|
|
||||||
int getModifiers();
|
/**
|
||||||
|
* Return the name of the method.
|
||||||
boolean isStatic();
|
*/
|
||||||
|
|
||||||
String getMethodName();
|
String getMethodName();
|
||||||
|
|
||||||
//TODO does the method return type have a generic wildcard or generic type parameters?
|
/**
|
||||||
|
* Return whether the underlying method is declared as 'static'.
|
||||||
|
*/
|
||||||
|
boolean isStatic();
|
||||||
|
|
||||||
// annotation metadata
|
/**
|
||||||
|
* Return whether the underlying method is marked as 'final'.
|
||||||
|
*/
|
||||||
|
boolean isFinal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the underlying method is overridable,
|
||||||
|
* i.e. not marked as static, final or private.
|
||||||
|
*/
|
||||||
|
boolean isOverridable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the names of all annotation types defined on the underlying method.
|
||||||
|
* @return the annotation type names, or an empty Set if none found
|
||||||
|
*/
|
||||||
Set<String> getAnnotationTypes();
|
Set<String> getAnnotationTypes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the underlying method has an annotation of the given
|
||||||
|
* type defined.
|
||||||
|
* @param annotationType the annotation type to look for
|
||||||
|
* @return whether a matching annotation is defined
|
||||||
|
*/
|
||||||
boolean hasAnnotation(String annotationType);
|
boolean hasAnnotation(String annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the attributes of the annotation of the given type,
|
||||||
|
* if any (i.e. if defined on the underlying method).
|
||||||
|
* @param annotationType the annotation type to look for
|
||||||
|
* @return a Map of attributes, with the attribute name as key (e.g. "value")
|
||||||
|
* and the defined attribute value as Map value. This return value will be
|
||||||
|
* <code>null</code> if no matching annotation is defined.
|
||||||
|
*/
|
||||||
Map<String, Object> getAnnotationAttributes(String annotationType);
|
Map<String, Object> getAnnotationAttributes(String annotationType);
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the underlying class has an annotation that
|
|
||||||
* is itself annotated with the meta-annotation of the given type.
|
|
||||||
* @param metaAnnotationType the meta-annotation type to look for
|
|
||||||
* @return whether a matching meta-annotation is defined
|
|
||||||
*/
|
|
||||||
boolean hasMetaAnnotation(String metaAnnotationType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the names of all meta-annotation types defined on the
|
|
||||||
* given annotation type of the underlying class.
|
|
||||||
* @return the meta-annotation type names
|
|
||||||
*/
|
|
||||||
Set<String> getMetaAnnotationTypes(String annotationType);
|
|
||||||
|
|
||||||
Set<String> getAnnotationTypesWithMetaAnnotation(String qualifierClassName);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,10 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||||
*/
|
*/
|
||||||
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
|
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new StandardAnnotationMetadata wrapper for the given Class.
|
||||||
|
* @param introspectedClass the Class to introspect
|
||||||
|
*/
|
||||||
public StandardAnnotationMetadata(Class introspectedClass) {
|
public StandardAnnotationMetadata(Class introspectedClass) {
|
||||||
super(introspectedClass);
|
super(introspectedClass);
|
||||||
}
|
}
|
||||||
|
|
@ -59,6 +63,16 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
|
for (Annotation ann : anns) {
|
||||||
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
|
return AnnotationUtils.getAnnotationAttributes(ann, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (Annotation ann : anns) {
|
for (Annotation ann : anns) {
|
||||||
|
|
@ -87,25 +101,25 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
public Set<MethodMetadata> getAnnotatedMethods() {
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Method[] methods = getIntrospectedClass().getDeclaredMethods();
|
||||||
for (Annotation ann : anns) {
|
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
for (Method method : methods) {
|
||||||
return AnnotationUtils.getAnnotationAttributes(ann);
|
if (method.getAnnotations().length > 0) {
|
||||||
|
annotatedMethods.add(new StandardMethodMetadata(method));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return annotatedMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
||||||
Method[] methods = getIntrospectedClass().getMethods();
|
Method[] methods = getIntrospectedClass().getDeclaredMethods();
|
||||||
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
Annotation[] methodAnnotations = method.getAnnotations();
|
Annotation[] methodAnnotations = method.getAnnotations();
|
||||||
for (Annotation ann : methodAnnotations) {
|
for (Annotation ann : methodAnnotations) {
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
MethodMetadata mm = new StandardMethodMetadata(method);
|
annotatedMethods.add(new StandardMethodMetadata(method));
|
||||||
annotatedMethods.add(mm);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -18,6 +18,8 @@ package org.springframework.core.type;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ClassMetadata} implementation that uses standard reflection
|
* {@link ClassMetadata} implementation that uses standard reflection
|
||||||
* to introspect a given <code>Class</code>.
|
* to introspect a given <code>Class</code>.
|
||||||
|
|
@ -30,57 +32,69 @@ public class StandardClassMetadata implements ClassMetadata {
|
||||||
private final Class introspectedClass;
|
private final Class introspectedClass;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new StandardClassMetadata wrapper for the given Class.
|
||||||
|
* @param introspectedClass the Class to introspect
|
||||||
|
*/
|
||||||
public StandardClassMetadata(Class introspectedClass) {
|
public StandardClassMetadata(Class introspectedClass) {
|
||||||
|
Assert.notNull(introspectedClass, "Class must not be null");
|
||||||
this.introspectedClass = introspectedClass;
|
this.introspectedClass = introspectedClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the underlying Class.
|
||||||
|
*/
|
||||||
public final Class getIntrospectedClass() {
|
public final Class getIntrospectedClass() {
|
||||||
return this.introspectedClass;
|
return this.introspectedClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return getIntrospectedClass().getName();
|
return this.introspectedClass.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInterface() {
|
public boolean isInterface() {
|
||||||
return getIntrospectedClass().isInterface();
|
return this.introspectedClass.isInterface();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAbstract() {
|
public boolean isAbstract() {
|
||||||
return Modifier.isAbstract(getIntrospectedClass().getModifiers());
|
return Modifier.isAbstract(this.introspectedClass.getModifiers());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConcrete() {
|
public boolean isConcrete() {
|
||||||
return !(isInterface() || isAbstract());
|
return !(isInterface() || isAbstract());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFinal() {
|
||||||
|
return Modifier.isFinal(this.introspectedClass.getModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isIndependent() {
|
public boolean isIndependent() {
|
||||||
return (!hasEnclosingClass() ||
|
return (!hasEnclosingClass() ||
|
||||||
(getIntrospectedClass().getDeclaringClass() != null &&
|
(this.introspectedClass.getDeclaringClass() != null &&
|
||||||
Modifier.isStatic(getIntrospectedClass().getModifiers())));
|
Modifier.isStatic(this.introspectedClass.getModifiers())));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasEnclosingClass() {
|
public boolean hasEnclosingClass() {
|
||||||
return (getIntrospectedClass().getEnclosingClass() != null);
|
return (this.introspectedClass.getEnclosingClass() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEnclosingClassName() {
|
public String getEnclosingClassName() {
|
||||||
Class enclosingClass = getIntrospectedClass().getEnclosingClass();
|
Class enclosingClass = this.introspectedClass.getEnclosingClass();
|
||||||
return (enclosingClass != null ? enclosingClass.getName() : null);
|
return (enclosingClass != null ? enclosingClass.getName() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSuperClass() {
|
public boolean hasSuperClass() {
|
||||||
return (getIntrospectedClass().getSuperclass() != null);
|
return (this.introspectedClass.getSuperclass() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSuperClassName() {
|
public String getSuperClassName() {
|
||||||
Class superClass = getIntrospectedClass().getSuperclass();
|
Class superClass = this.introspectedClass.getSuperclass();
|
||||||
return (superClass != null ? superClass.getName() : null);
|
return (superClass != null ? superClass.getName() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getInterfaceNames() {
|
public String[] getInterfaceNames() {
|
||||||
Class[] ifcs = getIntrospectedClass().getInterfaces();
|
Class[] ifcs = this.introspectedClass.getInterfaces();
|
||||||
String[] ifcNames = new String[ifcs.length];
|
String[] ifcNames = new String[ifcs.length];
|
||||||
for (int i = 0; i < ifcs.length; i++) {
|
for (int i = 0; i < ifcs.length; i++) {
|
||||||
ifcNames[i] = ifcs[i].getName();
|
ifcNames[i] = ifcs[i].getName();
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package org.springframework.core.type;
|
package org.springframework.core.type;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.AccessibleObject;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
@ -28,52 +27,62 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* {@link MethodMetadata} implementation that uses standard reflection
|
||||||
|
* to introspect a given <code>Method</code>.
|
||||||
|
*
|
||||||
* @author Mark Pollack
|
* @author Mark Pollack
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StandardMethodMetadata implements MethodMetadata {
|
public class StandardMethodMetadata implements MethodMetadata {
|
||||||
|
|
||||||
private final Method introspectedMethod;
|
private final Method introspectedMethod;
|
||||||
|
|
||||||
public StandardMethodMetadata(Method method) {
|
|
||||||
Assert.notNull(method, "Method must not be null");
|
/**
|
||||||
introspectedMethod = method;
|
* Create a new StandardMethodMetadata wrapper for the given Method.
|
||||||
|
* @param introspectedMethod the Method to introspect
|
||||||
|
*/
|
||||||
|
public StandardMethodMetadata(Method introspectedMethod) {
|
||||||
|
Assert.notNull(introspectedMethod, "Method must not be null");
|
||||||
|
this.introspectedMethod = introspectedMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the underlying Method.
|
||||||
|
*/
|
||||||
public final Method getIntrospectedMethod() {
|
public final Method getIntrospectedMethod() {
|
||||||
return this.introspectedMethod;
|
return this.introspectedMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
public String getMethodName() {
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
return this.introspectedMethod.getName();
|
||||||
for (Annotation ann : anns) {
|
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
|
||||||
return AnnotationUtils.getAnnotationAttributes(ann);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isStatic() {
|
||||||
|
return Modifier.isStatic(this.introspectedMethod.getModifiers());
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
public boolean isFinal() {
|
||||||
|
return Modifier.isFinal(this.introspectedMethod.getModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOverridable() {
|
||||||
|
return (!isStatic() && !isFinal() && !Modifier.isPrivate(this.introspectedMethod.getModifiers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getAnnotationTypes() {
|
public Set<String> getAnnotationTypes() {
|
||||||
Set<String> types = new HashSet<String>();
|
Set<String> types = new HashSet<String>();
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
Annotation[] anns = this.introspectedMethod.getAnnotations();
|
||||||
for (Annotation ann : anns) {
|
for (Annotation ann : anns) {
|
||||||
types.add(ann.annotationType().getName());
|
types.add(ann.annotationType().getName());
|
||||||
}
|
}
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMethodName() {
|
|
||||||
return introspectedMethod.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getModifiers() {
|
|
||||||
return introspectedMethod.getModifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasAnnotation(String annotationType) {
|
public boolean hasAnnotation(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
Annotation[] anns = this.introspectedMethod.getAnnotations();
|
||||||
for (Annotation ann : anns) {
|
for (Annotation ann : anns) {
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -82,44 +91,14 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStatic() {
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
return Modifier.isStatic(getIntrospectedMethod().getModifiers());
|
Annotation[] anns = this.introspectedMethod.getAnnotations();
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
|
||||||
for (Annotation ann : anns) {
|
for (Annotation ann : anns) {
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
Set<String> types = new HashSet<String>();
|
return AnnotationUtils.getAnnotationAttributes(ann, true);
|
||||||
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
|
||||||
for (Annotation meta : metaAnns) {
|
|
||||||
types.add(meta.annotationType().getName());
|
|
||||||
}
|
|
||||||
return types;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
|
||||||
//TODO can refactor into shared (utility) method with StandardAnnotationMetadata
|
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
|
||||||
for (Annotation ann : anns) {
|
|
||||||
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
|
||||||
for (Annotation meta : metaAnns) {
|
|
||||||
if (meta.annotationType().getName().equals(metaAnnotationType)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getAnnotationTypesWithMetaAnnotation(String qualifierClassName) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.type.classreading;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
|
import org.springframework.asm.Type;
|
||||||
|
import org.springframework.asm.commons.EmptyVisitor;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASM visitor which looks for the annotations defined on a class or method.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
final class AnnotationAttributesReadingVisitor implements AnnotationVisitor {
|
||||||
|
|
||||||
|
private final String annotationType;
|
||||||
|
|
||||||
|
private final Map<String, Map<String, Object>> annotationMap;
|
||||||
|
|
||||||
|
private final Map<String, Set<String>> metaAnnotationMap;
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||||
|
|
||||||
|
|
||||||
|
public AnnotationAttributesReadingVisitor(
|
||||||
|
String annotationType, Map<String, Map<String, Object>> attributesMap,
|
||||||
|
Map<String, Set<String>> metaAnnotationMap, ClassLoader classLoader) {
|
||||||
|
|
||||||
|
this.annotationType = annotationType;
|
||||||
|
this.annotationMap = attributesMap;
|
||||||
|
this.metaAnnotationMap = metaAnnotationMap;
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void visit(String name, Object value) {
|
||||||
|
Object valueToUse = value;
|
||||||
|
if (valueToUse instanceof Type) {
|
||||||
|
valueToUse = ((Type) value).getClassName();
|
||||||
|
}
|
||||||
|
this.attributes.put(name, valueToUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitEnum(String name, String desc, String value) {
|
||||||
|
Object valueToUse = value;
|
||||||
|
try {
|
||||||
|
Class<?> enumType = this.classLoader.loadClass(Type.getType(desc).getClassName());
|
||||||
|
Field enumConstant = ReflectionUtils.findField(enumType, value);
|
||||||
|
if (enumConstant != null) {
|
||||||
|
valueToUse = enumConstant.get(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
// Class not found - can't resolve class reference in annotation attribute.
|
||||||
|
}
|
||||||
|
this.attributes.put(name, valueToUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||||
|
return new EmptyVisitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationVisitor visitArray(final String attrName) {
|
||||||
|
return new AnnotationVisitor() {
|
||||||
|
public void visit(String name, Object value) {
|
||||||
|
Object newValue = value;
|
||||||
|
if (newValue instanceof Type) {
|
||||||
|
newValue = ((Type) value).getClassName();
|
||||||
|
}
|
||||||
|
Object existingValue = attributes.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;
|
||||||
|
}
|
||||||
|
attributes.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() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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.attributes.containsKey(attributeName)) {
|
||||||
|
this.attributes.put(attributeName, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Register annotations that the annotation type is annotated with.
|
||||||
|
if (this.metaAnnotationMap != null) {
|
||||||
|
Annotation[] metaAnnotations = annotationClass.getAnnotations();
|
||||||
|
Set<String> metaAnnotationTypeNames = new HashSet<String>();
|
||||||
|
for (Annotation metaAnnotation : metaAnnotations) {
|
||||||
|
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
||||||
|
}
|
||||||
|
this.metaAnnotationMap.put(this.annotationType, metaAnnotationTypeNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex) {
|
||||||
|
// Class not found - can't determine meta-annotations.
|
||||||
|
}
|
||||||
|
this.annotationMap.put(this.annotationType, this.attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -16,12 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.core.type.classreading;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -30,11 +25,8 @@ import java.util.Set;
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
import org.springframework.asm.MethodVisitor;
|
import org.springframework.asm.MethodVisitor;
|
||||||
import org.springframework.asm.Type;
|
import org.springframework.asm.Type;
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
import org.springframework.util.ObjectUtils;
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM class visitor which looks for the class name and implemented types as
|
* ASM class visitor which looks for the class name and implemented types as
|
||||||
|
|
@ -45,9 +37,9 @@ import org.springframework.util.ReflectionUtils;
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
|
final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
|
||||||
|
|
||||||
private final Map<String, Map<String, Object>> attributesMap = new LinkedHashMap<String, Map<String, Object>>();
|
private final Map<String, Map<String, Object>> annotationMap = new LinkedHashMap<String, Map<String, Object>>();
|
||||||
|
|
||||||
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
||||||
|
|
||||||
|
|
@ -63,109 +55,28 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||||
MethodMetadataReadingVisitor md = new MethodMetadataReadingVisitor(classLoader, name, access);
|
MethodMetadataReadingVisitor mm = new MethodMetadataReadingVisitor(name, access, this.classLoader);
|
||||||
methodMetadataSet.add(md);
|
this.methodMetadataSet.add(mm);
|
||||||
return md;
|
return mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
||||||
final String className = Type.getType(desc).getClassName();
|
String className = Type.getType(desc).getClassName();
|
||||||
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
return new AnnotationAttributesReadingVisitor(className, this.annotationMap, this.metaAnnotationMap, this.classLoader);
|
||||||
return new AnnotationVisitor() {
|
|
||||||
public void visit(String name, Object value) {
|
|
||||||
// Explicitly defined annotation attribute value.
|
|
||||||
Object valueToUse = value;
|
|
||||||
if (value instanceof Type) {
|
|
||||||
try {
|
|
||||||
valueToUse = classLoader.loadClass(((Type) value).getClassName());
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
// Class not found - can't resolve class reference in annotation attribute.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attributes.put(name, valueToUse);
|
|
||||||
}
|
|
||||||
public void visitEnum(String name, String desc, String value) {
|
|
||||||
Object valueToUse = value;
|
|
||||||
try {
|
|
||||||
Class<?> enumType = classLoader.loadClass(Type.getType(desc).getClassName());
|
|
||||||
Field enumConstant = ReflectionUtils.findField(enumType, value);
|
|
||||||
if (enumConstant != null) {
|
|
||||||
valueToUse = enumConstant.get(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
// Class not found - can't resolve class reference in annotation attribute.
|
|
||||||
}
|
|
||||||
attributes.put(name, valueToUse);
|
|
||||||
}
|
|
||||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
|
||||||
return new EmptyVisitor();
|
|
||||||
}
|
|
||||||
public AnnotationVisitor visitArray(final String attrName) {
|
|
||||||
return new AnnotationVisitor() {
|
|
||||||
public void visit(String name, Object value) {
|
|
||||||
Object newValue = value;
|
|
||||||
Object existingValue = attributes.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;
|
|
||||||
}
|
|
||||||
attributes.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() {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public void visitEnd() {
|
|
||||||
try {
|
|
||||||
Class<?> annotationClass = classLoader.loadClass(className);
|
|
||||||
// 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 && !attributes.containsKey(attributeName)) {
|
|
||||||
attributes.put(attributeName, defaultValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Register annotations that the annotation type is annotated with.
|
|
||||||
Annotation[] metaAnnotations = annotationClass.getAnnotations();
|
|
||||||
Set<String> metaAnnotationTypeNames = new HashSet<String>();
|
|
||||||
for (Annotation metaAnnotation : metaAnnotations) {
|
|
||||||
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
|
||||||
}
|
|
||||||
metaAnnotationMap.put(className, metaAnnotationTypeNames);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
// Class not found - can't determine meta-annotations.
|
|
||||||
}
|
|
||||||
attributesMap.put(className, attributes);
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Set<String> getAnnotationTypes() {
|
public Set<String> getAnnotationTypes() {
|
||||||
return this.attributesMap.keySet();
|
return this.annotationMap.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasAnnotation(String annotationType) {
|
public boolean hasAnnotation(String annotationType) {
|
||||||
return this.attributesMap.containsKey(annotationType);
|
return this.annotationMap.containsKey(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
|
return this.annotationMap.get(annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||||
|
|
@ -182,17 +93,20 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<MethodMetadata> getAnnotatedMethods() {
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||||
return this.attributesMap.get(annotationType);
|
for (MethodMetadata method : this.methodMetadataSet) {
|
||||||
|
if (!method.getAnnotationTypes().isEmpty()) {
|
||||||
|
annotatedMethods.add(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return annotatedMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
||||||
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||||
for (MethodMetadata method : methodMetadataSet) {
|
for (MethodMetadata method : this.methodMetadataSet) {
|
||||||
if (method.hasAnnotation(annotationType))
|
if (method.hasAnnotation(annotationType)) {
|
||||||
{
|
|
||||||
annotatedMethods.add(method);
|
annotatedMethods.add(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -33,7 +33,7 @@ import org.springframework.core.io.ResourceLoader;
|
||||||
*/
|
*/
|
||||||
public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {
|
public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {
|
||||||
|
|
||||||
private final Map<Resource, SimpleMetadataReader> classReaderCache = new HashMap<Resource, SimpleMetadataReader>();
|
private final Map<Resource, MetadataReader> classReaderCache = new HashMap<Resource, MetadataReader>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -62,9 +62,9 @@ public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SimpleMetadataReader getMetadataReader(Resource resource) throws IOException {
|
public MetadataReader getMetadataReader(Resource resource) throws IOException {
|
||||||
synchronized (this.classReaderCache) {
|
synchronized (this.classReaderCache) {
|
||||||
SimpleMetadataReader metadataReader = this.classReaderCache.get(resource);
|
MetadataReader metadataReader = this.classReaderCache.get(resource);
|
||||||
if (metadataReader == null) {
|
if (metadataReader == null) {
|
||||||
metadataReader = super.getMetadataReader(resource);
|
metadataReader = super.getMetadataReader(resource);
|
||||||
this.classReaderCache.put(resource, metadataReader);
|
this.classReaderCache.put(resource, metadataReader);
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ class ClassMetadataReadingVisitor implements ClassVisitor, ClassMetadata {
|
||||||
|
|
||||||
private boolean isAbstract;
|
private boolean isAbstract;
|
||||||
|
|
||||||
|
private boolean isFinal;
|
||||||
|
|
||||||
private String enclosingClassName;
|
private String enclosingClassName;
|
||||||
|
|
||||||
private boolean independentInnerClass;
|
private boolean independentInnerClass;
|
||||||
|
|
@ -58,6 +60,7 @@ class ClassMetadataReadingVisitor implements ClassVisitor, ClassMetadata {
|
||||||
this.className = ClassUtils.convertResourcePathToClassName(name);
|
this.className = ClassUtils.convertResourcePathToClassName(name);
|
||||||
this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
|
this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
|
||||||
this.isAbstract = ((access & Opcodes.ACC_ABSTRACT) != 0);
|
this.isAbstract = ((access & Opcodes.ACC_ABSTRACT) != 0);
|
||||||
|
this.isFinal = ((access & Opcodes.ACC_FINAL) != 0);
|
||||||
if (supername != null) {
|
if (supername != null) {
|
||||||
this.superClassName = ClassUtils.convertResourcePathToClassName(supername);
|
this.superClassName = ClassUtils.convertResourcePathToClassName(supername);
|
||||||
}
|
}
|
||||||
|
|
@ -122,6 +125,10 @@ class ClassMetadataReadingVisitor implements ClassVisitor, ClassMetadata {
|
||||||
return !(this.isInterface || this.isAbstract);
|
return !(this.isInterface || this.isAbstract);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFinal() {
|
||||||
|
return this.isFinal;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isIndependent() {
|
public boolean isIndependent() {
|
||||||
return (this.enclosingClassName == null || this.independentInnerClass);
|
return (this.enclosingClassName == null || this.independentInnerClass);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.core.type.classreading;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.core.type.ClassMetadata;
|
import org.springframework.core.type.ClassMetadata;
|
||||||
|
|
||||||
|
|
@ -28,13 +29,19 @@ import org.springframework.core.type.ClassMetadata;
|
||||||
*/
|
*/
|
||||||
public interface MetadataReader {
|
public interface MetadataReader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the resource reference for the class file.
|
||||||
|
*/
|
||||||
|
Resource getResource();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read basic class metadata for the underlying class.
|
* Read basic class metadata for the underlying class.
|
||||||
*/
|
*/
|
||||||
ClassMetadata getClassMetadata();
|
ClassMetadata getClassMetadata();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read full annotation metadata for the underlying class.
|
* Read full annotation metadata for the underlying class,
|
||||||
|
* including metadata for annotated methods.
|
||||||
*/
|
*/
|
||||||
AnnotationMetadata getAnnotationMetadata();
|
AnnotationMetadata getAnnotationMetadata();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.core.type.classreading;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -32,139 +26,68 @@ import org.springframework.asm.Opcodes;
|
||||||
import org.springframework.asm.Type;
|
import org.springframework.asm.Type;
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
import org.springframework.asm.commons.EmptyVisitor;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* ASM method visitor which looks for the annotations defined on the method,
|
||||||
|
* exposing them through the {@link org.springframework.core.type.MethodMetadata}
|
||||||
|
* interface.
|
||||||
|
*
|
||||||
* @author Mark Pollack
|
* @author Mark Pollack
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
class MethodMetadataReadingVisitor extends MethodAdapter implements MethodMetadata {
|
final class MethodMetadataReadingVisitor extends MethodAdapter implements MethodMetadata {
|
||||||
|
|
||||||
private ClassLoader classLoader;
|
private final String name;
|
||||||
|
|
||||||
private String name;
|
private final int access;
|
||||||
|
|
||||||
private int access;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
private boolean isStatic;
|
private final Map<String, Map<String, Object>> annotationMap = new LinkedHashMap<String, Map<String, Object>>();
|
||||||
|
|
||||||
private final Map<String, Map<String, Object>> attributesMap = new LinkedHashMap<String, Map<String, Object>>();
|
|
||||||
|
|
||||||
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
|
||||||
|
|
||||||
|
|
||||||
public MethodMetadataReadingVisitor(ClassLoader classLoader, String name, int access) {
|
public MethodMetadataReadingVisitor(String name, int access, ClassLoader classLoader) {
|
||||||
super(new EmptyVisitor());
|
super(new EmptyVisitor());
|
||||||
this.classLoader = classLoader;
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.access = access;
|
this.access = access;
|
||||||
this.isStatic = ((access & Opcodes.ACC_STATIC) != 0);
|
this.classLoader = classLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
|
||||||
return this.attributesMap.get(annotationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getAnnotationTypes() {
|
|
||||||
return this.attributesMap.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMethodName() {
|
public String getMethodName() {
|
||||||
return name;
|
return this.name;
|
||||||
}
|
|
||||||
|
|
||||||
public int getModifiers() {
|
|
||||||
return access;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasAnnotation(String annotationType) {
|
|
||||||
return this.attributesMap.containsKey(annotationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
|
||||||
return this.metaAnnotationMap.get(annotationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
|
||||||
Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
|
|
||||||
for (Set<String> metaTypes : allMetaTypes) {
|
|
||||||
if (metaTypes.contains(metaAnnotationType)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStatic() {
|
public boolean isStatic() {
|
||||||
return this.isStatic;
|
return ((this.access & Opcodes.ACC_STATIC) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getAnnotationTypesWithMetaAnnotation(String metaAnnotationType) {
|
public boolean isFinal() {
|
||||||
Set<String> annotationTypes = new LinkedHashSet<String>();
|
return ((this.access & Opcodes.ACC_FINAL) != 0);
|
||||||
for (Map.Entry<String, Set<String>> entry : metaAnnotationMap.entrySet()) {
|
|
||||||
String attributeType = entry.getKey();
|
|
||||||
Set<String> metaAttributes = entry.getValue();
|
|
||||||
if (metaAttributes.contains(metaAnnotationType)) {
|
|
||||||
annotationTypes.add(attributeType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOverridable() {
|
||||||
|
return (!isStatic() && !isFinal() && ((this.access & Opcodes.ACC_PRIVATE) == 0));
|
||||||
}
|
}
|
||||||
return annotationTypes;
|
|
||||||
|
public Set<String> getAnnotationTypes() {
|
||||||
|
return this.annotationMap.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAnnotation(String annotationType) {
|
||||||
|
return this.annotationMap.containsKey(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
|
return this.annotationMap.get(annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
||||||
final String className = Type.getType(desc).getClassName();
|
String className = Type.getType(desc).getClassName();
|
||||||
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
return new AnnotationAttributesReadingVisitor(className, this.annotationMap, null, this.classLoader);
|
||||||
return new EmptyVisitor() {
|
|
||||||
@Override
|
|
||||||
public void visit(String name, Object value) {
|
|
||||||
// Explicitly defined annotation attribute value.
|
|
||||||
attributes.put(name, value);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void visitEnum(String name, String desc, String value) {
|
|
||||||
Object valueToUse = value;
|
|
||||||
try {
|
|
||||||
Class<?> enumType = classLoader.loadClass(Type.getType(desc).getClassName());
|
|
||||||
Field enumConstant = ReflectionUtils.findField(enumType, value);
|
|
||||||
if (enumConstant != null) {
|
|
||||||
valueToUse = enumConstant.get(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
// Class not found - can't resolve class reference in annotation attribute.
|
|
||||||
}
|
|
||||||
attributes.put(name, valueToUse);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void visitEnd() {
|
|
||||||
try {
|
|
||||||
Class<?> annotationClass = classLoader.loadClass(className);
|
|
||||||
// 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 && !attributes.containsKey(attributeName)) {
|
|
||||||
attributes.put(attributeName, defaultValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Register annotations that the annotation type is annotated with.
|
|
||||||
Annotation[] metaAnnotations = annotationClass.getAnnotations();
|
|
||||||
Set<String> metaAnnotationTypeNames = new HashSet<String>();
|
|
||||||
for (Annotation metaAnnotation : metaAnnotations) {
|
|
||||||
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
|
||||||
}
|
|
||||||
metaAnnotationMap.put(className, metaAnnotationTypeNames);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
// Class not found
|
|
||||||
}
|
|
||||||
attributesMap.put(className, attributes);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package org.springframework.core.type.classreading;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
import org.springframework.asm.ClassReader;
|
import org.springframework.asm.ClassReader;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.core.type.ClassMetadata;
|
import org.springframework.core.type.ClassMetadata;
|
||||||
|
|
||||||
|
|
@ -30,7 +34,9 @@ import org.springframework.core.type.ClassMetadata;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
public class SimpleMetadataReader implements MetadataReader {
|
final class SimpleMetadataReader implements MetadataReader {
|
||||||
|
|
||||||
|
private final Resource resource;
|
||||||
|
|
||||||
private final ClassReader classReader;
|
private final ClassReader classReader;
|
||||||
|
|
||||||
|
|
@ -41,27 +47,23 @@ public class SimpleMetadataReader implements MetadataReader {
|
||||||
private AnnotationMetadata annotationMetadata;
|
private AnnotationMetadata annotationMetadata;
|
||||||
|
|
||||||
|
|
||||||
SimpleMetadataReader(ClassReader classReader, ClassLoader classLoader) {
|
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
|
||||||
this.classReader = classReader;
|
this.resource = resource;
|
||||||
|
InputStream is = this.resource.getInputStream();
|
||||||
|
try {
|
||||||
|
this.classReader = new ClassReader(is);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
public Resource getResource() {
|
||||||
* Return the underlying ASM ClassReader.
|
return this.resource;
|
||||||
*/
|
|
||||||
public final ClassReader getClassReader() {
|
|
||||||
return this.classReader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the underlying ClassLoader.
|
|
||||||
*/
|
|
||||||
public final ClassLoader getClassLoader() {
|
|
||||||
return this.classLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ClassMetadata getClassMetadata() {
|
public ClassMetadata getClassMetadata() {
|
||||||
if (this.classMetadata == null) {
|
if (this.classMetadata == null) {
|
||||||
ClassMetadataReadingVisitor visitor = new ClassMetadataReadingVisitor();
|
ClassMetadataReadingVisitor visitor = new ClassMetadataReadingVisitor();
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,7 @@
|
||||||
package org.springframework.core.type.classreading;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.springframework.asm.ClassReader;
|
|
||||||
import org.springframework.core.io.DefaultResourceLoader;
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
|
|
@ -72,20 +70,14 @@ public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SimpleMetadataReader getMetadataReader(String className) throws IOException {
|
public MetadataReader getMetadataReader(String className) throws IOException {
|
||||||
String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
|
String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
|
||||||
ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
|
ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
|
||||||
return getMetadataReader(this.resourceLoader.getResource(resourcePath));
|
return getMetadataReader(this.resourceLoader.getResource(resourcePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleMetadataReader getMetadataReader(Resource resource) throws IOException {
|
public MetadataReader getMetadataReader(Resource resource) throws IOException {
|
||||||
InputStream is = resource.getInputStream();
|
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
|
||||||
try {
|
|
||||||
return new SimpleMetadataReader(new ClassReader(is), this.resourceLoader.getClassLoader());
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue