@Configuration parsing fully relies on Spring's MetadataReader abstraction now

This commit is contained in:
Juergen Hoeller 2009-04-24 11:16:46 +00:00
parent 37e1333a41
commit b5d21108da
30 changed files with 819 additions and 1944 deletions

View File

@ -18,7 +18,6 @@ package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
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.stereotype.Component;
/**
* 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
@ -59,7 +57,6 @@ import org.springframework.stereotype.Component;
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Component
public @interface Configuration {

View File

@ -16,20 +16,19 @@
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.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeanMetadataElement;
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.util.Assert;
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.ClassUtils;
/**
@ -45,75 +44,42 @@ import org.springframework.util.ClassUtils;
* @see ConfigurationClassMethod
* @see ConfigurationClassParser
*/
final class ConfigurationClass implements BeanMetadataElement {
final class ConfigurationClass {
private String name;
private final AnnotationMetadata metadata;
private ConfigurationClass declaringClass;
private Object source;
private final Resource resource;
private String beanName;
private int modifiers;
private final Set<Annotation> annotations = new HashSet<Annotation>();
private final Set<ConfigurationClassMethod> methods = new LinkedHashSet<ConfigurationClassMethod>();
private final Map<String, Integer> overloadedMethodMap = new LinkedHashMap<String, Integer>();
/**
* Sets the fully-qualified name of this class.
*/
public void setName(String className) {
this.name = className;
public ConfigurationClass(MetadataReader metadataReader, String beanName) {
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.beanName = beanName;
}
/**
* Returns the fully-qualified name of this class.
*/
public String getName() {
return name;
public ConfigurationClass(Class clazz, String beanName) {
this.metadata = new StandardAnnotationMetadata(clazz);
this.resource = new DescriptiveResource(clazz.toString());
this.beanName = beanName;
}
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() {
return name == null ? null : ClassUtils.getShortName(name);
}
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());
return ClassUtils.getShortName(getMetadata().getClassName());
}
public void setBeanName(String beanName) {
@ -121,71 +87,29 @@ final class ConfigurationClass implements BeanMetadataElement {
}
public String getBeanName() {
return 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;
return this.beanName;
}
public ConfigurationClass addMethod(ConfigurationClassMethod method) {
method.setDeclaringClass(this);
methods.add(method);
Integer count = overloadedMethodMap.get(method.getName());
this.methods.add(method);
String name = method.getMetadata().getMethodName();
Integer count = this.overloadedMethodMap.get(name);
if (count != null) {
overloadedMethodMap.put(method.getName(), count + 1);
this.overloadedMethodMap.put(name, count + 1);
}
else {
overloadedMethodMap.put(method.getName(), 1);
this.overloadedMethodMap.put(name, 1);
}
return this;
}
public Set<ConfigurationClassMethod> getBeanMethods() {
return methods;
public Set<ConfigurationClassMethod> getConfigurationMethods() {
return this.methods;
}
public void validate(ProblemReporter problemReporter) {
// 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();
int count = entry.getValue();
if (count > 1) {
@ -194,11 +118,11 @@ final class ConfigurationClass implements BeanMetadataElement {
}
// A configuration class may not be final (CGLIB limitation)
if (getAnnotation(Configuration.class) != null) {
if (Modifier.isFinal(modifiers)) {
if (getMetadata().hasAnnotation(Configuration.class.getName())) {
if (getMetadata().isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
for (ConfigurationClassMethod method : methods) {
for (ConfigurationClassMethod method : this.methods) {
method.validate(problemReporter);
}
}
@ -210,7 +134,7 @@ final class ConfigurationClass implements BeanMetadataElement {
public FinalConfigurationProblem() {
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) {
super(String.format("@Configuration class '%s' has %s overloaded factory methods of name '%s'. " +
"Only one factory method of the same name allowed.",
getSimpleName(), count, methodName), ConfigurationClass.this.getLocation());
getSimpleName(), count, methodName), new Location(getResource()));
}
}
}

View File

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

View File

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

View File

@ -20,6 +20,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.RootBeanDefinition;
import org.springframework.core.io.Resource;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@ -78,7 +80,7 @@ class ConfigurationClassBeanDefinitionReader {
*/
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
doLoadBeanDefinitionForConfigurationClass(configClass);
for (ConfigurationClassMethod method : configClass.getBeanMethods()) {
for (ConfigurationClassMethod method : configClass.getConfigurationMethods()) {
loadBeanDefinitionsForModelMethod(method);
}
}
@ -89,7 +91,7 @@ class ConfigurationClassBeanDefinitionReader {
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
if (configClass.getBeanName() == null) {
GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
configBeanDef.setBeanClassName(configClass.getName());
configBeanDef.setBeanClassName(configClass.getMetadata().getClassName());
String configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(configBeanDef, registry);
configClass.setBeanName(configBeanName);
if (logger.isDebugEnabled()) {
@ -103,24 +105,26 @@ class ConfigurationClassBeanDefinitionReader {
* {@link #registry} based on its contents.
*/
private void loadBeanDefinitionsForModelMethod(ConfigurationClassMethod method) {
MethodMetadata metadata = method.getMetadata();
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
ConfigurationClass configClass = method.getDeclaringClass();
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setUniqueFactoryMethodName(method.getName());
beanDef.setUniqueFactoryMethodName(metadata.getMethodName());
beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
// consider name and any aliases
Bean bean = method.getRequiredAnnotation(Bean.class);
List<String> names = new ArrayList<String>(Arrays.asList(bean.name()));
String beanName = (names.size() > 0) ? names.remove(0) : method.getName();
for (String alias : bean.name()) {
Map<String, Object> beanAttributes = metadata.getAnnotationAttributes(Bean.class.getName());
List<String> names = new ArrayList<String>(Arrays.asList((String[]) beanAttributes.get("name")));
String beanName = (names.size() > 0 ? names.remove(0) : method.getMetadata().getMethodName());
for (String alias : names) {
registry.registerAlias(beanName, alias);
}
// has this already been overriden (i.e.: via XML)?
if (registry.containsBeanDefinition(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)) {
// no -> then it's an external override, probably XML
// 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);
}
// is this bean to be instantiated lazily?
Lazy lazy = method.getAnnotation(Lazy.class);
if (lazy != null) {
beanDef.setLazyInit(lazy.value());
if (metadata.hasAnnotation(Lazy.class.getName())) {
beanDef.setLazyInit((Boolean) metadata.getAnnotationAttributes(Lazy.class.getName()).get("value"));
}
else {
Lazy defaultLazy = configClass.getAnnotation(Lazy.class);
if (defaultLazy != null) {
beanDef.setLazyInit(defaultLazy.value());
else if (configClass.getMetadata().hasAnnotation(Lazy.class.getName())){
beanDef.setLazyInit((Boolean) configClass.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("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);
if (dependsOn != null && dependsOn.value().length > 0) {
beanDef.setDependsOn(dependsOn.value());
}
Autowire autowire = bean.autowire();
Autowire autowire = (Autowire) beanAttributes.get("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
String initMethodName = bean.initMethod();
String initMethodName = (String) beanAttributes.get("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
String destroyMethodName = bean.destroyMethod();
String destroyMethodName = (String) beanAttributes.get("destroyMethod");
if (StringUtils.hasText(destroyMethodName)) {
beanDef.setDestroyMethodName(destroyMethodName);
}
// consider scoping
Scope scope = method.getAnnotation(Scope.class);
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
if (scope != null) {
beanDef.setScope(scope.value());
proxyMode = scope.proxyMode();
if (metadata.hasAnnotation(Scope.class.getName())) {
Map<String, Object> scopeAttributes = metadata.getAnnotationAttributes(Scope.class.getName());
beanDef.setScope((String) scopeAttributes.get("value"));
proxyMode = (ScopedProxyMode) scopeAttributes.get("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
@ -188,7 +190,7 @@ class ConfigurationClassBeanDefinitionReader {
}
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);
@ -196,11 +198,11 @@ class ConfigurationClassBeanDefinitionReader {
/**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition created
* by JavaConfig as opposed to any other configuration source. Used in bean overriding cases
* where it's necessary to determine whether the bean definition was created externally.
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
* created from a configuration class as opposed to any other configuration source.
* 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 {
public ConfigurationClassBeanDefinition() {

View File

@ -60,17 +60,14 @@ class ConfigurationClassEnhancer {
*/
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
callbackInstances.add(new BeanMethodInterceptor(beanFactory));
callbackInstances.add(NoOp.INSTANCE);
for (Callback callback : callbackInstances) {
callbackTypes.add(callback.getClass());
this.callbackInstances.add(new BeanMethodInterceptor(beanFactory));
this.callbackInstances.add(NoOp.INSTANCE);
for (Callback callback : this.callbackInstances) {
this.callbackTypes.add(callback.getClass());
}
// 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.
callbackFilter = new CallbackFilter() {
this.callbackFilter = new CallbackFilter() {
public int accept(Method candidateMethod) {
return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null) ? 0 : 1;
}
@ -84,12 +81,12 @@ class ConfigurationClassEnhancer {
* @return fully-qualified name of the enhanced subclass
*/
public Class enhance(Class configClass) {
if (logger.isInfoEnabled()) {
logger.info("Enhancing " + configClass.getName());
if (logger.isDebugEnabled()) {
logger.debug("Enhancing " + configClass.getName());
}
Class<?> enhancedClass = createClass(newEnhancer(configClass));
if (logger.isInfoEnabled()) {
logger.info(String.format("Successfully enhanced %s; enhanced class name is: %s",
if (logger.isDebugEnabled()) {
logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
configClass.getName(), enhancedClass.getName()));
}
return enhancedClass;
@ -100,17 +97,14 @@ class ConfigurationClassEnhancer {
*/
private Enhancer newEnhancer(Class<?> superclass) {
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
// any performance problem.
enhancer.setUseCache(false);
enhancer.setSuperclass(superclass);
enhancer.setUseFactory(false);
enhancer.setCallbackFilter(callbackFilter);
enhancer.setCallbackTypes(callbackTypes.toArray(new Class[callbackTypes.size()]));
enhancer.setCallbackFilter(this.callbackFilter);
enhancer.setCallbackTypes(this.callbackTypes.toArray(new Class[this.callbackTypes.size()]));
return enhancer;
}
@ -120,7 +114,7 @@ class ConfigurationClassEnhancer {
*/
private Class<?> createClass(Enhancer enhancer) {
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;
}
@ -142,7 +136,6 @@ class ConfigurationClassEnhancer {
this.beanFactory = beanFactory;
}
/**
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
* existence of this bean object.
@ -161,7 +154,7 @@ class ConfigurationClassEnhancer {
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
String scopedBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
if (this.beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
@ -170,7 +163,7 @@ class ConfigurationClassEnhancer {
// container for already cached instances
if (factoryContainsBean(beanName)) {
// 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()) {
logger.debug(String.format("Returning cached object [%s] for @Bean method %s.%s",
cachedBean, method.getDeclaringClass().getSimpleName(), beanName));
@ -185,18 +178,18 @@ class ConfigurationClassEnhancer {
/**
* 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.:
* we're in the middle of servicing the initial request for this bean. From JavaConfig's
* perspective, this means that the bean does not actually yet exist, and that it is now
* our job to create it for the first time by executing the logic in the corresponding
* Bean method.
* we're in the middle of servicing the initial request for this bean. From an enhanced
* factory method's perspective, this means that the bean does not actually yet exist,
* and that it is now our job to create it for the first time by executing the logic
* in the corresponding factory method.
* <p>Said another way, this check repurposes
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
* the container is calling this method or the user is calling this method.
* @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) {
return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName);
return (this.beanFactory.containsBean(beanName) && !this.beanFactory.isCurrentlyInCreation(beanName));
}
}

View File

@ -16,17 +16,10 @@
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.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.MethodMetadata;
/**
* 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 ConfigurationClassBeanDefinitionReader
*/
final class ConfigurationClassMethod implements BeanMetadataElement {
final class ConfigurationClassMethod {
private final String name;
private final MethodMetadata metadata;
private final int modifiers;
private final ReturnType returnType;
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
private transient ConfigurationClass declaringClass;
private transient Object source;
private final ConfigurationClass declaringClass;
public ConfigurationClassMethod(String name, int modifiers, ReturnType returnType, Annotation... annotations) {
Assert.hasText(name);
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) {
public ConfigurationClassMethod(MethodMetadata metadata, ConfigurationClass declaringClass) {
this.metadata = metadata;
this.declaringClass = declaringClass;
}
public MethodMetadata getMetadata() {
return this.metadata;
}
public ConfigurationClass getDeclaringClass() {
return declaringClass;
return this.declaringClass;
}
public void setSource(Object source) {
this.source = source;
}
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 Location getResourceLocation() {
return new Location(this.declaringClass.getResource(), this.metadata.getMethodName());
}
public void validate(ProblemReporter problemReporter) {
if (Modifier.isPrivate(getModifiers())) {
problemReporter.error(new PrivateMethodError());
}
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());
if (this.declaringClass.getMetadata().hasAnnotation(Configuration.class.getName()) && !getMetadata().isOverridable()) {
problemReporter.error(new NonOverridableMethodError());
}
}
/**
* {@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() {
super(String.format("Method '%s' must not be private; increase the method's visibility to continue",
getName()), ConfigurationClassMethod.this.getLocation());
}
}
/**
* {@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());
public NonOverridableMethodError() {
super(String.format("Method '%s' must not be private, final or static; change the method's modifiers to continue",
getMetadata().getMethodName()), getResourceLocation());
}
}

View File

@ -17,13 +17,23 @@
package org.springframework.context.annotation;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Stack;
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.core.type.classreading.SimpleMetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.core.io.Resource;
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.
@ -41,19 +51,20 @@ import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
*/
class ConfigurationClassParser {
private final SimpleMetadataReaderFactory metadataReaderFactory;
private final MetadataReaderFactory metadataReaderFactory;
private final ProblemReporter problemReporter;
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
* configuration model.
* @param model model to be populated by each successive call to {@link #parse}
* Create a new {@link ConfigurationClassParser} instance that will be used
* to populate a configuration model.
*/
public ConfigurationClassParser(SimpleMetadataReaderFactory metadataReaderFactory, ProblemReporter problemReporter) {
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory, ProblemReporter problemReporter) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.model = new LinkedHashSet<ConfigurationClass>();
@ -67,17 +78,80 @@ class ConfigurationClassParser {
* (assumes that this configuration class was configured via XML)
*/
public void parse(String className, String beanName) throws IOException {
SimpleMetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
ConfigurationClass configClass = new ConfigurationClass();
configClass.setBeanName(beanName);
reader.getClassReader().accept(new ConfigurationClassVisitor(configClass, model, problemReporter, metadataReaderFactory), false);
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
/**
* 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);
}
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}.
* @param problemReporter {@link ProblemReporter} against which any validation errors
* will be registered
* @see ConfigurationClass#validate
*/
public void validate() {
@ -90,4 +164,78 @@ class ConfigurationClassParser {
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())
);
}
}
}

View File

@ -35,23 +35,23 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.Conventions;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
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.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
/**
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
* {@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
* 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
* respective bean definitions registered before any other BeanFactoryPostProcessor
* executes.
@ -62,32 +62,33 @@ import org.springframework.util.ClassUtils;
*/
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");
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 */
private static final boolean cglibAvailable = ClassUtils.isPresent(
"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 ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
/**
* Override the default {@link ProblemReporter}.
* @param problemReporter custom problem reporter
* Set the {@link ProblemReporter} to use.
* <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) {
this.problemReporter = problemReporter;
@ -116,34 +117,38 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
enhanceConfigurationClasses(beanFactory);
}
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
protected final void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
Set<BeanDefinitionHolder> configBeanDefs = new LinkedHashSet<BeanDefinitionHolder>();
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
for (String beanName : registry.getBeanDefinitionNames()) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (checkConfigurationClassBeanDefinition(beanDef)) {
configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
if (checkConfigurationClassCandidate(beanDef)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configBeanDefs.isEmpty()) {
if (configCandidates.isEmpty()) {
return;
}
// Populate a new configuration model by parsing each @Configuration classes
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter);
for (BeanDefinitionHolder holder : configBeanDefs) {
String beanClassName = holder.getBeanDefinition().getBeanClassName();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
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) {
throw new BeanDefinitionStoreException("Failed to load bean class [" + beanClassName + "]", ex);
throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
}
}
parser.validate();
@ -152,6 +157,42 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
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;
* any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
@ -162,7 +203,7 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
Set<BeanDefinitionHolder> configBeanDefs = new LinkedHashSet<BeanDefinitionHolder>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
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));
}
}
@ -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;
}
}

View File

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

View File

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

View File

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

View File

@ -34,12 +34,10 @@ import org.springframework.core.io.ClassPathResource;
import test.beans.Colour;
import test.beans.TestBean;
/**
* System tests covering use of {@link Autowired} and {@link Value} within
* {@link Configuration} classes.
*
*
* @author Chris Beams
*/
public class AutowiredConfigurationTests {
@ -73,6 +71,7 @@ public class AutowiredConfigurationTests {
}
}
/**
* {@link Autowired} constructors are not supported on {@link Configuration} classes
* due to CGLIB constraints
@ -96,6 +95,7 @@ public class AutowiredConfigurationTests {
}
}
@Test
public void testValueInjection() {
System.setProperty("myProp", "foo");
@ -119,6 +119,7 @@ public class AutowiredConfigurationTests {
}
}
@Test
public void testCustomProperties() {
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext(
@ -143,4 +144,5 @@ public class AutowiredConfigurationTests {
return new TestBean(hostname);
}
}
}

View File

@ -74,15 +74,6 @@ public class ConfigurationClassProcessingTests {
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
}
@Configuration
static class ConfigWithBeanWithCustomName {
static TestBean testBean = new TestBean();
@Bean(name="customName")
public TestBean methodName() {
return testBean;
}
}
@Test
public void aliasesAreRespected() {
BeanFactory factory = initBeanFactory(ConfigWithBeanWithAliases.class);
@ -98,27 +89,11 @@ public class ConfigurationClassProcessingTests {
} 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)
public void testFinalBeanMethod() {
initBeanFactory(ConfigWithFinalBean.class);
}
@Configuration
static class ConfigWithFinalBean {
public final @Bean TestBean testBean() {
return new TestBean();
}
}
@Test
public void simplestPossibleConfiguration() {
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
static class SimplestPossibleConfig {
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
static class ConfigWithPrototypeBean {

View File

@ -19,14 +19,14 @@ package org.springframework.context.annotation.configuration;
import java.lang.annotation.Inherited;
import org.junit.Test;
import test.beans.TestBean;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import test.beans.TestBean;
/**
* Tests that polymorphic Configuration classes need not explicitly redeclare the
* {@link Configuration} annotation. This respects the {@link Inherited} nature
@ -37,7 +37,7 @@ import test.beans.TestBean;
public class PolymorphicConfigurationTests {
@Test
public void subclassNeedNotDeclareConfigurationAnnotation() {
public void beanMethodsDetectedOnSuperClass() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
@ -56,6 +56,7 @@ public class PolymorphicConfigurationTests {
}
@Configuration
static class Config extends SuperConfig {
}

View File

@ -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
* @return the Map of annotation attributes, with attribute names as keys
* and corresponding attribute values as values
*/
public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
return getAnnotationAttributes(annotation, false);
}
/**
* 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>();
Method[] methods = annotation.annotationType().getDeclaredMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
for (Method method : methods) {
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
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) {
throw new IllegalStateException("Could not obtain annotation attribute values", ex);

View File

@ -45,9 +45,20 @@ public interface AnnotationMetadata extends ClassMetadata {
*/
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
* given annotation type of the underlying class.
* @param annotationType the meta-annotation type to look for
* @return the meta-annotation type names
*/
Set<String> getMetaAnnotationTypes(String annotationType);
@ -61,26 +72,20 @@ public interface AnnotationMetadata extends ClassMetadata {
boolean hasMetaAnnotation(String metaAnnotationType);
/**
* 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.
* Retrieve the method metadata for all methods that are annotated
* with at least one annotation type.
* @return a Set of {@link MethodMetadata} for methods that have annotations.
* The return value will be an empty set if no annotated methods are found.
*/
Map<String, Object> getAnnotationAttributes(String annotationType);
// TODO return null would be more consistent with other methods if no match is found
Set<MethodMetadata> getAnnotatedMethods();
/**
* Retrieve the method meta-data for all methods that have the
* Retrieve the method metadata for all methods that have the
* given annotation type.
* @param annotationType the annotation type to look for
* @return a Set of (@link MethodMetadata) for methods that
* have a matching annotation. The return value will be an
* empty set if no methods match the annotation type.
* @return a Set of {@link MethodMetadata} for methods that have a matching
* annotation. The return value will be an empty set if no methods match
* the annotation type.
*/
Set<MethodMetadata> getAnnotatedMethods(String annotationType);

View File

@ -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");
* you may not use this file except in compliance with the License.
@ -49,6 +49,11 @@ public interface ClassMetadata {
*/
boolean isConcrete();
/**
* Return whether the underlying class is marked as 'final'.
*/
boolean isFinal();
/**
* Determine whether the underlying class is independent,
* i.e. whether it is a top-level class or a nested class

View File

@ -20,41 +20,60 @@ import java.util.Map;
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 Juergen Hoeller
* @since 3.0
* @see StandardMethodMetadata
* @see AnnotationMetadata#getAnnotatedMethods
*/
public interface MethodMetadata {
int getModifiers();
boolean isStatic();
/**
* Return the name of the method.
*/
String getMethodName();
//TODO does the method return type have a generic wildcard or generic type parameters?
// annotation metadata
Set<String> getAnnotationTypes();
boolean hasAnnotation(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);
/**
* Return whether the underlying method is declared as 'static'.
*/
boolean isStatic();
/**
* 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();
/**
* 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);
/**
* 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);
}

View File

@ -35,6 +35,10 @@ import org.springframework.core.annotation.AnnotationUtils;
*/
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) {
super(introspectedClass);
}
@ -59,6 +63,16 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
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) {
Annotation[] anns = getIntrospectedClass().getAnnotations();
for (Annotation ann : anns) {
@ -87,25 +101,25 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
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);
public Set<MethodMetadata> getAnnotatedMethods() {
Method[] methods = getIntrospectedClass().getDeclaredMethods();
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
for (Method method : methods) {
if (method.getAnnotations().length > 0) {
annotatedMethods.add(new StandardMethodMetadata(method));
}
}
return null;
return annotatedMethods;
}
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
Method[] methods = getIntrospectedClass().getMethods();
Method[] methods = getIntrospectedClass().getDeclaredMethods();
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
for (Method method : methods) {
Annotation[] methodAnnotations = method.getAnnotations();
for (Annotation ann : methodAnnotations) {
if (ann.annotationType().getName().equals(annotationType)) {
MethodMetadata mm = new StandardMethodMetadata(method);
annotatedMethods.add(mm);
annotatedMethods.add(new StandardMethodMetadata(method));
}
}
}

View File

@ -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");
* 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 org.springframework.util.Assert;
/**
* {@link ClassMetadata} implementation that uses standard reflection
* to introspect a given <code>Class</code>.
@ -30,57 +32,69 @@ public class StandardClassMetadata implements ClassMetadata {
private final Class introspectedClass;
/**
* Create a new StandardClassMetadata wrapper for the given Class.
* @param introspectedClass the Class to introspect
*/
public StandardClassMetadata(Class introspectedClass) {
Assert.notNull(introspectedClass, "Class must not be null");
this.introspectedClass = introspectedClass;
}
/**
* Return the underlying Class.
*/
public final Class getIntrospectedClass() {
return this.introspectedClass;
}
public String getClassName() {
return getIntrospectedClass().getName();
return this.introspectedClass.getName();
}
public boolean isInterface() {
return getIntrospectedClass().isInterface();
return this.introspectedClass.isInterface();
}
public boolean isAbstract() {
return Modifier.isAbstract(getIntrospectedClass().getModifiers());
return Modifier.isAbstract(this.introspectedClass.getModifiers());
}
public boolean isConcrete() {
return !(isInterface() || isAbstract());
}
public boolean isFinal() {
return Modifier.isFinal(this.introspectedClass.getModifiers());
}
public boolean isIndependent() {
return (!hasEnclosingClass() ||
(getIntrospectedClass().getDeclaringClass() != null &&
Modifier.isStatic(getIntrospectedClass().getModifiers())));
(this.introspectedClass.getDeclaringClass() != null &&
Modifier.isStatic(this.introspectedClass.getModifiers())));
}
public boolean hasEnclosingClass() {
return (getIntrospectedClass().getEnclosingClass() != null);
return (this.introspectedClass.getEnclosingClass() != null);
}
public String getEnclosingClassName() {
Class enclosingClass = getIntrospectedClass().getEnclosingClass();
Class enclosingClass = this.introspectedClass.getEnclosingClass();
return (enclosingClass != null ? enclosingClass.getName() : null);
}
public boolean hasSuperClass() {
return (getIntrospectedClass().getSuperclass() != null);
return (this.introspectedClass.getSuperclass() != null);
}
public String getSuperClassName() {
Class superClass = getIntrospectedClass().getSuperclass();
Class superClass = this.introspectedClass.getSuperclass();
return (superClass != null ? superClass.getName() : null);
}
public String[] getInterfaceNames() {
Class[] ifcs = getIntrospectedClass().getInterfaces();
Class[] ifcs = this.introspectedClass.getInterfaces();
String[] ifcNames = new String[ifcs.length];
for (int i = 0; i < ifcs.length; i++) {
ifcNames[i] = ifcs[i].getName();

View File

@ -17,7 +17,6 @@
package org.springframework.core.type;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
@ -28,52 +27,62 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
/**
* {@link MethodMetadata} implementation that uses standard reflection
* to introspect a given <code>Method</code>.
*
* @author Mark Pollack
* @author Juergen Hoeller
* @since 3.0
*/
public class StandardMethodMetadata implements MethodMetadata {
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() {
return this.introspectedMethod;
}
public Map<String, Object> getAnnotationAttributes(String annotationType) {
Annotation[] anns = getIntrospectedMethod().getAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(ann);
}
}
return null;
public String getMethodName() {
return this.introspectedMethod.getName();
}
public boolean isStatic() {
return Modifier.isStatic(this.introspectedMethod.getModifiers());
}
public boolean isFinal() {
return Modifier.isFinal(this.introspectedMethod.getModifiers());
}
public boolean isOverridable() {
return (!isStatic() && !isFinal() && !Modifier.isPrivate(this.introspectedMethod.getModifiers()));
}
public Set<String> getAnnotationTypes() {
Set<String> types = new HashSet<String>();
Annotation[] anns = getIntrospectedMethod().getAnnotations();
Annotation[] anns = this.introspectedMethod.getAnnotations();
for (Annotation ann : anns) {
types.add(ann.annotationType().getName());
}
return types;
}
public String getMethodName() {
return introspectedMethod.getName();
}
public int getModifiers() {
return introspectedMethod.getModifiers();
}
public boolean hasAnnotation(String annotationType) {
Annotation[] anns = getIntrospectedMethod().getAnnotations();
Annotation[] anns = this.introspectedMethod.getAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return true;
@ -82,44 +91,14 @@ public class StandardMethodMetadata implements MethodMetadata {
return false;
}
public boolean isStatic() {
return Modifier.isStatic(getIntrospectedMethod().getModifiers());
}
public Set<String> getMetaAnnotationTypes(String annotationType) {
Annotation[] anns = getIntrospectedMethod().getAnnotations();
public Map<String, Object> getAnnotationAttributes(String annotationType) {
Annotation[] anns = this.introspectedMethod.getAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
Set<String> types = new HashSet<String>();
Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation meta : metaAnns) {
types.add(meta.annotationType().getName());
}
return types;
return AnnotationUtils.getAnnotationAttributes(ann, true);
}
}
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;
}
}

View File

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

View File

@ -16,12 +16,7 @@
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.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@ -30,11 +25,8 @@ import java.util.Set;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Type;
import org.springframework.asm.commons.EmptyVisitor;
import org.springframework.core.type.AnnotationMetadata;
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
@ -45,9 +37,9 @@ import org.springframework.util.ReflectionUtils;
* @author Mark Fisher
* @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>>();
@ -63,109 +55,28 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodMetadataReadingVisitor md = new MethodMetadataReadingVisitor(classLoader, name, access);
methodMetadataSet.add(md);
return md;
MethodMetadataReadingVisitor mm = new MethodMetadataReadingVisitor(name, access, this.classLoader);
this.methodMetadataSet.add(mm);
return mm;
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
final String className = Type.getType(desc).getClassName();
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
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);
}
};
String className = Type.getType(desc).getClassName();
return new AnnotationAttributesReadingVisitor(className, this.annotationMap, this.metaAnnotationMap, this.classLoader);
}
public Set<String> getAnnotationTypes() {
return this.attributesMap.keySet();
return this.annotationMap.keySet();
}
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) {
@ -182,17 +93,20 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
return false;
}
public Map<String, Object> getAnnotationAttributes(String annotationType) {
return this.attributesMap.get(annotationType);
public Set<MethodMetadata> getAnnotatedMethods() {
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
for (MethodMetadata method : this.methodMetadataSet) {
if (!method.getAnnotationTypes().isEmpty()) {
annotatedMethods.add(method);
}
}
return annotatedMethods;
}
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
for (MethodMetadata method : methodMetadataSet) {
if (method.hasAnnotation(annotationType))
{
for (MethodMetadata method : this.methodMetadataSet) {
if (method.hasAnnotation(annotationType)) {
annotatedMethods.add(method);
}
}

View File

@ -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");
* 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 {
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
public SimpleMetadataReader getMetadataReader(Resource resource) throws IOException {
public MetadataReader getMetadataReader(Resource resource) throws IOException {
synchronized (this.classReaderCache) {
SimpleMetadataReader metadataReader = this.classReaderCache.get(resource);
MetadataReader metadataReader = this.classReaderCache.get(resource);
if (metadataReader == null) {
metadataReader = super.getMetadataReader(resource);
this.classReaderCache.put(resource, metadataReader);

View File

@ -45,6 +45,8 @@ class ClassMetadataReadingVisitor implements ClassVisitor, ClassMetadata {
private boolean isAbstract;
private boolean isFinal;
private String enclosingClassName;
private boolean independentInnerClass;
@ -58,6 +60,7 @@ class ClassMetadataReadingVisitor implements ClassVisitor, ClassMetadata {
this.className = ClassUtils.convertResourcePathToClassName(name);
this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
this.isAbstract = ((access & Opcodes.ACC_ABSTRACT) != 0);
this.isFinal = ((access & Opcodes.ACC_FINAL) != 0);
if (supername != null) {
this.superClassName = ClassUtils.convertResourcePathToClassName(supername);
}
@ -122,6 +125,10 @@ class ClassMetadataReadingVisitor implements ClassVisitor, ClassMetadata {
return !(this.isInterface || this.isAbstract);
}
public boolean isFinal() {
return this.isFinal;
}
public boolean isIndependent() {
return (this.enclosingClassName == null || this.independentInnerClass);
}

View File

@ -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");
* you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.core.type.classreading;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
@ -28,13 +29,19 @@ import org.springframework.core.type.ClassMetadata;
*/
public interface MetadataReader {
/**
* Return the resource reference for the class file.
*/
Resource getResource();
/**
* Read basic class metadata for the underlying class.
*/
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();

View File

@ -16,13 +16,7 @@
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.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@ -32,139 +26,68 @@ import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.asm.commons.EmptyVisitor;
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 Juergen Hoeller
* @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>> attributesMap = new LinkedHashMap<String, Map<String, Object>>();
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
private final Map<String, Map<String, Object>> annotationMap = new LinkedHashMap<String, Map<String, Object>>();
public MethodMetadataReadingVisitor(ClassLoader classLoader, String name, int access) {
public MethodMetadataReadingVisitor(String name, int access, ClassLoader classLoader) {
super(new EmptyVisitor());
this.classLoader = classLoader;
this.name = name;
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 String getMethodName() {
return this.name;
}
public boolean isStatic() {
return ((this.access & Opcodes.ACC_STATIC) != 0);
}
public boolean isFinal() {
return ((this.access & Opcodes.ACC_FINAL) != 0);
}
public boolean isOverridable() {
return (!isStatic() && !isFinal() && ((this.access & Opcodes.ACC_PRIVATE) == 0));
}
public Set<String> getAnnotationTypes() {
return this.attributesMap.keySet();
}
public String getMethodName() {
return name;
}
public int getModifiers() {
return access;
return this.annotationMap.keySet();
}
public boolean hasAnnotation(String annotationType) {
return this.attributesMap.containsKey(annotationType);
return this.annotationMap.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() {
return this.isStatic;
}
public Set<String> getAnnotationTypesWithMetaAnnotation(String metaAnnotationType) {
Set<String> annotationTypes = new LinkedHashSet<String>();
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);
}
}
return annotationTypes;
public Map<String, Object> getAnnotationAttributes(String annotationType) {
return this.annotationMap.get(annotationType);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
final String className = Type.getType(desc).getClassName();
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
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);
}
};
String className = Type.getType(desc).getClassName();
return new AnnotationAttributesReadingVisitor(className, this.annotationMap, null, this.classLoader);
}
}

View File

@ -16,7 +16,11 @@
package org.springframework.core.type.classreading;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.asm.ClassReader;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
@ -30,7 +34,9 @@ import org.springframework.core.type.ClassMetadata;
* @author Juergen Hoeller
* @since 2.5
*/
public class SimpleMetadataReader implements MetadataReader {
final class SimpleMetadataReader implements MetadataReader {
private final Resource resource;
private final ClassReader classReader;
@ -41,27 +47,23 @@ public class SimpleMetadataReader implements MetadataReader {
private AnnotationMetadata annotationMetadata;
SimpleMetadataReader(ClassReader classReader, ClassLoader classLoader) {
this.classReader = classReader;
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
this.resource = resource;
InputStream is = this.resource.getInputStream();
try {
this.classReader = new ClassReader(is);
}
finally {
is.close();
}
this.classLoader = classLoader;
}
/**
* Return the underlying ASM ClassReader.
*/
public final ClassReader getClassReader() {
return this.classReader;
public Resource getResource() {
return this.resource;
}
/**
* Return the underlying ClassLoader.
*/
public final ClassLoader getClassLoader() {
return this.classLoader;
}
public ClassMetadata getClassMetadata() {
if (this.classMetadata == null) {
ClassMetadataReadingVisitor visitor = new ClassMetadataReadingVisitor();

View File

@ -17,9 +17,7 @@
package org.springframework.core.type.classreading;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.asm.ClassReader;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
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 +
ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
return getMetadataReader(this.resourceLoader.getResource(resourcePath));
}
public SimpleMetadataReader getMetadataReader(Resource resource) throws IOException {
InputStream is = resource.getInputStream();
try {
return new SimpleMetadataReader(new ClassReader(is), this.resourceLoader.getClassLoader());
}
finally {
is.close();
}
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
}