();
+
+ 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) {
+ String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
+ Class extends Annotation> annoType = ConfigurationClassReaderUtils.loadToolingSafeClass(annoTypeName, classLoader);
+ if (annoType == null) {
+ return new EmptyVisitor();
+ }
+ ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoType, 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));
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
new file mode 100644
index 00000000000..afebf5feaa2
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
@@ -0,0 +1,207 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.aop.scope.ScopedProxyUtils;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.BeanDefinitionReader;
+import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.annotation.Autowire;
+import org.springframework.core.io.Resource;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Reads a given fully-populated configuration model, registering bean definitions
+ * with the given {@link BeanDefinitionRegistry} based on its contents.
+ *
+ * This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does
+ * not implement/extend any of its artifacts as a configuration model is not a {@link Resource}.
+ *
+ * @author Chris Beams
+ * @author Juergen Hoeller
+ * @since 3.0
+ */
+class ConfigurationClassBeanDefinitionReader {
+
+ private static final Log logger = LogFactory.getLog(ConfigurationClassBeanDefinitionReader.class);
+
+ private final BeanDefinitionRegistry registry;
+
+
+ public ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry) {
+ Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
+ this.registry = registry;
+ }
+
+
+ /**
+ * Reads {@code configurationModel}, registering bean definitions with {@link #registry}
+ * based on its contents.
+ */
+ public void loadBeanDefinitions(Set configurationModel) {
+ for (ConfigurationClass configClass : configurationModel) {
+ loadBeanDefinitionsForConfigurationClass(configClass);
+ }
+ }
+
+ /**
+ * Reads a particular {@link ConfigurationClass}, registering bean definitions for the
+ * class itself, all its {@link Bean} methods
+ */
+ private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
+ doLoadBeanDefinitionForConfigurationClass(configClass);
+ for (ConfigurationClassMethod method : configClass.getBeanMethods()) {
+ loadBeanDefinitionsForModelMethod(method);
+ }
+ }
+
+ /**
+ * Registers the {@link Configuration} class itself as a bean definition.
+ */
+ private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
+ if (configClass.getBeanName() == null) {
+ GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
+ configBeanDef.setBeanClassName(configClass.getName());
+ String configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(configBeanDef, registry);
+ configClass.setBeanName(configBeanName);
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName));
+ }
+ }
+ }
+
+ /**
+ * Reads a particular {@link ConfigurationClassMethod}, registering bean definitions with
+ * {@link #registry} based on its contents.
+ */
+ private void loadBeanDefinitionsForModelMethod(ConfigurationClassMethod method) {
+ RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
+ ConfigurationClass configClass = method.getDeclaringClass();
+ beanDef.setFactoryBeanName(configClass.getBeanName());
+ beanDef.setFactoryMethodName(method.getName());
+ beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
+
+ // consider name and any aliases
+ Bean bean = method.getRequiredAnnotation(Bean.class);
+ List names = new ArrayList(Arrays.asList(bean.name()));
+ String beanName = (names.size() > 0) ? names.remove(0) : method.getName();
+ for (String alias : bean.name()) {
+ 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?
+ if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
+ // no -> then it's an external override, probably XML
+ // overriding is legal, return immediately
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Skipping loading bean definition for %s: a definition for bean " +
+ "'%s' already exists. This is likely due to an override in XML.", method, beanName));
+ }
+ return;
+ }
+ }
+
+ if (method.getAnnotation(Primary.class) != null) {
+ beanDef.setPrimary(true);
+ }
+
+ // is this bean to be instantiated lazily?
+ Lazy lazy = method.getAnnotation(Lazy.class);
+ if (lazy != null) {
+ beanDef.setLazyInit(lazy.value());
+ }
+ else {
+ Lazy defaultLazy = configClass.getAnnotation(Lazy.class);
+ if (defaultLazy != null) {
+ beanDef.setLazyInit(defaultLazy.value());
+ }
+ }
+
+ DependsOn dependsOn = method.getAnnotation(DependsOn.class);
+ if (dependsOn != null && dependsOn.value().length > 0) {
+ beanDef.setDependsOn(dependsOn.value());
+ }
+
+ Autowire autowire = bean.autowire();
+ if (autowire.isAutowire()) {
+ beanDef.setAutowireMode(autowire.value());
+ }
+
+ String initMethodName = bean.initMethod();
+ if (StringUtils.hasText(initMethodName)) {
+ beanDef.setInitMethodName(initMethodName);
+ }
+
+ String destroyMethodName = bean.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 (proxyMode == ScopedProxyMode.DEFAULT) {
+ proxyMode = ScopedProxyMode.NO;
+ }
+ }
+
+ // replace the original bean definition with the target one, if necessary
+ BeanDefinition beanDefToRegister = beanDef;
+ if (proxyMode != ScopedProxyMode.NO) {
+ BeanDefinitionHolder proxyDef = ScopedProxyUtils.createScopedProxy(
+ new BeanDefinitionHolder(beanDef, beanName), registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
+ beanDefToRegister = proxyDef.getBeanDefinition();
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getName(), beanName));
+ }
+
+ registry.registerBeanDefinition(beanName, beanDefToRegister);
+ }
+
+
+ /**
+ * {@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
+ * (e.g. via XML).
+ */
+ @SuppressWarnings("serial")
+ private class ConfigurationClassBeanDefinition extends RootBeanDefinition {
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java
index c3036d9bd03..0cbae633883 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java
@@ -16,60 +16,59 @@
package org.springframework.context.annotation;
-import static java.lang.String.*;
-import static org.springframework.context.annotation.AsmUtils.*;
-
-import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.List;
-import net.sf.cglib.core.DefaultGeneratorStrategy;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.asm.ClassAdapter;
-import org.springframework.asm.ClassReader;
-import org.springframework.asm.ClassWriter;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+
+import org.springframework.aop.scope.ScopedProxyUtils;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
-
/**
* Enhances {@link Configuration} classes by generating a CGLIB subclass capable of
* interacting with the Spring container to respect bean semantics.
- *
- * @see #enhance(String)
- *
+ *
* @author Chris Beams
+ * @author Juergen Hoeller
+ * @since 3.0
+ * @see #enhance
* @see ConfigurationClassPostProcessor
*/
class ConfigurationClassEnhancer {
- private static final Log log = LogFactory.getLog(ConfigurationClassEnhancer.class);
+ private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
+
+ private final List callbackInstances = new ArrayList();
+
+ private final List> callbackTypes = new ArrayList>();
- private final ArrayList callbackInstances = new ArrayList();
- private final ArrayList> callbackTypes = new ArrayList>();
private final CallbackFilter callbackFilter;
/**
* Creates a new {@link ConfigurationClassEnhancer} instance.
*/
- public ConfigurationClassEnhancer(DefaultListableBeanFactory beanFactory) {
- Assert.notNull(beanFactory, "beanFactory must be non-null");
+ 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)
+ for (Callback callback : callbackInstances) {
callbackTypes.add(callback.getClass());
+ }
- // set up the callback filter to return the index of the BeanMethodInterceptor when
+ // Set up the callback filter to return the index of the BeanMethodInterceptor when
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
callbackFilter = new CallbackFilter() {
public int accept(Method candidateMethod) {
@@ -82,24 +81,18 @@ class ConfigurationClassEnhancer {
/**
* Loads the specified class and generates a CGLIB subclass of it equipped with
* container-aware callbacks capable of respecting scoping and other bean semantics.
- *
* @return fully-qualified name of the enhanced subclass
*/
- public String enhance(String configClassName) {
- if (log.isInfoEnabled())
- log.info("Enhancing " + configClassName);
-
- Class> superclass = loadRequiredClass(configClassName);
-
- Class> subclass = createClass(newEnhancer(superclass), superclass);
-
- subclass = nestOneClassDeeperIfAspect(superclass, subclass);
-
- if (log.isInfoEnabled())
- log.info(format("Successfully enhanced %s; enhanced class name is: %s",
- configClassName, subclass.getName()));
-
- return subclass.getName();
+ public Class enhance(Class configClass) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Enhancing " + configClass.getName());
+ }
+ Class> enhancedClass = createClass(newEnhancer(configClass));
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format("Successfully enhanced %s; enhanced class name is: %s",
+ configClass.getName(), enhancedClass.getName()));
+ }
+ return enhancedClass;
}
/**
@@ -116,7 +109,7 @@ class ConfigurationClassEnhancer {
enhancer.setSuperclass(superclass);
enhancer.setUseFactory(false);
enhancer.setCallbackFilter(callbackFilter);
- enhancer.setCallbackTypes(callbackTypes.toArray(new Class>[] {}));
+ enhancer.setCallbackTypes(callbackTypes.toArray(new Class[callbackTypes.size()]));
return enhancer;
}
@@ -125,53 +118,86 @@ class ConfigurationClassEnhancer {
* Uses enhancer to generate a subclass of superclass, ensuring that
* {@link #callbackInstances} are registered for the new subclass.
*/
- private Class> createClass(Enhancer enhancer, Class> superclass) {
+ private Class> createClass(Enhancer enhancer) {
Class> subclass = enhancer.createClass();
-
- Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[] {}));
-
+ Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[callbackInstances.size()]));
return subclass;
}
+
/**
- * Works around a constraint imposed by the AspectJ 5 annotation-style programming
- * model. See comments inline for detail.
- *
- * @return original subclass instance unless superclass is annnotated with @Aspect, in
- * which case a subclass of the subclass is returned
+ * Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper
+ * handling of bean semantics such as scoping and AOP proxying.
+ * @author Chris Beams
+ * @see Bean
+ * @see ConfigurationClassEnhancer
*/
- private Class> nestOneClassDeeperIfAspect(Class> superclass, Class> origSubclass) {
- boolean superclassIsAnAspect = false;
-
- // check for @Aspect by name rather than by class literal to avoid
- // requiring AspectJ as a runtime dependency.
- for (Annotation anno : superclass.getAnnotations())
- if (anno.annotationType().getName().equals("org.aspectj.lang.annotation.Aspect"))
- superclassIsAnAspect = true;
-
- if (!superclassIsAnAspect)
- return origSubclass;
-
- // the superclass is annotated with AspectJ's @Aspect.
- // this means that we must create a subclass of the subclass
- // in order to avoid some guard logic in Spring core that disallows
- // extending a concrete aspect class.
- Enhancer enhancer = newEnhancer(origSubclass);
- enhancer.setStrategy(new DefaultGeneratorStrategy() {
- @Override
- protected byte[] transform(byte[] b) throws Exception {
- ClassWriter writer = new ClassWriter(false);
- ClassAdapter adapter = new AddAnnotationAdapter(writer, "Lorg/aspectj/lang/annotation/Aspect;");
- ClassReader reader = new ClassReader(b);
- reader.accept(adapter, false);
- return writer.toByteArray();
+ private static class BeanMethodInterceptor implements MethodInterceptor {
+
+ private static final Log logger = LogFactory.getLog(BeanMethodInterceptor.class);
+
+ private final ConfigurableBeanFactory beanFactory;
+
+ public BeanMethodInterceptor(ConfigurableBeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+
+ /**
+ * Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
+ * existence of this bean object.
+ */
+ public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
+ // by default the bean name is the name of the @Bean-annotated method
+ String beanName = method.getName();
+
+ // check to see if the user has explicitly set the bean name
+ Bean bean = method.getAnnotation(Bean.class);
+ if(bean != null && bean.name().length > 0) {
+ beanName = bean.name()[0];
}
- });
- // create a subclass of the original subclass
- Class> newSubclass = createClass(enhancer, origSubclass);
+ // determine whether this bean is a scoped-proxy
+ Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
+ if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
+ String scopedBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
+ if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
+ beanName = scopedBeanName;
+ }
+ }
- return newSubclass;
- }
+ // to handle the case of an inter-bean method reference, we must explicitly check the
+ // 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);
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Returning cached object [%s] for @Bean method %s.%s",
+ cachedBean, method.getDeclaringClass().getSimpleName(), beanName));
+ }
+ return cachedBean;
+ }
+
+ // actually create and return the bean
+ return proxy.invokeSuper(obj, args);
+ }
+
+ /**
+ * Check the beanFactory to see whether the bean named beanName 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.
+ * 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 beanName already exists in the factory
+ */
+ private boolean factoryContainsBean(String beanName) {
+ return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName);
+ }
+ }
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/BeanMethod.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassMethod.java
similarity index 51%
rename from org.springframework.context/src/main/java/org/springframework/context/annotation/BeanMethod.java
rename to org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassMethod.java
index 121aaada018..a9e977b09bb 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/BeanMethod.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassMethod.java
@@ -16,9 +16,6 @@
package org.springframework.context.annotation;
-import static java.lang.String.*;
-import static org.springframework.context.annotation.StandardScopes.*;
-
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -28,34 +25,42 @@ 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;
/**
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
*
* @author Chris Beams
+ * @author Juergen Hoeller
+ * @since 3.0
* @see ConfigurationClass
- * @see ConfigurationModel
* @see ConfigurationClassParser
- * @see ConfigurationModelBeanDefinitionReader
+ * @see ConfigurationClassBeanDefinitionReader
*/
-final class BeanMethod implements BeanMetadataElement {
+final class ConfigurationClassMethod implements BeanMetadataElement {
private final String name;
+
private final int modifiers;
- private final ModelClass returnType;
+
+ private final ReturnType returnType;
+
private final ArrayList annotations = new ArrayList();
private transient ConfigurationClass declaringClass;
+
private transient Object source;
- public BeanMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
+
+ public ConfigurationClassMethod(String name, int modifiers, ReturnType returnType, Annotation... annotations) {
Assert.hasText(name);
this.name = name;
Assert.notNull(annotations);
- for (Annotation annotation : annotations)
+ for (Annotation annotation : annotations) {
this.annotations.add(annotation);
+ }
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers);
this.modifiers = modifiers;
@@ -68,7 +73,7 @@ final class BeanMethod implements BeanMetadataElement {
return name;
}
- public ModelClass getReturnType() {
+ public ReturnType getReturnType() {
return returnType;
}
@@ -100,18 +105,17 @@ final class BeanMethod implements BeanMetadataElement {
*/
public T getRequiredAnnotation(Class annoType) {
T anno = getAnnotation(annoType);
-
- if(anno == null)
+ if (anno == null) {
throw new IllegalStateException(
- format("required annotation %s is not present on %s", annoType.getSimpleName(), this));
-
+ 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#addBeanMethod(BeanMethod)
+ * @see ConfigurationClass#addMethod(ConfigurationClassMethod)
*/
public void setDeclaringClass(ConfigurationClass declaringClass) {
this.declaringClass = declaringClass;
@@ -130,97 +134,118 @@ final class BeanMethod implements BeanMetadataElement {
}
public Location getLocation() {
- if (declaringClass == null)
+ if (declaringClass == null) {
throw new IllegalStateException(
"declaringClass property is null. Call setDeclaringClass() before calling getLocation()");
+ }
return new Location(declaringClass.getLocation().getResource(), getSource());
}
public void validate(ProblemReporter problemReporter) {
-
- if (Modifier.isPrivate(getModifiers()))
+ if (Modifier.isPrivate(getModifiers())) {
problemReporter.error(new PrivateMethodError());
-
- if (Modifier.isFinal(getModifiers()))
+ }
+ if (Modifier.isFinal(getModifiers())) {
problemReporter.error(new FinalMethodError());
-
- Scope scope = this.getAnnotation(Scope.class);
- if(scope != null
- && scope.proxyMode() != ScopedProxyMode.NO
- && (scope.value().equals(SINGLETON) || scope.value().equals(PROTOTYPE)))
- problemReporter.error(new InvalidScopedProxyDeclarationError(this));
+ }
}
- @Override
- public String toString() {
- String returnTypeName = returnType == null ? "" : returnType.getSimpleName();
- return format("%s: name=%s; returnType=%s; modifiers=%d",
- getClass().getSimpleName(), name, returnTypeName, modifiers);
- }
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((annotations == null) ? 0 : annotations.hashCode());
- result = prime * result + modifiers;
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
- return result;
- }
+ static class ReturnType implements BeanMetadataElement {
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- BeanMethod other = (BeanMethod) obj;
- if (annotations == null) {
- if (other.annotations != null)
- return false;
- } else if (!annotations.equals(other.annotations))
- return false;
- if (modifiers != other.modifiers)
- return false;
- if (name == null) {
- if (other.name != null)
- return false;
- } else if (!name.equals(other.name))
- return false;
- if (returnType == null) {
- if (other.returnType != null)
- return false;
- } else if (!returnType.equals(other.returnType))
- return false;
- return true;
- }
+ private String name;
+
+ private boolean isInterface;
+
+ private transient Object source;
- /** {@link Bean} methods must be non-private in order to accommodate CGLIB. */
- class PrivateMethodError extends Problem {
- PrivateMethodError() {
- super(format("Method '%s' may not be private; increase the method's visibility to continue", getName()),
- BeanMethod.this.getLocation());
+
+ public ReturnType(String name) {
+ this.name = name;
}
- }
- /** {@link Bean} methods must be non-final in order to accommodate CGLIB. */
- class FinalMethodError extends Problem {
- FinalMethodError() {
- super(format("Method '%s' may not be final; remove the final modifier to continue", getName()),
- BeanMethod.this.getLocation());
+ /**
+ * 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());
}
}
- class InvalidScopedProxyDeclarationError extends Problem {
- InvalidScopedProxyDeclarationError(BeanMethod method) {
- super(format("Method %s contains an invalid annotation declaration: scoped proxies "
- + "cannot be created for singleton/prototype beans", method.getName()),
- BeanMethod.this.getLocation());
+
+ /**
+ * {@link Bean} methods must be non-private in order to accommodate CGLIB.
+ */
+ private class PrivateMethodError 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());
+ }
}
-}
\ No newline at end of file
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassMethodVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassMethodVisitor.java
deleted file mode 100644
index 918c4b88505..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassMethodVisitor.java
+++ /dev/null
@@ -1,138 +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 org.springframework.context.annotation.AsmUtils.*;
-import static org.springframework.util.ClassUtils.*;
-
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
-
-import org.springframework.asm.AnnotationVisitor;
-import org.springframework.asm.ClassAdapter;
-import org.springframework.asm.Label;
-import org.springframework.asm.MethodAdapter;
-import org.springframework.asm.MethodVisitor;
-import org.springframework.asm.Opcodes;
-
-
-/**
- * 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}.
- *
- * @author Chris Beams
- */
-class ConfigurationClassMethodVisitor extends MethodAdapter {
-
- private final ConfigurationClass configClass;
- private final String methodName;
- private final int modifiers;
- private final ModelClass returnType;
- private final ArrayList annotations = new ArrayList();
- private final ClassLoader classLoader;
-
- private int lineNumber;
-
- /**
- * Creates 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(ASM_EMPTY_VISITOR);
-
- 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
- public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
- String annoClassName = convertAsmTypeDescriptorToClassName(annoTypeDesc);
-
- Class extends Annotation> annoClass = loadToolingSafeClass(annoClassName, classLoader);
-
- if (annoClass == null)
- return super.visitAnnotation(annoTypeDesc, visible);
-
- Annotation annotation = createMutableAnnotation(annoClass, classLoader);
-
- annotations.add(annotation);
-
- return new MutableAnnotationVisitor(annotation, classLoader);
- }
-
- /**
- * Provides the line number of this method within its declaring class. In reality, this
- * number is always inaccurate - lineNo 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[] {});
- BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray);
- method.setSource(lineNumber);
- configClass.addBeanMethod(method);
- break;
- }
- }
- }
-
- /**
- * Determines return type from ASM methodDescriptor and determines whether
- * that type is an interface.
- */
- private ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
- final ModelClass returnType = new ModelClass(getReturnTypeFromAsmMethodDescriptor(methodDescriptor));
-
- // detect whether the return type is an interface
- newAsmClassReader(convertClassNameToResourcePath(returnType.getName()), classLoader).accept(
- new ClassAdapter(ASM_EMPTY_VISITOR) {
- @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;
- }
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
index 5fa1c6b3c33..475fb88af52 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
@@ -16,75 +16,79 @@
package org.springframework.context.annotation;
-import static org.springframework.context.annotation.AsmUtils.*;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.Stack;
import org.springframework.asm.ClassReader;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.util.ClassUtils;
-
/**
- * Parses a {@link Configuration} class definition, populating a {@link ConfigurationModel}.
+ * Parses a {@link Configuration} class definition, populating a configuration model.
* This ASM-based implementation avoids reflection and eager classloading in order to
* interoperate effectively with tooling (Spring IDE) and OSGi environments.
- *
- * This class helps separate the concern of parsing the structure of a Configuration class
+ *
+ *
This class helps separate the concern of parsing the structure of a Configuration class
* from the concern of registering {@link BeanDefinition} objects based on the content of
* that model.
- *
+ *
* @author Chris Beams
- * @see ConfigurationModel
- * @see ConfigurationModelBeanDefinitionReader
+ * @author Juergen Hoeller
+ * @since 3.0
+ * @see ConfigurationClassBeanDefinitionReader
*/
class ConfigurationClassParser {
- /**
- * Model to be populated during calls to {@link #parse(Object, String)}
- */
- private final ConfigurationModel model;
+ private final Set model;
+
private final ProblemReporter problemReporter;
+
private final ClassLoader classLoader;
+
/**
- * Creates a new {@link ConfigurationClassParser} instance that will be used to populate a
- * {@link ConfigurationModel}.
- *
+ * 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}
- * @see #getConfigurationModel()
*/
public ConfigurationClassParser(ProblemReporter problemReporter, ClassLoader classLoader) {
- this.model = new ConfigurationModel();
+ this.model = new LinkedHashSet();
this.problemReporter = problemReporter;
this.classLoader = classLoader;
}
+
/**
- * Parse the {@link Configuration @Configuration} class encapsulated by
- * configurationSource .
- *
- * @param configurationSource reader for Configuration class being parsed
- * @param configurationId may be null, but if populated represents the bean id (assumes
- * that this configuration class was configured via XML)
+ * Parse the specified {@link Configuration @Configuration} class.
+ * @param className the name of the class to parse
+ * @param beanName may be null, but if populated represents the bean id
+ * (assumes that this configuration class was configured via XML)
*/
- public void parse(String className, String configurationId) {
-
+ public void parse(String className, String beanName) {
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
-
- ClassReader configClassReader = newAsmClassReader(getClassAsStream(resourcePath, classLoader));
-
+ ClassReader configClassReader = ConfigurationClassReaderUtils.newAsmClassReader(ConfigurationClassReaderUtils.getClassAsStream(resourcePath, classLoader));
ConfigurationClass configClass = new ConfigurationClass();
- configClass.setBeanName(configurationId);
-
+ configClass.setBeanName(beanName);
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
model.add(configClass);
}
/**
- * Returns the current {@link ConfigurationModel}, to be called after {@link #parse}.
+ * Recurse through the model validating each {@link ConfigurationClass}.
+ * @param problemReporter {@link ProblemReporter} against which any validation errors
+ * will be registered
+ * @see ConfigurationClass#validate
*/
- public ConfigurationModel getConfigurationModel() {
- return model;
+ public void validate() {
+ for (ConfigurationClass configClass : this.model) {
+ configClass.validate(this.problemReporter);
+ }
+ }
+
+ public Set getModel() {
+ return this.model;
}
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
index b54b5626922..0b0d15b4327 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
@@ -16,28 +16,31 @@
package org.springframework.context.annotation;
-import static java.lang.String.*;
-
import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.BeansException;
+
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.parsing.FailFastProblemReporter;
+import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+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.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-
+import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
/**
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
@@ -53,129 +56,134 @@ import org.springframework.util.StringUtils;
* executes.
*
* @author Chris Beams
+ * @author Juergen Hoeller
* @since 3.0
*/
-public class ConfigurationClassPostProcessor extends AbstractConfigurationClassProcessor
- implements Ordered, BeanFactoryPostProcessor {
+public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
+
+ public static final String CONFIGURATION_CLASS_ATTRIBUTE =
+ Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
+
+ /** 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);
/**
- * A well-known class in the CGLIB API used when testing to see if CGLIB
- * is present on the classpath. Package-private visibility allows for
- * manipulation by tests.
- * @see #assertCglibIsPresent(BeanDefinitionRegistry)
+ * 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}
*/
- static String CGLIB_TEST_CLASS = "net.sf.cglib.proxy.Callback";
+ private ProblemReporter problemReporter = new FailFastProblemReporter();
- /**
- * Holder for the calling BeanFactory
- * @see #postProcessBeanFactory(ConfigurableListableBeanFactory)
- */
- private DefaultListableBeanFactory beanFactory;
+ private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/**
- * @return {@link Ordered#HIGHEST_PRECEDENCE}.
+ * Override the default {@link ProblemReporter}.
+ * @param problemReporter custom problem reporter
*/
- public int getOrder() {
- return Ordered.HIGHEST_PRECEDENCE;
+ public void setProblemReporter(ProblemReporter problemReporter) {
+ this.problemReporter = problemReporter;
}
- /**
- * Finds {@link Configuration} bean definitions within clBeanFactory
- * and processes them in order to register bean definitions for each Bean method
- * found within; also prepares the the Configuration classes for servicing
- * bean requests at runtime by replacing them with CGLIB-enhanced subclasses.
- */
- public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
- Assert.isInstanceOf(DefaultListableBeanFactory.class, clBeanFactory);
- beanFactory = (DefaultListableBeanFactory) clBeanFactory;
-
- BeanDefinitionRegistry factoryBeanDefs = processConfigBeanDefinitions();
-
- for(String beanName : factoryBeanDefs.getBeanDefinitionNames())
- beanFactory.registerBeanDefinition(beanName, factoryBeanDefs.getBeanDefinition(beanName));
+ public void setBeanClassLoader(ClassLoader beanClassLoader) {
+ this.beanClassLoader = beanClassLoader;
+ }
- enhanceConfigurationClasses();
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE;
}
+
/**
- * @return a ConfigurationParser that uses the enclosing BeanFactory's
- * ClassLoader to load all Configuration class artifacts.
+ * Prepare the Configuration classes for servicing bean requests at runtime
+ * by replacing them with CGLIB-enhanced subclasses.
*/
- @Override
- protected ConfigurationClassParser createConfigurationParser() {
- return new ConfigurationClassParser(this.getProblemReporter(), beanFactory.getBeanClassLoader());
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
+ if (!(beanFactory instanceof BeanDefinitionRegistry)) {
+ throw new IllegalStateException(
+ "ConfigurationClassPostProcessor expects a BeanFactory that implements BeanDefinitionRegistry");
+ }
+ processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
+ enhanceConfigurationClasses(beanFactory);
}
+
/**
- * @return map of all non-abstract {@link BeanDefinition}s in the
- * enclosing {@link #beanFactory}
+ * Build and validate a configuration model based on the registry of
+ * {@link Configuration} classes.
*/
- @Override
- protected BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs) {
-
- BeanDefinitionRegistry configBeanDefs = new DefaultListableBeanFactory();
-
- for (String beanName : beanFactory.getBeanDefinitionNames()) {
- BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
+ protected final void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
+ Set configBeanDefs = new LinkedHashSet();
+ for (String beanName : registry.getBeanDefinitionNames()) {
+ BeanDefinition beanDef = registry.getBeanDefinition(beanName);
+ if (checkConfigurationClassBeanDefinition(beanDef)) {
+ configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
+ }
+ }
- if (beanDef.isAbstract() && !includeAbstractBeanDefs)
- continue;
+ // Return immediately if no @Configuration classes were found
+ if (configBeanDefs.isEmpty()) {
+ return;
+ }
- if (isConfigurationClassBeanDefinition(beanDef, beanFactory.getBeanClassLoader()))
- configBeanDefs.registerBeanDefinition(beanName, beanDef);
+ // Populate a new configuration model by parsing each @Configuration classes
+ ConfigurationClassParser parser = new ConfigurationClassParser(this.problemReporter, this.beanClassLoader);
+ for (BeanDefinitionHolder holder : configBeanDefs) {
+ parser.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
+ parser.validate();
- return configBeanDefs;
+ // Read the model and create bean definitions based on its content
+ new ConfigurationClassBeanDefinitionReader(registry).loadBeanDefinitions(parser.getModel());
}
/**
- * Post-processes a BeanFactory in search of Configuration class BeanDefinitions; any
- * candidates are then enhanced by a {@link ConfigurationClassEnhancer}. Candidate status is
- * determined by BeanDefinition attribute metadata.
- *
+ * Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
+ * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
+ * Candidate status is determined by BeanDefinition attribute metadata.
* @see ConfigurationClassEnhancer
- * @see BeanFactoryPostProcessor
*/
- private void enhanceConfigurationClasses() {
-
- BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(true);
-
- if (configBeanDefs.getBeanDefinitionCount() == 0)
+ private void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
+ Set configBeanDefs = new LinkedHashSet();
+ for (String beanName : beanFactory.getBeanDefinitionNames()) {
+ BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
+ if ("full".equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE))) {
+ configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
+ }
+ }
+ if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
-
- assertCglibIsPresent(configBeanDefs);
-
- ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(beanFactory);
-
- for (String beanName : configBeanDefs.getBeanDefinitionNames()) {
- BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
- String configClassName = beanDef.getBeanClassName();
- String enhancedClassName = enhancer.enhance(configClassName);
-
- if (logger.isDebugEnabled())
- logger.debug(format("Replacing bean definition '%s' existing class name '%s' "
- + "with enhanced class name '%s'", beanName, configClassName, enhancedClassName));
-
- beanDef.setBeanClassName(enhancedClassName);
}
- }
-
- /**
- * Tests for the presence of CGLIB on the classpath by trying to
- * classload {@link #CGLIB_TEST_CLASS}.
- * @throws IllegalStateException if CGLIB is not present.
- */
- private void assertCglibIsPresent(BeanDefinitionRegistry configBeanDefs) {
- try {
- Class.forName(CGLIB_TEST_CLASS);
- } catch (ClassNotFoundException e) {
+ if (!cglibAvailable) {
+ Set beanNames = new LinkedHashSet();
+ for (BeanDefinitionHolder holder : configBeanDefs) {
+ beanNames.add(holder.getBeanName());
+ }
throw new IllegalStateException("CGLIB is required to process @Configuration classes. " +
- "Either add CGLIB to the classpath or remove the following @Configuration bean definitions: [" +
- StringUtils.arrayToCommaDelimitedString(configBeanDefs.getBeanDefinitionNames()) + "]");
+ "Either add CGLIB to the classpath or remove the following @Configuration bean definitions: " +
+ beanNames);
+ }
+ ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(beanFactory);
+ for (BeanDefinitionHolder holder : configBeanDefs) {
+ AbstractBeanDefinition beanDef = (AbstractBeanDefinition) holder.getBeanDefinition();
+ try {
+ Class configClass = beanDef.resolveBeanClass(this.beanClassLoader);
+ Class enhancedClass = enhancer.enhance(configClass);
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' " +
+ "with enhanced class name '%s'", holder.getBeanName(), configClass.getName(), enhancedClass.getName()));
+ }
+ beanDef.setBeanClass(enhancedClass);
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
+ }
}
}
@@ -183,29 +191,41 @@ public class ConfigurationClassPostProcessor extends AbstractConfigurationClassP
* @return whether the BeanDefinition's beanClass (or its ancestry) is
* {@link Configuration}-annotated, false if no beanClass is specified.
*/
- private static boolean isConfigurationClassBeanDefinition(BeanDefinition beanDef, ClassLoader classLoader) {
-
+ private boolean checkConfigurationClassBeanDefinition(BeanDefinition beanDef) {
// accommodating SPR-5655
- Assert.isInstanceOf(AbstractBeanDefinition.class, beanDef);
- if(((AbstractBeanDefinition) beanDef).hasBeanClass())
- return AnnotationUtils.findAnnotation(
- ((AbstractBeanDefinition)beanDef).getBeanClass(), Configuration.class) != null;
-
+ 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;
+ }
+ }
+ SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(this.beanClassLoader);
String className = beanDef.getBeanClassName();
-
while (className != null && !(className.equals(Object.class.getName()))) {
try {
- MetadataReader metadataReader =
- new SimpleMetadataReaderFactory(classLoader).getMetadataReader(className);
- AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
- ClassMetadata classMetadata = metadataReader.getClassMetadata();
-
- if (annotationMetadata.hasAnnotation(Configuration.class.getName()))
+ MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
+ AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
+ if (metadata.hasAnnotation(Configuration.class.getName())) {
+ beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "full");
return true;
-
- className = classMetadata.getSuperClassName();
- } catch (IOException ex) {
- throw new RuntimeException(ex);
+ }
+ 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);
}
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassReaderUtils.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassReaderUtils.java
new file mode 100644
index 00000000000..08f97f1da29
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassReaderUtils.java
@@ -0,0 +1,376 @@
+/*
+ * 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.io.InputStream;
+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.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.asm.ClassReader;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import static org.springframework.core.annotation.AnnotationUtils.*;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+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 {
+
+ private static final Log logger = LogFactory.getLog(ConfigurationClassReaderUtils.class);
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Create a new ASM {@link ClassReader} for pathToClass . Appends '.class' to
+ * pathToClass before attempting to load.
+ */
+ public static ClassReader newAsmClassReader(String pathToClass, ClassLoader classLoader) {
+ InputStream is = getClassAsStream(pathToClass, classLoader);
+ return newAsmClassReader(is);
+ }
+
+ /**
+ * Convenience method that creates and returns a new ASM {@link ClassReader} for the
+ * given InputStream is , closing the InputStream after creating the
+ * ClassReader and rethrowing any IOException thrown during ClassReader instantiation as
+ * an unchecked exception. Logs and ignores any IOException thrown when closing the
+ * InputStream.
+ *
+ * @param is InputStream that will be provided to the new ClassReader instance.
+ */
+ public static ClassReader newAsmClassReader(InputStream is) {
+ try {
+ return new ClassReader(is);
+ }
+ catch (IOException ex) {
+ throw new BeanDefinitionStoreException("An unexpected exception occurred while creating ASM ClassReader: " + ex);
+ }
+ finally {
+ try {
+ is.close();
+ }
+ catch (IOException ex) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Uses the default ClassLoader to load pathToClass . Appends '.class' to
+ * pathToClass before attempting to load.
+ * @param pathToClass resource path to class, not including .class suffix. e.g.: com/acme/MyClass
+ * @return inputStream for pathToClass
+ * @throws RuntimeException if pathToClass does not exist
+ */
+ public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) {
+ String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX;
+ InputStream is = classLoader.getResourceAsStream(classFileName);
+ if (is == null) {
+ throw new IllegalStateException("Class file [" + classFileName + "] not found");
+ }
+ return is;
+ }
+
+ /**
+ * Loads the specified class using the default class loader, gracefully handling any
+ * {@link ClassNotFoundException} that may be thrown by issuing a WARN level logging
+ * statement and return null. This functionality is specifically implemented to
+ * accomodate tooling (Spring IDE) concerns, where user-defined types will not be
+ * available to the tooling.
+ *
+ * Because {@link ClassNotFoundException} is compensated for by returning null, callers
+ * must take care to handle the null case appropriately.
+ *
+ * In cases where the WARN logging statement is not desired, use the
+ * {@link #loadClass(String)} method, which returns null but issues no logging
+ * statements.
+ *
+ * This method should only ever return null in the case of a user-defined type be
+ * processed at tooling time. Therefore, tooling may not be able to represent any custom
+ * annotation semantics, but JavaConfig itself will not have any problem loading and
+ * respecting them at actual runtime.
+ *
+ * @param type of class to be returned
+ * @param fqClassName fully-qualified class name
+ *
+ * @return newly loaded class, null if class could not be found.
+ *
+ * @see #loadClass(String)
+ * @see #loadRequiredClass(String)
+ * @see ClassUtils#getDefaultClassLoader()
+ */
+ @SuppressWarnings("unchecked")
+ public static Class extends T> loadToolingSafeClass(String fqClassName, ClassLoader classLoader) {
+ try {
+ return (Class extends T>) classLoader.loadClass(fqClassName);
+ }
+ catch (ClassNotFoundException ex) {
+ logger.warn(String.format("Unable to load class [%s], likely due to tooling-specific restrictions."
+ + "Attempting to continue, but unexpected errors may occur", fqClassName), ex);
+ return null;
+ }
+ }
+
+ /**
+ * 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 attributes = new HashMap();
+ private final Map> attributeTypes = new HashMap>();
+
+ 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(), 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 proxy object and other object by comparing the return
+ * values of the methods specified by their common {@link Annotation} ancestry.
+ *
+ * other must be the same type as or a subtype of proxy . Will
+ * return false otherwise.
+ *
+ * Eagerly returns true if {@code proxy} == other
+ *
+ *
+ * Conforms strictly to the equals() specification for Annotation
+ *
+ *
+ * @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() {
+ ArrayList attribs = new ArrayList();
+
+ 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) {
+ Method method = null;
+
+ try {
+ method = annotationType.getDeclaredMethod(attributeName);
+ } catch (Exception ex) {
+ ReflectionUtils.handleReflectionException(ex);
+ }
+
+ return method.getReturnType();
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassVisitor.java
index 47bde05e471..89743101274 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassVisitor.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassVisitor.java
@@ -16,75 +16,83 @@
package org.springframework.context.annotation;
-import static java.lang.String.*;
-import static org.springframework.context.annotation.AsmUtils.*;
-import static org.springframework.util.ClassUtils.*;
-
import java.lang.annotation.Annotation;
-import java.util.HashMap;
+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.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.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 extends ClassAdapter {
+class ConfigurationClassVisitor implements ClassVisitor {
- private static final String OBJECT_DESC = convertClassNameToResourcePath(Object.class.getName());
+ private static final String OBJECT_DESC = ClassUtils.convertClassNameToResourcePath(Object.class.getName());
private final ConfigurationClass configClass;
- private final ConfigurationModel model;
+
+ private final Set model;
+
private final ProblemReporter problemReporter;
+
private final ClassLoader classLoader;
- private final HashMap innerClasses = new HashMap();
+ private final Stack importStack;
- private boolean processInnerClasses = true;
- public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model,
- ProblemReporter problemReporter, ClassLoader classLoader) {
- super(ASM_EMPTY_VISITOR);
+ public ConfigurationClassVisitor(ConfigurationClass configClass, Set model,
+ ProblemReporter problemReporter, ClassLoader classLoader) {
+
this.configClass = configClass;
this.model = model;
this.problemReporter = problemReporter;
this.classLoader = classLoader;
+ this.importStack = new ImportStack();
}
- public void setProcessInnerClasses(boolean processInnerClasses) {
- this.processInnerClasses = processInnerClasses;
+ private ConfigurationClassVisitor(ConfigurationClass configClass, Set model,
+ ProblemReporter problemReporter, ClassLoader classLoader, Stack importStack) {
+ this.configClass = configClass;
+ this.model = model;
+ this.problemReporter = problemReporter;
+ this.classLoader = classLoader;
+ this.importStack = importStack;
}
- @Override
- public void visitSource(String sourceFile, String debug) {
- String resourcePath =
- convertClassNameToResourcePath(configClass.getName())
- .substring(0, configClass.getName().lastIndexOf('.') + 1).concat(sourceFile);
-
- configClass.setSource(resourcePath);
- }
- @Override
- public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3,
- String superTypeDesc, String[] arg5) {
+ public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3, String superTypeDesc, String[] arg5) {
visitSuperType(superTypeDesc);
- configClass.setName(convertResourcePathToClassName(classTypeDesc));
+ 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
@@ -94,128 +102,327 @@ class ConfigurationClassVisitor extends ClassAdapter {
private void visitSuperType(String superTypeDesc) {
// traverse up the type hierarchy unless the next ancestor is java.lang.Object
- if (OBJECT_DESC.equals(superTypeDesc))
+ if (OBJECT_DESC.equals(superTypeDesc)) {
return;
+ }
+ ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader, importStack);
+ ClassReader reader = ConfigurationClassReaderUtils.newAsmClassReader(superTypeDesc, classLoader);
+ reader.accept(visitor, false);
+ }
+
+ public void visitSource(String sourceFile, String debug) {
+ String resourcePath =
+ ClassUtils.convertClassNameToResourcePath(configClass.getName())
+ .substring(0, configClass.getName().lastIndexOf('.') + 1).concat(sourceFile);
- ConfigurationClassVisitor visitor =
- new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader);
+ configClass.setSource(resourcePath);
+ }
- ClassReader reader = newAsmClassReader(superTypeDesc, classLoader);
- reader.accept(visitor, false);
+ public void visitOuterClass(String s, String s1, String s2) {
}
/**
* Visits a class level annotation on a {@link Configuration @Configuration} class.
- *
* 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 MutableAnnotation
+ * @see ConfigurationClassAnnotation
* @see Configuration
* @see Lazy
* @see Import
*/
- @Override
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
- String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc);
- Class extends Annotation> annoClass = loadToolingSafeClass(annoTypeName, classLoader);
+ String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
+ Class extends Annotation> annoClass = ConfigurationClassReaderUtils.loadToolingSafeClass(annoTypeName, classLoader);
- if (annoClass == null)
+ if (annoClass == null) {
// annotation was unable to be loaded -> probably Spring IDE unable to load a user-defined annotation
- return super.visitAnnotation(annoTypeDesc, visible);
+ return new EmptyVisitor();
+ }
if (Import.class.equals(annoClass)) {
- ImportStack importStack = ImportStackHolder.getImportStack();
-
if (!importStack.contains(configClass)) {
importStack.push(configClass);
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
}
-
problemReporter.error(new CircularImportProblem(configClass, importStack));
}
- Annotation mutableAnnotation = createMutableAnnotation(annoClass, classLoader);
+ ConfigurationClassAnnotation mutableAnnotation = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
configClass.addAnnotation(mutableAnnotation);
- return new MutableAnnotationVisitor(mutableAnnotation, classLoader);
+ 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}.
*/
- @Override
- public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor,
- String arg3, String[] arg4) {
-
+ public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, String arg3, String[] arg4) {
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers, classLoader);
}
+ public void visitEnd() {
+ }
+
+
/**
- * Implementation deals with inner classes here even though it would have been more
- * intuitive to deal with outer classes. Due to limitations in ASM (resulting from
- * limitations in the VM spec) we cannot directly look for outer classes in all cases,
- * so instead build up a model of {@link #innerClasses} and process declaring class
- * logic in a kind of inverted manner.
+ * 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}.
*/
- @Override
- public void visitInnerClass(String name, String outerName, String innerName, int access) {
- if (processInnerClasses == false)
- return;
+ private static class ConfigurationClassMethodVisitor extends MethodAdapter {
+
+ private final ConfigurationClass configClass;
+ private final String methodName;
+ private final int modifiers;
+ private final ConfigurationClassMethod.ReturnType returnType;
+ private final List annotations = new ArrayList();
+ 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
+ public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
+ String annoClassName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
+ Class extends Annotation> annoClass = ConfigurationClassReaderUtils.loadToolingSafeClass(annoClassName, classLoader);
+ if (annoClass == null) {
+ return super.visitAnnotation(annoTypeDesc, visible);
+ }
+ ConfigurationClassAnnotation annotation = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
+ annotations.add(annotation);
+ return new ConfigurationClassAnnotationVisitor(annotation, classLoader);
+ }
- String innerClassName = convertResourcePathToClassName(name);
- String configClassName = configClass.getName();
+ /**
+ * Provides the line number of this method within its declaring class. In reality, this
+ * number is always inaccurate - lineNo 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;
+ }
- // if the innerClassName is equal to configClassName, we just
- // ran into the outermost inner class look up the outer class
- // associated with this
- if (innerClassName.equals(configClassName)) {
- if (innerClasses.containsKey(outerName)) {
- configClass.setDeclaringClass(innerClasses.get(outerName));
+ /**
+ * 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;
+ }
}
- return;
}
- ConfigurationClass innerConfigClass = new ConfigurationClass();
+ /**
+ * Determines return type from ASM methodDescriptor 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
+ ConfigurationClassReaderUtils.newAsmClassReader(ClassUtils.convertClassNameToResourcePath(returnType.getName()), classLoader).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;
+ }
+ }
+
+
+ /**
+ * 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 model;
+
+ private final ProblemReporter problemReporter;
+
+ private final ClassLoader classLoader;
+
+ private final List classesToImport = new ArrayList();
+
+
+ public ImportAnnotationVisitor(
+ Set model, ProblemReporter problemReporter, ClassLoader classLoader) {
+
+ this.model = model;
+ this.problemReporter = problemReporter;
+ this.classLoader = classLoader;
+ }
+
+ 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();
+ ClassReader reader = ConfigurationClassReaderUtils.newAsmClassReader(ClassUtils.convertClassNameToResourcePath(classToImport), classLoader);
+ reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader, importStack), false);
+ if (configClass.getAnnotation(Configuration.class) == null) {
+ problemReporter.error(new NonAnnotatedConfigurationProblem(configClass.getName(), configClass.getLocation()));
+ }
+ else {
+ model.add(configClass);
+ }
+ }
+ }
- ConfigurationClassVisitor ccVisitor =
- new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), problemReporter, classLoader);
- ccVisitor.setProcessInnerClasses(false);
- ClassReader reader = newAsmClassReader(name, classLoader);
- reader.accept(ccVisitor, false);
+ @SuppressWarnings("serial")
+ private static class ImportStack extends Stack {
- if (innerClasses.containsKey(outerName))
- innerConfigClass.setDeclaringClass(innerClasses.get(outerName));
+ /**
+ * Simplified contains() implementation that tests to see if any {@link ConfigurationClass}
+ * exists within this stack that has the same name as elem . Elem must be of
+ * type ConfigurationClass.
+ */
+ @Override
+ public boolean contains(Object elem) {
+ if (!(elem instanceof ConfigurationClass)) {
+ return false;
+ }
+ ConfigurationClass configClass = (ConfigurationClass) elem;
+ Comparator comparator = new Comparator() {
+ 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)
+ *
+ * com.acme.Foo
+ * com.acme.Bar
+ * com.acme.Baz
+ *
+ * Returns "Foo->Bar->Baz". In the case of an empty stack, returns empty string.
+ */
+ @Override
+ public synchronized String toString() {
+ StringBuilder builder = new StringBuilder();
+ Iterator 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);
+ }
- // is the inner class a @Configuration class? If so, add it to the list
- if (innerConfigClass.getAnnotation(Configuration.class) != null)
- innerClasses.put(name, innerConfigClass);
}
/**
* {@link Problem} registered upon detection of a circular {@link Import}.
- *
* @see Import
- * @see ImportStack
- * @see ImportStackHolder
*/
- class CircularImportProblem extends Problem {
+ private static class CircularImportProblem extends Problem {
- CircularImportProblem(ConfigurationClass attemptedImport, Stack importStack) {
- super(format("A circular @Import has been detected: " +
+ public CircularImportProblem(ConfigurationClass attemptedImport, Stack 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(convertClassNameToResourcePath(importStack.peek().getName())),
- importStack.peek().getSource())
+ new Location(new ClassPathResource(
+ ClassUtils.convertClassNameToResourcePath(importStack.peek().getName())),
+ importStack.peek().getSource())
);
}
-
}
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationModel.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationModel.java
deleted file mode 100644
index a08008a0ff9..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationModel.java
+++ /dev/null
@@ -1,59 +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.util.LinkedHashSet;
-
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.parsing.ProblemReporter;
-
-
-/**
- * Represents the set of all user-defined {@link Configuration} classes. Once this model
- * is populated using a {@link ConfigurationClassParser}, it can be rendered out to a set of
- * {@link BeanDefinition} objects. This model provides an important layer of indirection
- * between the complexity of parsing a set of classes and the complexity of representing
- * the contents of those classes as BeanDefinitions.
- *
- * @author Chris Beams
- * @see ConfigurationClass
- * @see ConfigurationClassParser
- * @see ConfigurationModelBeanDefinitionReader
- */
-@SuppressWarnings("serial")
-final class ConfigurationModel extends LinkedHashSet {
-
- /**
- * Recurses 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(ProblemReporter problemReporter) {
- for (ConfigurationClass configClass : this)
- configClass.validate(problemReporter);
- }
-
- @Override
- public String toString() {
- return format("%s containing @Configuration classes: %s", getClass().getSimpleName(), super.toString());
- }
-
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationModelBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationModelBeanDefinitionReader.java
deleted file mode 100644
index 9c1dba09a30..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationModelBeanDefinitionReader.java
+++ /dev/null
@@ -1,279 +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 static org.springframework.util.StringUtils.*;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
-import org.springframework.aop.scope.ScopedProxyFactoryBean;
-import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.NoSuchBeanDefinitionException;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.beans.factory.support.BeanDefinitionReader;
-import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.GenericBeanDefinition;
-import org.springframework.beans.factory.support.RootBeanDefinition;
-import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
-import org.springframework.core.io.Resource;
-import org.springframework.util.Assert;
-
-
-/**
- * Reads a given fully-populated {@link ConfigurationModel}, registering bean definitions
- * with the given {@link BeanDefinitionRegistry} based on its contents.
- *
- * This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does not
- * implement/extend any of its artifacts as {@link ConfigurationModel} is not a
- * {@link Resource}.
- *
- * @author Chris Beams
- * @see ConfigurationModel
- * @see AbstractConfigurationClassProcessor#processConfigBeanDefinitions()
- */
-class ConfigurationModelBeanDefinitionReader {
-
- private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class);
-
- private BeanDefinitionRegistry registry;
-
-
- /**
- * Reads {@code configurationModel}, registering bean definitions with {@link #registry}
- * based on its contents.
- *
- * @return new {@link BeanDefinitionRegistry} containing {@link BeanDefinition}s read
- * from the model.
- */
- public BeanDefinitionRegistry loadBeanDefinitions(ConfigurationModel configurationModel) {
- registry = new SimpleBeanDefinitionRegistry();
-
- for (ConfigurationClass configClass : configurationModel)
- loadBeanDefinitionsForConfigurationClass(configClass);
-
- return registry;
- }
-
- /**
- * Reads a particular {@link ConfigurationClass}, registering bean definitions for the
- * class itself, all its {@link Bean} methods
- */
- private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
- doLoadBeanDefinitionForConfigurationClass(configClass);
-
- for (BeanMethod method : configClass.getBeanMethods())
- loadBeanDefinitionsForModelMethod(method);
- }
-
- /**
- * Registers the {@link Configuration} class itself as a bean definition.
- * @param beanDefs
- */
- private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
-
- GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
- configBeanDef.setBeanClassName(configClass.getName());
-
- String configBeanName = configClass.getBeanName();
-
- // consider the case where it's already been defined (probably in XML)
- // and potentially has PropertyValues and ConstructorArgs)
- if (registry.containsBeanDefinition(configBeanName)) {
- if (log.isInfoEnabled())
- log.info(format("Copying property and constructor arg values from existing bean definition for "
- + "@Configuration class %s to new bean definition", configBeanName));
- AbstractBeanDefinition existing = (AbstractBeanDefinition) registry.getBeanDefinition(configBeanName);
- configBeanDef.setPropertyValues(existing.getPropertyValues());
- configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues());
- configBeanDef.setResource(existing.getResource());
- }
-
- if (log.isInfoEnabled())
- log.info(format("Registering bean definition for @Configuration class %s", configBeanName));
-
- registry.registerBeanDefinition(configBeanName, configBeanDef);
- }
-
- /**
- * Reads a particular {@link BeanMethod}, registering bean definitions with
- * {@link #registry} based on its contents.
- */
- private void loadBeanDefinitionsForModelMethod(BeanMethod method) {
- RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
-
- ConfigurationClass configClass = method.getDeclaringClass();
-
- beanDef.setFactoryBeanName(configClass.getBeanName());
- beanDef.setFactoryMethodName(method.getName());
-
- Bean bean = method.getRequiredAnnotation(Bean.class);
-
- // consider scoping
- Scope scope = method.getAnnotation(Scope.class);
- if(scope != null)
- beanDef.setScope(scope.value());
-
- // consider name and any aliases
- ArrayList names = new ArrayList(Arrays.asList(bean.name()));
- String beanName = (names.size() > 0) ? names.remove(0) : method.getName();
- for (String alias : bean.name())
- registry.registerAlias(beanName, alias);
-
- // has this already been overriden (i.e.: via XML)?
- if (containsBeanDefinitionIncludingAncestry(beanName, registry)) {
- BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry);
-
- // is the existing bean definition one that was created by JavaConfig?
- if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
- // no -> then it's an external override, probably XML
-
- // overriding is legal, return immediately
- log.info(format("Skipping loading bean definition for %s: a definition for bean "
- + "'%s' already exists. This is likely due to an override in XML.", method, beanName));
- return;
- }
- }
-
- if (method.getAnnotation(Primary.class) != null)
- beanDef.setPrimary(true);
-
- // is this bean to be instantiated lazily?
- Lazy defaultLazy = configClass.getAnnotation(Lazy.class);
- if (defaultLazy != null)
- beanDef.setLazyInit(defaultLazy.value());
- Lazy lazy = method.getAnnotation(Lazy.class);
- if (lazy != null)
- beanDef.setLazyInit(lazy.value());
-
- // does this bean have a custom init-method specified?
- String initMethodName = bean.initMethod();
- if (hasText(initMethodName))
- beanDef.setInitMethodName(initMethodName);
-
- // does this bean have a custom destroy-method specified?
- String destroyMethodName = bean.destroyMethod();
- if (hasText(destroyMethodName))
- beanDef.setDestroyMethodName(destroyMethodName);
-
- // is this method annotated with @Scope(scopedProxy=...)?
- if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
- RootBeanDefinition targetDef = beanDef;
-
- // Create a scoped proxy definition for the original bean name,
- // "hiding" the target bean in an internal target definition.
- String targetBeanName = resolveHiddenScopedProxyBeanName(beanName);
- RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
- scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
-
- if (scope.proxyMode() == ScopedProxyMode.TARGET_CLASS)
- targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
- // ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
- // don't need to set it explicitly here.
- else
- scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
-
- // The target bean should be ignored in favor of the scoped proxy.
- targetDef.setAutowireCandidate(false);
-
- // Register the target bean as separate bean in the factory
- registry.registerBeanDefinition(targetBeanName, targetDef);
-
- // replace the original bean definition with the target one
- beanDef = scopedProxyDefinition;
- }
-
- if (bean.dependsOn().length > 0)
- beanDef.setDependsOn(bean.dependsOn());
-
- log.info(format("Registering bean definition for @Bean method %s.%s()",
- configClass.getName(), beanName));
-
- registry.registerBeanDefinition(beanName, beanDef);
-
- }
-
- private boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
- try {
- getBeanDefinitionIncludingAncestry(beanName, registry);
- return true;
- } catch (NoSuchBeanDefinitionException ex) {
- return false;
- }
- }
-
- private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
- if(!(registry instanceof ConfigurableListableBeanFactory))
- return registry.getBeanDefinition(beanName);
-
- ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) registry;
-
- do {
- if (clbf.containsBeanDefinition(beanName))
- return registry.getBeanDefinition(beanName);
-
- BeanFactory parent = clbf.getParentBeanFactory();
- if (parent == null) {
- clbf = null;
- } else if (parent instanceof ConfigurableListableBeanFactory) {
- clbf = (ConfigurableListableBeanFactory) parent;
- // TODO: re-enable
- // } else if (parent instanceof AbstractApplicationContext) {
- // clbf = ((AbstractApplicationContext) parent).getBeanFactory();
- } else {
- throw new IllegalStateException("unknown parent type: " + parent.getClass().getName());
- }
- } while (clbf != null);
-
- throw new NoSuchBeanDefinitionException(
- format("No bean definition matching name '%s' " +
- "could be found in %s or its ancestry", beanName, registry));
- }
-
- /**
- * Return the hidden name based on a scoped proxy bean name.
- *
- * @param originalBeanName the scope proxy bean name as declared in the
- * Configuration-annotated class
- *
- * @return the internally-used hidden bean name
- */
- public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
- Assert.hasText(originalBeanName);
- return TARGET_NAME_PREFIX.concat(originalBeanName);
- }
-
- /** Prefix used when registering the target object for a scoped proxy. */
- private static final String TARGET_NAME_PREFIX = "scopedTarget.";
-}
-
-
-/**
- * {@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
- * (e.g. via XML).
- */
-@SuppressWarnings("serial")
-class ConfigurationClassBeanDefinition extends RootBeanDefinition {
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/DependsOn.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/DependsOn.java
new file mode 100644
index 00000000000..7380032a4ab
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/DependsOn.java
@@ -0,0 +1,54 @@
+/*
+ * 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.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Documented;
+
+/**
+ * Beans on which the current bean depends. Any beans specified are guaranteed to be
+ * created by the container before this bean. Used infrequently in cases where a bean
+ * does not explicitly depend on another through properties or constructor arguments,
+ * but rather depends on the side effects of another bean's initialization.
+ * Note: This attribute will not be inherited by child bean definitions,
+ * hence it needs to be specified per concrete bean definition.
+ *
+ *
May be used on any class directly or indirectly annotated with
+ * {@link org.springframework.stereotype.Component} or on methods annotated
+ * with {@link Bean}.
+ *
+ *
Using {@link DependsOn} at the class level has no effect unless component-scanning
+ * is being used. If a {@link DependsOn}-annotated class is declared via XML,
+ * {@link DependsOn} annotation metadata is ignored, and
+ * {@literal } is respected instead.
+ *
+ * @author Juergen Hoeller
+ * @since 3.0
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface DependsOn {
+
+ String[] value() default {};
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Import.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Import.java
index 20ac1bf19c4..51b6f01fc86 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Import.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Import.java
@@ -23,18 +23,18 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-
/**
* Annotation that allows one {@link Configuration} class to import another Configuration,
* and thereby all its {@link Bean} definitions.
*
*
Provides functionality equivalent to the {@literal } element in Spring XML.
+ * Only supported for actual {@link Configuration} classes.
*
* @author Chris Beams
* @since 3.0
* @see Configuration
*/
-@Target({ ElementType.TYPE })
+@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportAnnotationVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportAnnotationVisitor.java
deleted file mode 100644
index b2d06a69bad..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportAnnotationVisitor.java
+++ /dev/null
@@ -1,89 +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 static org.springframework.context.annotation.AsmUtils.*;
-import static org.springframework.util.ClassUtils.*;
-
-import java.util.ArrayList;
-
-import org.springframework.asm.AnnotationVisitor;
-import org.springframework.asm.ClassReader;
-import org.springframework.asm.Type;
-import org.springframework.beans.factory.parsing.ProblemReporter;
-import org.springframework.util.Assert;
-
-
-/**
- * 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}.
- *
- * @author Chris Beams
- * @see Import
- * @see ImportStack
- * @see ImportStackHolder
- * @see ConfigurationClassVisitor
- */
-class ImportAnnotationVisitor extends AnnotationAdapter {
-
- private final ArrayList classesToImport = new ArrayList();
- private final ConfigurationModel model;
- private final ProblemReporter problemReporter;
- private final ClassLoader classLoader;
-
- public ImportAnnotationVisitor(ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) {
- super(ASM_EMPTY_VISITOR);
- this.model = model;
- this.problemReporter = problemReporter;
- this.classLoader = classLoader;
- }
-
- @Override
- public AnnotationVisitor visitArray(String attribName) {
- Assert.isTrue("value".equals(attribName),
- format("expected 'value' attribute, got unknown '%s' attribute", attribName));
-
- return new AnnotationAdapter(ASM_EMPTY_VISITOR) {
- @Override
- public void visit(String na, Object type) {
- Assert.isInstanceOf(Type.class, type);
- classesToImport.add(((Type) type).getClassName());
- }
- };
- }
-
- @Override
- public void visitEnd() {
- for (String classToImport : classesToImport)
- processClassToImport(classToImport);
-
- ImportStackHolder.getImportStack().pop();
- }
-
- private void processClassToImport(String classToImport) {
- ConfigurationClass configClass = new ConfigurationClass();
-
- ClassReader reader = newAsmClassReader(convertClassNameToResourcePath(classToImport), classLoader);
-
- reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
-
- model.add(configClass);
- }
-
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportStack.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportStack.java
deleted file mode 100644
index 82b5230ae49..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportStack.java
+++ /dev/null
@@ -1,83 +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.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.Stack;
-
-import org.springframework.util.Assert;
-
-
-/**
- * {@link Stack} used for detecting circular use of the {@link Import} annotation.
- *
- * @author Chris Beams
- * @see Import
- * @see ImportStackHolder
- * @see CircularImportException
- */
-@SuppressWarnings("serial")
-class ImportStack extends Stack {
-
- /**
- * Simplified contains() implementation that tests to see if any {@link ConfigurationClass}
- * exists within this stack that has the same name as elem . Elem must be of
- * type ConfigurationClass.
- */
- @Override
- public boolean contains(Object elem) {
- Assert.isInstanceOf(ConfigurationClass.class, elem);
-
- ConfigurationClass configClass = (ConfigurationClass) elem;
-
- Comparator comparator = new Comparator() {
- public int compare(ConfigurationClass first, ConfigurationClass second) {
- return first.getName().equals(second.getName()) ? 0 : 1;
- }
- };
-
- int index = Collections.binarySearch(this, configClass, comparator);
-
- return index >= 0 ? true : false;
- }
-
- /**
- * Given a stack containing (in order)
- *
- * com.acme.Foo
- * com.acme.Bar
- * com.acme.Baz
- *
- * Returns "Foo->Bar->Baz". In the case of an empty stack, returns empty string.
- */
- @Override
- public synchronized String toString() {
- StringBuilder builder = new StringBuilder();
-
- Iterator iterator = this.iterator();
- while (iterator.hasNext()) {
- builder.append(iterator.next().getSimpleName());
- if (iterator.hasNext())
- builder.append("->");
- }
-
- return builder.toString();
- }
-
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportStackHolder.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportStackHolder.java
deleted file mode 100644
index 33def118492..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportStackHolder.java
+++ /dev/null
@@ -1,48 +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;
-
-
-
-/**
- * Holder class to expose a thread-bound {@link ImportStack}, used while detecting circular
- * declarations of the {@link Import} annotation.
- *
- * @author Chris Beams
- * @see Import
- * @see ImportStack
- * @see CircularImportException
- */
-class ImportStackHolder {
-
- private static ThreadLocal stackHolder = new ThreadLocal() {
- @Override
- protected ImportStack initialValue() {
- return new ImportStack();
- }
- };
-
- public static ImportStack getImportStack() {
- return stackHolder.get();
- }
-
- @Override
- public String toString() {
- return "Holder for circular @Import detection stack";
- }
-
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Lazy.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Lazy.java
index c42b4530402..fa17b493978 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Lazy.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Lazy.java
@@ -21,7 +21,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-
+import java.lang.annotation.Inherited;
/**
* Indicates whether a bean is to be lazily initialized.
@@ -51,8 +51,9 @@ import java.lang.annotation.Target;
* @see Configuration
* @see org.springframework.stereotype.Component
*/
-@Target({ ElementType.TYPE, ElementType.METHOD })
+@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
+@Inherited
@Documented
public @interface Lazy {
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ModelClass.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ModelClass.java
deleted file mode 100644
index 36b48427c63..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ModelClass.java
+++ /dev/null
@@ -1,172 +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 org.springframework.util.ClassUtils.*;
-
-import org.springframework.beans.BeanMetadataElement;
-import org.springframework.beans.factory.parsing.Location;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.util.ClassUtils;
-
-
-/**
- * Represents a class, free from java reflection,
- * populated by {@link ConfigurationClassParser}.
- *
- * @author Chris Beams
- * @see ConfigurationModel
- * @see ConfigurationClass
- * @see ConfigurationClassParser
- */
-class ModelClass implements BeanMetadataElement {
-
- private String name;
- private boolean isInterface;
- private transient Object source;
-
- /**
- * Creates a new and empty ModelClass instance.
- */
- public ModelClass() {
- }
-
- /**
- * Creates a new ModelClass instance
- *
- * @param name fully-qualified name of the class being represented
- */
- public ModelClass(String name) {
- this(name, false);
- }
-
- /**
- * Creates a new ModelClass instance
- *
- * @param name fully-qualified name of the class being represented
- * @param isInterface whether the represented type is an interface
- */
- public ModelClass(String name, boolean isInterface) {
- this.name = name;
- this.isInterface = isInterface;
- }
-
- /**
- * 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(convertClassNameToResourcePath(getName())), getSource());
- }
-
- /**
- * Given a ModelClass instance representing a class com.acme.Foo, this method will
- * return
- *
- *
- * ModelClass: name=Foo
- *
- */
- @Override
- public String toString() {
- return String.format("%s: name=%s", getClass().getSimpleName(), getSimpleName());
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = (prime * result) + (isInterface ? 1231 : 1237);
- result = (prime * result) + ((name == null) ? 0 : name.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
-
- if (obj == null)
- return false;
-
- if (getClass() != obj.getClass())
- return false;
-
- ModelClass other = (ModelClass) obj;
- if (isInterface != other.isInterface)
- return false;
-
- if (name == null) {
- if (other.name != null)
- return false;
- } else if (!name.equals(other.name))
- return false;
-
- return true;
- }
-
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationArrayVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationArrayVisitor.java
deleted file mode 100644
index 610988fba0d..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationArrayVisitor.java
+++ /dev/null
@@ -1,77 +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 org.springframework.context.annotation.AsmUtils.*;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-
-import org.springframework.asm.AnnotationVisitor;
-
-
-/**
- * ASM {@link AnnotationVisitor} that visits any annotation array values while populating
- * a new {@link MutableAnnotation} instance.
- *
- * @author Chris Beams
- * @see MutableAnnotation
- * @see AsmUtils#createMutableAnnotation
- */
-class MutableAnnotationArrayVisitor extends AnnotationAdapter {
-
- private final ArrayList values = new ArrayList();
- private final MutableAnnotation mutableAnno;
- private final String attribName;
-
- private final ClassLoader classLoader;
-
- public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName, ClassLoader classLoader) {
- super(AsmUtils.ASM_EMPTY_VISITOR);
-
- this.mutableAnno = mutableAnno;
- this.attribName = attribName;
- this.classLoader = classLoader;
- }
-
- @Override
- public void visit(String na, Object value) {
- values.add(value);
- }
-
- @Override
- public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
- String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc);
- Class extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
-
- if (annoType == null)
- return super.visitAnnotation(na, annoTypeDesc);
-
- Annotation anno = createMutableAnnotation(annoType, classLoader);
- values.add(anno);
- return new MutableAnnotationVisitor(anno, classLoader);
- }
-
- @Override
- public void visitEnd() {
- Class> arrayType = mutableAnno.getAttributeType(attribName);
- Object[] array = (Object[]) Array.newInstance(arrayType.getComponentType(), 0);
- mutableAnno.setAttributeValue(attribName, values.toArray(array));
- }
-
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationInvocationHandler.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationInvocationHandler.java
deleted file mode 100644
index da869b24f40..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationInvocationHandler.java
+++ /dev/null
@@ -1,213 +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 static org.springframework.core.annotation.AnnotationUtils.*;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-
-import org.springframework.util.Assert;
-import org.springframework.util.ReflectionUtils;
-import org.springframework.util.StringUtils;
-
-
-/**
- * Handles calls to {@link MutableAnnotation} attribute methods at runtime. Essentially
- * emulates what JDK annotation dynamic proxies do.
- *
- * @author Chris Beams
- * @see MutableAnnotation
- * @see AsmUtils#createMutableAnnotation
- */
-final class MutableAnnotationInvocationHandler implements InvocationHandler {
-
- private final Class extends Annotation> annoType;
- private final HashMap attributes = new HashMap();
- private final HashMap> attributeTypes = new HashMap>();
-
- 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(), 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 proxy object and other object by comparing the return
- * values of the methods specified by their common {@link Annotation} ancestry.
- *
- * other must be the same type as or a subtype of proxy . Will
- * return false otherwise.
- *
- * Eagerly returns true if {@code proxy} == other
- *
- *
- * Conforms strictly to the equals() specification for Annotation
- *
- *
- * @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 RuntimeException(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() {
- ArrayList attribs = new ArrayList();
-
- 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) {
- Method method = null;
-
- try {
- method = annotationType.getDeclaredMethod(attributeName);
- } catch (Exception ex) {
- ReflectionUtils.handleReflectionException(ex);
- }
-
- return method.getReturnType();
- }
-
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationVisitor.java
deleted file mode 100644
index 6f2160af58e..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/MutableAnnotationVisitor.java
+++ /dev/null
@@ -1,121 +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 org.springframework.context.annotation.AsmUtils.*;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-
-import org.springframework.asm.AnnotationVisitor;
-import org.springframework.asm.Type;
-import org.springframework.util.Assert;
-
-
-/**
- * ASM {@link AnnotationVisitor} that populates a given {@link MutableAnnotation} instance
- * with its attributes.
- *
- * @author Chris Beams
- * @see MutableAnnotation
- * @see MutableAnnotationInvocationHandler
- * @see AsmUtils#createMutableAnnotation
- */
-class MutableAnnotationVisitor implements AnnotationVisitor {
-
- protected final MutableAnnotation mutableAnno;
-
- private final ClassLoader classLoader;
-
- /**
- * Creates a new {@link MutableAnnotationVisitor} instance that will populate the the
- * attributes of the given mutableAnno . Accepts {@link Annotation} instead of
- * {@link MutableAnnotation} to avoid the need for callers to typecast.
- *
- * @param mutableAnno {@link MutableAnnotation} instance to visit and populate
- *
- * @throws IllegalArgumentException if mutableAnno is not of type
- * {@link MutableAnnotation}
- *
- * @see AsmUtils#createMutableAnnotation
- */
- public MutableAnnotationVisitor(Annotation mutableAnno, ClassLoader classLoader) {
- Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable");
- this.mutableAnno = (MutableAnnotation) mutableAnno;
- this.classLoader = classLoader;
- }
-
- public AnnotationVisitor visitArray(final String attribName) {
- return new MutableAnnotationArrayVisitor(mutableAnno, attribName, 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 fqClassName = ((Type) attribValue).getClassName();
-
- Class> classVal = loadToolingSafeClass(fqClassName, classLoader);
-
- if (classVal == null)
- return;
-
- mutableAnno.setAttributeValue(attribName, classVal);
- return;
- }
-
- // otherwise, assume the value can be set literally
- mutableAnno.setAttributeValue(attribName, attribValue);
- }
-
- @SuppressWarnings("unchecked")
- public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
- String enumClassName = convertAsmTypeDescriptorToClassName(enumTypeDescriptor);
-
- Class extends Enum> enumClass = loadToolingSafeClass(enumClassName, classLoader);
-
- if (enumClass == null)
- return;
-
- Enum enumValue = Enum.valueOf(enumClass, strEnumValue);
- mutableAnno.setAttributeValue(attribName, enumValue);
- }
-
- public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) {
- String annoTypeName = convertAsmTypeDescriptorToClassName(attribAnnoTypeDesc);
- Class extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
-
- if (annoType == null)
- return ASM_EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc);
-
- Annotation anno = createMutableAnnotation(annoType, classLoader);
-
- try {
- Field attribute = mutableAnno.getClass().getField(attribName);
- attribute.set(mutableAnno, anno);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
-
- return new MutableAnnotationVisitor(anno, classLoader);
- }
-
- public void visitEnd() {
- }
-
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Primary.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Primary.java
index 2a29b92109a..56307e81f32 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Primary.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Primary.java
@@ -18,11 +18,11 @@ 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;
-
/**
* Indicates that a bean should be given preference when multiple candidates
* are qualified to autowire a single-valued dependency. If exactly one 'primary'
@@ -43,8 +43,9 @@ import java.lang.annotation.Target;
* @see Bean
* @see org.springframework.stereotype.Component
*/
-@Target({ ElementType.TYPE, ElementType.METHOD })
+@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
+@Inherited
@Documented
public @interface Primary {
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
index 558aa461497..9bc14970ce8 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
@@ -59,13 +59,11 @@ public @interface Scope {
/**
* Specifies whether a component should be configured as a scoped proxy
* and if so, whether the proxy should be interface-based or subclass-based.
- *
* Defaults to {@link ScopedProxyMode#NO}, indicating no scoped proxy
* should be created.
- *
*
Analogous to {@literal } support in Spring XML. Valid
* only in conjunction with a non-singleton, non-prototype {@link #value()}.
*/
- ScopedProxyMode proxyMode() default ScopedProxyMode.NO;
+ ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadata.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadata.java
index fac37245247..07b41d1000d 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadata.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadata.java
@@ -17,6 +17,7 @@
package org.springframework.context.annotation;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.util.Assert;
/**
* Describes scope characteristics for a Spring-managed bean including the scope
@@ -26,6 +27,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
* scoped-proxies.
*
* @author Mark Fisher
+ * @author Juergen Hoeller
* @since 2.5
* @see ScopeMetadataResolver
* @see ScopedProxyMode
@@ -41,6 +43,7 @@ public class ScopeMetadata {
* Set the name of the scope.
*/
public void setScopeName(String scopeName) {
+ Assert.notNull(scopeName, "'scopeName' must not be null");
this.scopeName = scopeName;
}
@@ -55,6 +58,7 @@ public class ScopeMetadata {
* Set the proxy-mode to be applied to the scoped instance.
*/
public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
+ Assert.notNull(scopedProxyMode, "'scopedProxyMode' must not be null");
this.scopedProxyMode = scopedProxyMode;
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadataResolver.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadataResolver.java
index d25609e6581..e9cbcb25f7b 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadataResolver.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadataResolver.java
@@ -23,7 +23,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
*
* @author Mark Fisher
* @since 2.5
- * @see Scope
+ * @see org.springframework.context.annotation.Scope
*/
public interface ScopeMetadataResolver {
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopedProxyMode.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopedProxyMode.java
index cc873d75705..178058270d1 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopedProxyMode.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopedProxyMode.java
@@ -29,6 +29,12 @@ package org.springframework.context.annotation;
*/
public enum ScopedProxyMode {
+ /**
+ * Default typically equals {@link #NO}, unless a different default
+ * has been configured at the component-scan instruction level.
+ */
+ DEFAULT,
+
/**
* Do not create a scoped proxy.
*
This proxy-mode is not typically useful when used with a
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/StandardScopes.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/StandardScopes.java
index 9aedff08252..732eb8eb5b8 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/StandardScopes.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/StandardScopes.java
@@ -16,17 +16,16 @@
package org.springframework.context.annotation;
-
/**
* Enumerates the names of the scopes supported out of the box in Spring.
- *
+ *
*
Not modeled as an actual java enum because annotations that accept a scope attribute
* must allow for user-defined scope names. Given that java enums are not extensible, these
* must remain simple string constants.
- *
+ *
* @author Chris Beams
* @since 3.0
- * @see Scope
+ * @see org.springframework.context.annotation.Scope
*/
public class StandardScopes {
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
index 42d875aa6d8..212f7ec31fa 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -37,6 +37,7 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.support.ResourceEditorRegistrar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -487,9 +488,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// Invoke factory processors registered with the context instance.
- for (BeanFactoryPostProcessor factoryProcessor : getBeanFactoryPostProcessors()) {
- factoryProcessor.postProcessBeanFactory(beanFactory);
- }
+ invokeBeanFactoryPostProcessors(getBeanFactoryPostProcessors(), beanFactory);
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
@@ -515,7 +514,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
OrderComparator.sort(priorityOrderedPostProcessors);
- invokeBeanFactoryPostProcessors(beanFactory, priorityOrderedPostProcessors);
+ invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List orderedPostProcessors = new ArrayList();
@@ -523,21 +522,21 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
orderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
OrderComparator.sort(orderedPostProcessors);
- invokeBeanFactoryPostProcessors(beanFactory, orderedPostProcessors);
+ invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List nonOrderedPostProcessors = new ArrayList();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
- invokeBeanFactoryPostProcessors(beanFactory, nonOrderedPostProcessors);
+ invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
}
/**
* Invoke the given BeanFactoryPostProcessor beans.
*/
private void invokeBeanFactoryPostProcessors(
- ConfigurableListableBeanFactory beanFactory, List postProcessors) {
+ List postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
diff --git a/org.springframework.context/src/test/java/example/scannable/AutowiredQualifierFooService.java b/org.springframework.context/src/test/java/example/scannable/AutowiredQualifierFooService.java
index 1fcc0e6d429..2bdea2ecce8 100644
--- a/org.springframework.context/src/test/java/example/scannable/AutowiredQualifierFooService.java
+++ b/org.springframework.context/src/test/java/example/scannable/AutowiredQualifierFooService.java
@@ -21,12 +21,14 @@ import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.AsyncResult;
/**
* @author Mark Fisher
* @author Juergen Hoeller
*/
+@Lazy
public class AutowiredQualifierFooService implements FooService {
@Autowired
diff --git a/org.springframework.context/src/test/java/example/scannable/FooServiceImpl.java b/org.springframework.context/src/test/java/example/scannable/FooServiceImpl.java
index 03884b65c63..d31815f1eb2 100644
--- a/org.springframework.context/src/test/java/example/scannable/FooServiceImpl.java
+++ b/org.springframework.context/src/test/java/example/scannable/FooServiceImpl.java
@@ -27,6 +27,8 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.DependsOn;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
@@ -38,7 +40,7 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @author Juergen Hoeller
*/
-@Service
+@Service @Lazy @DependsOn("myNamedComponent")
public class FooServiceImpl implements FooService {
@Autowired private FooDao fooDao;
diff --git a/org.springframework.context/src/test/java/example/scannable/NamedComponent.java b/org.springframework.context/src/test/java/example/scannable/NamedComponent.java
index 748eef7be8a..1dead3490dd 100644
--- a/org.springframework.context/src/test/java/example/scannable/NamedComponent.java
+++ b/org.springframework.context/src/test/java/example/scannable/NamedComponent.java
@@ -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.
@@ -17,11 +17,12 @@
package example.scannable;
import org.springframework.stereotype.Component;
+import org.springframework.context.annotation.Lazy;
/**
* @author Mark Fisher
*/
-@Component("myNamedComponent")
+@Component("myNamedComponent") @Lazy
public class NamedComponent {
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/AbstractCircularImportDetectionTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/AbstractCircularImportDetectionTests.java
index 8bfe58541c6..1a02ed5297f 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/AbstractCircularImportDetectionTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/AbstractCircularImportDetectionTests.java
@@ -60,7 +60,8 @@ public abstract class AbstractCircularImportDetectionTests {
boolean threw = false;
try {
newParser().parse(loadAsConfigurationSource(X.class), null);
- } catch (BeanDefinitionParsingException ex) {
+ }
+ catch (BeanDefinitionParsingException ex) {
assertTrue("Wrong message. Got: " + ex.getMessage(),
ex.getMessage().contains(
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.Z2' " +
@@ -71,6 +72,7 @@ public abstract class AbstractCircularImportDetectionTests {
assertTrue(threw);
}
+ @Configuration
@Import(B.class)
static class A {
@Bean
@@ -79,6 +81,7 @@ public abstract class AbstractCircularImportDetectionTests {
}
}
+ @Configuration
@Import(A.class)
static class B {
@Bean
@@ -87,6 +90,7 @@ public abstract class AbstractCircularImportDetectionTests {
}
}
+ @Configuration
@Import( { Y.class, Z.class })
class X {
@Bean
@@ -95,6 +99,7 @@ public abstract class AbstractCircularImportDetectionTests {
}
}
+ @Configuration
class Y {
@Bean
TestBean y() {
@@ -102,6 +107,7 @@ public abstract class AbstractCircularImportDetectionTests {
}
}
+ @Configuration
@Import( { Z1.class, Z2.class })
class Z {
@Bean
@@ -110,6 +116,7 @@ public abstract class AbstractCircularImportDetectionTests {
}
}
+ @Configuration
class Z1 {
@Bean
TestBean z1() {
@@ -117,6 +124,7 @@ public abstract class AbstractCircularImportDetectionTests {
}
}
+ @Configuration
@Import(Z.class)
class Z2 {
@Bean
@@ -124,4 +132,5 @@ public abstract class AbstractCircularImportDetectionTests {
return new TestBean();
}
}
+
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java
index d9612acd4ac..4cfe5ab2c47 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java
@@ -1,18 +1,33 @@
+/*
+ * Copyright 2002-2008 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 org.junit.Assert.*;
-
import org.junit.Before;
import org.junit.Test;
+
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
/**
- * Unit tests for the {@link AnnotationScopeMetadataResolver} class.
- *
* @author Rick Evans
* @author Chris Beams
+ * @author Juergen Hoeller
*/
public final class AnnotationScopeMetadataResolverTests {
@@ -34,7 +49,6 @@ public final class AnnotationScopeMetadataResolverTests {
assertEquals(ScopedProxyMode.NO, scopeMetadata.getScopedProxyMode());
}
-
@Test
public void testThatResolveScopeMetadataDoesApplyScopedProxyModeToAPrototype() {
this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(ScopedProxyMode.INTERFACES);
@@ -45,6 +59,16 @@ public final class AnnotationScopeMetadataResolverTests {
assertEquals(ScopedProxyMode.INTERFACES, scopeMetadata.getScopedProxyMode());
}
+ @Test
+ public void testThatResolveScopeMetadataDoesReadScopedProxyModeFromTheAnnotation() {
+ this.scopeMetadataResolver = new AnnotationScopeMetadataResolver();
+ AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnnotatedWithScopedProxy.class);
+ ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd);
+ assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata);
+ assertEquals("request", scopeMetadata.getScopeName());
+ assertEquals(ScopedProxyMode.TARGET_CLASS, scopeMetadata.getScopedProxyMode());
+ }
+
@Test(expected=IllegalArgumentException.class)
public void testCtorWithNullScopedProxyMode() {
new AnnotationScopeMetadataResolver(null);
@@ -65,4 +89,9 @@ public final class AnnotationScopeMetadataResolverTests {
private static final class AnnotatedWithPrototypeScope {
}
+
+ @Scope(value="request", proxyMode = ScopedProxyMode.TARGET_CLASS)
+ private static final class AnnotatedWithScopedProxy {
+ }
+
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/BeanMethodTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/BeanMethodTests.java
index ff8a761bef5..9bb3092fe67 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/BeanMethodTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/BeanMethodTests.java
@@ -13,42 +13,40 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.context.annotation;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-import static org.springframework.context.annotation.AsmUtils.*;
-import static org.springframework.context.annotation.ScopedProxyMode.*;
-import static org.springframework.context.annotation.StandardScopes.*;
+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;
-
/**
- * Unit tests for {@link BeanMethod}.
- *
* @author Chris Beams
*/
public class BeanMethodTests {
private ProblemReporter problemReporter = new FailFastProblemReporter();
private String beanName = "foo";
- private Bean beanAnno = createMutableAnnotation(Bean.class, ClassUtils.getDefaultClassLoader());
- private ModelClass returnType = new ModelClass("FooType");
+ 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() {
- BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno);
+ ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, 0, returnType, beanAnno);
assertThat(beanMethod.getName(), sameInstance(beanName));
assertThat(beanMethod.getModifiers(), equalTo(0));
@@ -84,7 +82,7 @@ public class BeanMethodTests {
@Test
public void finalMethodsAreIllegal() {
- BeanMethod beanMethod = new BeanMethod(beanName, Modifier.FINAL, returnType, beanAnno);
+ ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, Modifier.FINAL, returnType, beanAnno);
beanMethod.setDeclaringClass(declaringClass);
try {
beanMethod.validate(problemReporter);
@@ -96,7 +94,7 @@ public class BeanMethodTests {
@Test
public void privateMethodsAreIllegal() {
- BeanMethod beanMethod = new BeanMethod(beanName, Modifier.PRIVATE, returnType, beanAnno);
+ ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, Modifier.PRIVATE, returnType, beanAnno);
beanMethod.setDeclaringClass(declaringClass);
try {
beanMethod.validate(problemReporter);
@@ -106,57 +104,18 @@ public class BeanMethodTests {
}
}
- @Test
- public void singletonInterfaceScopedProxiesAreIllegal() {
- Scope scope = SingletonInterfaceProxy.class.getAnnotation(Scope.class);
- BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
- beanMethod.setDeclaringClass(declaringClass);
- try {
- beanMethod.validate(problemReporter);
- fail("should have failed due to singleton with scoped proxy");
- } catch (Exception ex) {
- assertTrue(ex.getMessage().contains("cannot be created for singleton/prototype beans"));
- }
- }
-
- @Test
- public void singletonTargetClassScopedProxiesAreIllegal() {
- Scope scope = SingletonTargetClassProxy.class.getAnnotation(Scope.class);
- BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
- beanMethod.setDeclaringClass(declaringClass);
- try {
- beanMethod.validate(problemReporter);
- fail("should have failed due to singleton with scoped proxy");
- } catch (Exception ex) {
- assertTrue(ex.getMessage().contains("cannot be created for singleton/prototype beans"));
- }
- }
-
@Test
public void singletonsSansProxyAreLegal() {
Scope scope = SingletonNoProxy.class.getAnnotation(Scope.class);
- BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
+ 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 prototypeInterfaceScopedProxiesAreIllegal() {
- Scope scope = PrototypeInterfaceProxy.class.getAnnotation(Scope.class);
- BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
- beanMethod.setDeclaringClass(declaringClass);
- try {
- beanMethod.validate(problemReporter);
- fail("should have failed due to prototype with scoped proxy");
- } catch (Exception ex) {
- assertTrue(ex.getMessage().contains("cannot be created for singleton/prototype beans"));
- }
- }
-
@Test
public void sessionInterfaceScopedProxiesAreLegal() {
Scope scope = SessionInterfaceProxy.class.getAnnotation(Scope.class);
- BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
+ ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, 0, returnType, beanAnno, scope);
beanMethod.setDeclaringClass(declaringClass);
beanMethod.validate(problemReporter); // should validate without problems - it's legal
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java
index 03a09777bba..c2f159251f3 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java
@@ -16,10 +16,15 @@
package org.springframework.context.annotation;
-import static org.junit.Assert.*;
-
+import example.scannable.CustomComponent;
+import example.scannable.FooService;
+import example.scannable.FooServiceImpl;
+import example.scannable.NamedStubDao;
+import example.scannable.StubFooDao;
import org.aspectj.lang.annotation.Aspect;
+import static org.junit.Assert.*;
import org.junit.Test;
+
import org.springframework.beans.TestBean;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -34,12 +39,6 @@ import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.stereotype.Component;
-import example.scannable.CustomComponent;
-import example.scannable.FooService;
-import example.scannable.FooServiceImpl;
-import example.scannable.NamedStubDao;
-import example.scannable.StubFooDao;
-
/**
* @author Mark Fisher
* @author Juergen Hoeller
@@ -49,7 +48,7 @@ public class ClassPathBeanDefinitionScannerTests {
private static final String BASE_PACKAGE = "example.scannable";
-
+
@Test
public void testSimpleScanWithDefaultFiltersAndPostProcessors() {
GenericApplicationContext context = new GenericApplicationContext();
@@ -62,9 +61,34 @@ public class ClassPathBeanDefinitionScannerTests {
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean("thoreau"));
+ assertTrue(context.containsBean(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
+ context.refresh();
+ FooServiceImpl service = context.getBean("fooServiceImpl", FooServiceImpl.class);
+ assertTrue(context.getDefaultListableBeanFactory().containsSingleton("myNamedComponent"));
+ assertEquals("bar", service.foo(1));
+ }
+
+ @Test
+ public void testSimpleScanWithDefaultFiltersAndPrimaryLazyBean() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
+ scanner.scan(BASE_PACKAGE);
+ scanner.scan("org.springframework.context.annotation5");
+ assertTrue(context.containsBean("serviceInvocationCounter"));
+ assertTrue(context.containsBean("fooServiceImpl"));
+ assertTrue(context.containsBean("stubFooDao"));
+ assertTrue(context.containsBean("myNamedComponent"));
+ assertTrue(context.containsBean("myNamedDao"));
+ assertTrue(context.containsBean("otherFooDao"));
+ context.refresh();
+ assertFalse(context.getBeanFactory().containsSingleton("otherFooDao"));
+ assertFalse(context.getBeanFactory().containsSingleton("fooServiceImpl"));
+ FooServiceImpl service = context.getBean("fooServiceImpl", FooServiceImpl.class);
+ assertTrue(context.getBeanFactory().containsSingleton("otherFooDao"));
+ assertEquals("other", service.foo(1));
}
@Test
@@ -354,7 +378,7 @@ public class ClassPathBeanDefinitionScannerTests {
assertEquals(10, beanCount);
context.refresh();
- FooServiceImpl fooService = (FooServiceImpl) context.getBean("fooService");
+ FooServiceImpl fooService = context.getBean("fooService", FooServiceImpl.class);
StaticListableBeanFactory myBf = (StaticListableBeanFactory) context.getBean("myBf");
MessageSource ms = (MessageSource) context.getBean("messageSource");
assertTrue(fooService.isInitCalled());
@@ -415,6 +439,7 @@ public class ClassPathBeanDefinitionScannerTests {
scanner.scan(BASE_PACKAGE);
try {
context.refresh();
+ context.getBean("fooService");
fail("BeanCreationException expected; fooDao should not have been an autowire-candidate");
}
catch (BeanCreationException expected) {
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathFactoryBeanDefinitionScannerTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathFactoryBeanDefinitionScannerTests.java
index 10840926c1e..023ef3c258e 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathFactoryBeanDefinitionScannerTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathFactoryBeanDefinitionScannerTests.java
@@ -16,91 +16,75 @@
package org.springframework.context.annotation;
-import java.util.Set;
-
import junit.framework.TestCase;
import org.springframework.aop.scope.ScopedObject;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.TestBean;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.SimpleMapScope;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.beans.factory.support.AutowireCandidateQualifier;
+import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation4.FactoryMethodComponent;
import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.util.ClassUtils;
-
+/**
+ * @author Mark Pollack
+ * @author Juergen Hoeller
+ */
public class ClassPathFactoryBeanDefinitionScannerTests extends TestCase {
private static final String BASE_PACKAGE = FactoryMethodComponent.class.getPackage().getName();
- private static final int NUM_DEFAULT_BEAN_DEFS = 5;
-
- private static final int NUM_FACTORY_METHODS = 5; // @ScopedProxy creates another
-
- private static final int NUM_COMPONENT_DEFS = 1;
-
- public void testSingletonScopedFactoryMethod()
- {
+ public void testSingletonScopedFactoryMethod() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
-
- SimpleMapScope scope = new SimpleMapScope();
- context.getBeanFactory().registerScope("request", scope);
-
- int beanCount = scanner.scan(BASE_PACKAGE);
-
- assertEquals(NUM_FACTORY_METHODS + NUM_COMPONENT_DEFS + NUM_DEFAULT_BEAN_DEFS, beanCount);
- assertTrue(context.containsBean("factoryMethodComponent"));
- assertTrue(context.containsBean("factoryMethodComponent$staticInstance"));
- assertTrue(context.containsBean("factoryMethodComponent$getPublicInstance"));
-
-
+ context.getBeanFactory().registerScope("request", new SimpleMapScope());
- TestBean staticTestBean = (TestBean)context.getBean("factoryMethodComponent$staticInstance");//1
- assertEquals("staticInstance", staticTestBean.getName());
- TestBean staticTestBean2 = (TestBean)context.getBean("factoryMethodComponent$staticInstance");//1
- assertSame(staticTestBean, staticTestBean2);
-
- TestBean tb = (TestBean)context.getBean("factoryMethodComponent$getPublicInstance"); //2
+ scanner.scan(BASE_PACKAGE);
+ context.registerBeanDefinition("clientBean", new RootBeanDefinition(QualifiedClientBean.class));
+ context.refresh();
+
+ FactoryMethodComponent fmc = context.getBean("factoryMethodComponent", FactoryMethodComponent.class);
+ assertFalse(fmc.getClass().getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR));
+
+ TestBean tb = (TestBean)context.getBean("publicInstance"); //2
assertEquals("publicInstance", tb.getName());
- TestBean tb2 = (TestBean)context.getBean("factoryMethodComponent$getPublicInstance"); //2
+ TestBean tb2 = (TestBean)context.getBean("publicInstance"); //2
assertEquals("publicInstance", tb2.getName());
assertSame(tb2, tb);
- //Were qualifiers applied to bean definition
- ConfigurableListableBeanFactory cbf = (ConfigurableListableBeanFactory)context.getAutowireCapableBeanFactory();
- AbstractBeanDefinition abd = (AbstractBeanDefinition)cbf.getBeanDefinition("factoryMethodComponent$getPublicInstance"); //2
- Set qualifierSet = abd.getQualifiers();
- assertEquals(1, qualifierSet.size());
-
-
- tb = (TestBean)context.getBean("factoryMethodComponent$getProtectedInstance"); //3
+ tb = (TestBean)context.getBean("protectedInstance"); //3
assertEquals("protectedInstance", tb.getName());
- tb2 = (TestBean)context.getBean("factoryMethodComponent$getProtectedInstance"); //3
+ assertSame(tb, context.getBean("protectedInstance"));
+ assertEquals("0", tb.getCountry());
+ tb2 = (TestBean)context.getBean("protectedInstance"); //3
assertEquals("protectedInstance", tb2.getName());
assertSame(tb2, tb);
-
- tb = (TestBean)context.getBean("factoryMethodComponent$getPrivateInstance"); //4
+
+ tb = (TestBean)context.getBean("privateInstance"); //4
assertEquals("privateInstance", tb.getName());
- assertEquals(0, tb.getAge());
- tb2 = (TestBean)context.getBean("factoryMethodComponent$getPrivateInstance"); //4
- assertEquals(1, tb2.getAge());
+ assertEquals(1, tb.getAge());
+ tb2 = (TestBean)context.getBean("privateInstance"); //4
+ assertEquals(2, tb2.getAge());
assertNotSame(tb2, tb);
- Object bean = context.getBean("scopedTarget.factoryMethodComponent$requestScopedInstance"); //5
- assertNotNull(bean);
- assertTrue(bean instanceof ScopedObject);
-
- //Scope assertions
+ Object bean = context.getBean("requestScopedInstance"); //5
assertTrue(AopUtils.isCglibProxy(bean));
-
-
-
-
-
+ assertTrue(bean instanceof ScopedObject);
+
+ QualifiedClientBean clientBean = context.getBean("clientBean", QualifiedClientBean.class);
+ assertSame(clientBean.testBean, context.getBean("publicInstance"));
}
+
+
+ public static class QualifiedClientBean {
+
+ @Autowired @Qualifier("public")
+ public TestBean testBean;
+ }
+
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java
index 3363a6bba7c..a62e8b113fa 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java
@@ -13,104 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.context.annotation;
-import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
-import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
-
-import java.lang.reflect.Field;
-import java.util.Vector;
-
-import org.junit.Ignore;
import org.junit.Test;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.ConfigurationClassPostProcessor;
-import org.springframework.util.ClassUtils;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
/**
- * Unit tests for {@link ConfigurationClassPostProcessor}.
- *
* @author Chris Beams
*/
public class ConfigurationClassPostProcessorTests {
- private static final String ORIG_CGLIB_TEST_CLASS = ConfigurationClassPostProcessor.CGLIB_TEST_CLASS;
- private static final String BOGUS_CGLIB_TEST_CLASS = "a.bogus.class";
-
- /**
- * CGLIB is an optional dependency for Spring. If users attempt
- * to use {@link Configuration} classes, they'll need it on the classpath;
- * if Configuration classes are present in the bean factory and CGLIB
- * is not present, an instructive exception should be thrown.
- */
- @Test
- public void testFailFastIfCglibNotPresent() {
- @Configuration class Config { }
-
- DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
- factory.registerBeanDefinition("config", rootBeanDefinition(Config.class).getBeanDefinition());
- ConfigurationClassPostProcessor cpp = new ConfigurationClassPostProcessor();
-
- // temporarily set the cglib test class to something bogus
- ConfigurationClassPostProcessor.CGLIB_TEST_CLASS = BOGUS_CGLIB_TEST_CLASS;
-
- try {
- cpp.postProcessBeanFactory(factory);
- } catch (RuntimeException ex) {
- assertTrue(ex.getMessage().contains("CGLIB is required to process @Configuration classes"));
- } finally {
- ConfigurationClassPostProcessor.CGLIB_TEST_CLASS = ORIG_CGLIB_TEST_CLASS;
- }
- }
-
- /**
- * In order to keep Spring's footprint as small as possible, CGLIB must
- * not be required on the classpath unless the user is taking advantage
- * of {@link Configuration} classes.
- *
- * This test will fail if any CGLIB classes are classloaded before the call
- * to {@link ConfigurationClassPostProcessor#enhanceConfigurationClasses}
- */
- @Ignore @Test // because classloader hacking below causes extremely hard to
- // debug downstream side effects. Re-enable at will to verify
- // CGLIB is not prematurely classloaded, but this technique is
- // not stable enough to leave enabled.
- public void testCglibClassesAreLoadedJustInTimeForEnhancement() throws Exception {
- ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
- Field classesField = ClassLoader.class.getDeclaredField("classes");
- classesField.setAccessible(true);
-
- // first, remove any CGLIB classes that may have been loaded by other tests
- @SuppressWarnings("unchecked")
- Vector> classes = (Vector>) classesField.get(classLoader);
-
- Vector> cglibClassesAlreadyLoaded = new Vector>();
- for(Class> loadedClass : classes)
- if(loadedClass.getName().startsWith("net.sf.cglib"))
- cglibClassesAlreadyLoaded.add(loadedClass);
-
- for(Class> cglibClass : cglibClassesAlreadyLoaded)
- classes.remove(cglibClass);
-
- // now, execute a scenario where everything except enhancement occurs
- // -- no CGLIB classes should get loaded!
- testFailFastIfCglibNotPresent();
-
- // test to ensure that indeed no CGLIB classes have been loaded
- for(Class> loadedClass : classes)
- if(loadedClass.getName().startsWith("net.sf.cglib"))
- fail("CGLIB class should not have been eagerly loaded: " + loadedClass.getName());
- }
-
/**
* Enhanced {@link Configuration} classes are only necessary for respecting
* certain bean semantics, like singleton-scoping, scoped proxies, etc.
- *
- * Technically, {@link ConfigurationClassPostProcessor} could fail to enhance the
+ * Technically, {@link ConfigurationClassPostProcessor} could fail to enhance the
* registered Configuration classes and many use cases would still work.
* Certain cases, however, like inter-bean singleton references would not.
* We test for such a case below, and in doing so prove that enhancement is
@@ -118,17 +38,35 @@ public class ConfigurationClassPostProcessorTests {
*/
@Test
public void testEnhancementIsPresentBecauseSingletonSemanticsAreRespected() {
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- beanFactory.registerBeanDefinition("config",
- rootBeanDefinition(SingletonBeanConfig.class).getBeanDefinition());
- new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(beanFactory);
Foo foo = (Foo) beanFactory.getBean("foo");
Bar bar = (Bar) beanFactory.getBean("bar");
- assertThat(foo, sameInstance(bar.foo));
+ assertSame(foo, bar.foo);
}
+ /**
+ * Tests the fix for SPR-5655, a special workaround that prefers reflection
+ * over ASM if a bean class is already loaded.
+ */
+ @Test
+ public void testAlreadyLoadedConfigurationClasses() {
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ beanFactory.registerBeanDefinition("unloadedConfig",
+ new RootBeanDefinition(UnloadedConfig.class.getName(), null, null));
+ beanFactory.registerBeanDefinition("loadedConfig", new RootBeanDefinition(LoadedConfig.class));
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(beanFactory);
+ beanFactory.getBean("foo");
+ beanFactory.getBean("bar");
+ }
+
+
@Configuration
static class SingletonBeanConfig {
+
public @Bean Foo foo() {
return new Foo();
}
@@ -138,27 +76,16 @@ public class ConfigurationClassPostProcessorTests {
}
}
- static class Foo { }
+
+ static class Foo {
+ }
+
+
static class Bar {
final Foo foo;
public Bar(Foo foo) { this.foo = foo; }
}
- /**
- * Tests the fix for SPR-5655, a special workaround that prefers reflection
- * over ASM if a bean class is already loaded.
- */
- @Test
- public void testAlreadyLoadedConfigurationClasses() {
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- beanFactory.registerBeanDefinition("unloadedConfig",
- rootBeanDefinition(UnloadedConfig.class.getName()).getBeanDefinition());
- beanFactory.registerBeanDefinition("loadedConfig",
- rootBeanDefinition(LoadedConfig.class).getBeanDefinition());
- new ConfigurationClassPostProcessor() .postProcessBeanFactory(beanFactory);
- beanFactory.getBean("foo");
- beanFactory.getBean("bar");
- }
@Configuration
static class UnloadedConfig {
@@ -167,10 +94,12 @@ public class ConfigurationClassPostProcessorTests {
}
}
+
@Configuration
static class LoadedConfig {
public @Bean Bar bar() {
return new Bar(new Foo());
}
}
+
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationModelTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationModelTests.java
deleted file mode 100644
index 8b2867b48ae..00000000000
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationModelTests.java
+++ /dev/null
@@ -1,48 +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 static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-
-import org.junit.Test;
-import org.springframework.context.annotation.ConfigurationClass;
-import org.springframework.context.annotation.ConfigurationModel;
-
-
-/**
- * Unit tests for {@link ConfigurationModel}.
- *
- * @author Chris Beams
- */
-public class ConfigurationModelTests {
-
- @Test
- public void testToString() {
- ConfigurationModel model = new ConfigurationModel();
- assertThat(model.toString(), equalTo(
- "ConfigurationModel containing @Configuration classes: []"));
-
- ConfigurationClass config1 = new ConfigurationClass();
- config1.setName("test.Config1");
- model.add(config1);
-
- assertThat(model.toString(), equalTo(format(
- "ConfigurationModel containing @Configuration classes: [%s]", config1)));
- }
-
-}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/InvalidConfigurationClassDefinitionTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/InvalidConfigurationClassDefinitionTests.java
index 4a42783bc79..8e3e909b273 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/InvalidConfigurationClassDefinitionTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/InvalidConfigurationClassDefinitionTests.java
@@ -45,9 +45,11 @@ public class InvalidConfigurationClassDefinitionTests {
beanFactory.registerBeanDefinition("config", configBeanDef);
try {
- new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(beanFactory);
fail("expected exception");
- } catch (BeanDefinitionParsingException ex) {
+ }
+ catch (BeanDefinitionParsingException ex) {
assertTrue(ex.getMessage(), ex.getMessage().contains("Remove the final modifier"));
}
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/AbstractBeanDefinitionConfigurationClassTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/AbstractBeanDefinitionConfigurationClassTests.java
deleted file mode 100644
index 675d0b3de2c..00000000000
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/AbstractBeanDefinitionConfigurationClassTests.java
+++ /dev/null
@@ -1,63 +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.configuration;
-
-import static org.junit.Assert.*;
-import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
-
-import org.junit.Test;
-import org.springframework.aop.support.AopUtils;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.ConfigurationClassPostProcessor;
-
-
-/**
- * Covers the somewhat unlilely case of a {@link Configuration} class being declared
- * as an abstract {@link BeanDefinition}.
- *
- * @author Chris Beams
- * @see BeanDefinition#isAbstract()
- */
-public class AbstractBeanDefinitionConfigurationClassTests {
-
- @SuppressWarnings("unused")
- @Test
- public void abstractConfigurationClassBeanDefinitionsAreIgnored() {
- @Configuration class Abstract { @Bean Object foo1() { return null; } }
- @Configuration class Concrete { @Bean Object foo2() { return null; } }
-
- DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
- factory.registerBeanDefinition("abstract",
- rootBeanDefinition(Abstract.class).setAbstract(true).getBeanDefinition());
- factory.registerBeanDefinition("concrete",
- rootBeanDefinition(Concrete.class).setAbstract(false).getBeanDefinition());
- new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
-
- assertTrue("abstract configuration should be CGLIB-enhanced",
- AopUtils.isCglibProxyClassName(factory.getBeanDefinition("abstract").getBeanClassName()));
- assertTrue("concrete configuration should be CGLIB-enhanced",
- AopUtils.isCglibProxyClassName(factory.getBeanDefinition("concrete").getBeanClassName()));
-
- assertFalse("abstract configuration's @Bean method should not be registered",
- factory.containsBeanDefinition("foo1"));
- assertTrue("concrete configuration's @Bean method should be registered",
- factory.containsBeanDefinition("foo2"));
- }
-}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/BeanAnnotationAttributePropagationTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/BeanAnnotationAttributePropagationTests.java
index 9f772c91eb5..1eddf67a4f8 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/BeanAnnotationAttributePropagationTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/BeanAnnotationAttributePropagationTests.java
@@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
-
+import org.springframework.context.annotation.DependsOn;
/**
* Unit tests proving that the various attributes available via the {@link Bean}
@@ -67,7 +67,7 @@ public class BeanAnnotationAttributePropagationTests {
@Test
public void dependsOnMetadataIsPropagated() {
@Configuration class Config {
- @Bean(dependsOn={"bar", "baz"}) Object foo() { return null; }
+ @Bean() @DependsOn({"bar", "baz"}) Object foo() { return null; }
}
assertArrayEquals("dependsOn metadata was not propagated",
@@ -149,8 +149,8 @@ public class BeanAnnotationAttributePropagationTests {
private AbstractBeanDefinition beanDef(Class> configClass) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition("config", new RootBeanDefinition(configClass));
- new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
-
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(factory);
return (AbstractBeanDefinition) factory.getBeanDefinition("foo");
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java
index 171366e1fae..cd8f40f00ab 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java
@@ -16,27 +16,24 @@
package org.springframework.context.annotation.configuration;
-import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
-import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
-
import org.junit.Test;
+import test.beans.ITestBean;
+import test.beans.TestBean;
+
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.StandardScopes;
-import test.beans.ITestBean;
-import test.beans.TestBean;
-
-
/**
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
* handling within {@link Configuration} class definitions.
@@ -51,27 +48,20 @@ public class ConfigurationClassProcessingTests {
* post-processes the factory using JavaConfig's {@link ConfigurationClassPostProcessor}.
* When complete, the factory is ready to service requests for any {@link Bean} methods
* declared by configClasses .
- *
- * @param configClasses the {@link Configuration} classes under test. may be an empty
- * list.
- *
- * @return fully initialized and post-processed {@link BeanFactory}
*/
- private static BeanFactory initBeanFactory(Class>... configClasses) {
+ private BeanFactory initBeanFactory(Class>... configClasses) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
-
for (Class> configClass : configClasses) {
String configBeanName = configClass.getName();
- factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass).getBeanDefinition());
+ factory.registerBeanDefinition(configBeanName, new RootBeanDefinition(configClass));
}
-
- new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
-
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(factory);
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
-
return factory;
}
+
@Test
public void customBeanNameIsRespected() {
BeanFactory factory = initBeanFactory(ConfigWithBeanWithCustomName.class);
@@ -132,20 +122,10 @@ public class ConfigurationClassProcessingTests {
@Test
public void simplestPossibleConfiguration() {
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
-
String stringBean = factory.getBean("stringBean", String.class);
-
- assertThat(stringBean, equalTo("foo"));
+ assertEquals(stringBean, "foo");
}
- @Configuration
- static class SimplestPossibleConfig {
- public @Bean String stringBean() {
- return "foo";
- }
- }
-
-
@Test
public void configurationWithPrototypeScopedBeans() {
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
@@ -154,12 +134,22 @@ public class ConfigurationClassProcessingTests {
ITestBean bar = factory.getBean("bar", ITestBean.class);
ITestBean baz = factory.getBean("baz", ITestBean.class);
- assertThat(foo.getSpouse(), sameInstance(bar));
- assertThat(bar.getSpouse(), not(sameInstance(baz)));
+ assertSame(foo.getSpouse(), bar);
+ assertNotSame(bar.getSpouse(), baz);
+ }
+
+
+ @Configuration
+ static class SimplestPossibleConfig {
+ public @Bean String stringBean() {
+ return "foo";
+ }
}
+
@Configuration
static class ConfigWithPrototypeBean {
+
public @Bean TestBean foo() {
TestBean foo = new TestBean("foo");
foo.setSpouse(bar());
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java
index 78fe0f3ee92..01e4bf859ad 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java
@@ -41,9 +41,11 @@ public class ImportTests {
private DefaultListableBeanFactory processConfigurationClasses(Class>... classes) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- for (Class> clazz : classes)
+ for (Class> clazz : classes) {
beanFactory.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
- new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+ }
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(beanFactory);
return beanFactory;
}
@@ -137,7 +139,8 @@ public class ImportTests {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(
WithMultipleArgumentsThatWillCauseDuplication.class));
- new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(beanFactory);
assertThat(beanFactory.getBeanDefinitionCount(), equalTo(4));
assertThat(beanFactory.getBean("foo", ITestBean.class).getName(), equalTo("foo2"));
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/PolymorphicConfigurationTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/PolymorphicConfigurationTests.java
index e6a7f996e82..2fefd1bb8eb 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/PolymorphicConfigurationTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/PolymorphicConfigurationTests.java
@@ -40,18 +40,23 @@ public class PolymorphicConfigurationTests {
public void subclassNeedNotDeclareConfigurationAnnotation() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
- new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
-
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(beanFactory);
beanFactory.getBean("testBean", TestBean.class);
}
+
@Configuration
static class SuperConfig {
+
@Bean
public TestBean testBean() {
return new TestBean();
}
}
- static class Config extends SuperConfig { }
+
+ static class Config extends SuperConfig {
+ }
+
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java
index 28c5cc0b4ad..0fb107db0b5 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java
@@ -13,21 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.context.annotation.configuration;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
+package org.springframework.context.annotation.configuration;
import java.util.HashMap;
import java.util.Map;
+import static org.hamcrest.CoreMatchers.*;
import org.junit.After;
+import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
+import test.beans.ITestBean;
+import test.beans.TestBean;
+
import org.springframework.aop.scope.ScopedObject;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
+import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -37,17 +40,12 @@ import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.context.annotation.StandardScopes;
import org.springframework.context.support.GenericApplicationContext;
-import test.beans.ITestBean;
-import test.beans.TestBean;
-
-
-
/**
* Tests that scopes are properly supported by using a custom Scope implementations
* and scoped proxy {@link Bean} declarations.
*
- * @author Costin Leau
- * @author Chris Beams
+ * @author Costin Leau
+ * @author Chris Beams
*/
public class ScopingTests {
@@ -82,19 +80,16 @@ public class ScopingTests {
return ctx;
}
-
@Test
public void testScopeOnClasses() throws Exception {
genericTestScope("scopedClass");
}
-
@Test
public void testScopeOnInterfaces() throws Exception {
genericTestScope("scopedInterface");
}
-
@Test
public void testSameScopeOnDifferentBeans() throws Exception {
Object beanAInScope = ctx.getBean("scopedClass");
@@ -112,22 +107,8 @@ public class ScopingTests {
assertNotSame(newBeanBInScope, beanBInScope);
}
-
- @Test
- public void testScopedProxyOnSingletonBeanMethod() throws Exception {
- // should throw - scoped proxies should not be applied on singleton/prototype beans
- try {
- createContext(null, InvalidProxyOnPredefinedScopesConfiguration.class);
- fail("exception expected");
- } catch (BeanDefinitionParsingException ex) {
- assertTrue(ex.getMessage().contains("scoped proxies cannot be created for singleton/prototype beans"));
- }
- }
-
-
@Test
public void testRawScopes() throws Exception {
-
String beanName = "scopedProxyInterface";
// get hidden bean
@@ -368,71 +349,56 @@ public class ScopingTests {
}
}
-}
-
-
-/**
- * Simple scope implementation which creates object based on a flag.
- *
- * @author Costin Leau
- * @author Chris Beams
- */
-class CustomScope implements org.springframework.beans.factory.config.Scope {
- public boolean createNewScope = true;
-
- private Map beans = new HashMap();
-
- /*
- * (non-Javadoc)
- * @see org.springframework.beans.factory.config.Scope#get(java.lang.String,
- * org.springframework.beans.factory.ObjectFactory)
+ /**
+ * Simple scope implementation which creates object based on a flag.
+ * @author Costin Leau
+ * @author Chris Beams
*/
- public Object get(String name, ObjectFactory> objectFactory) {
- if (createNewScope) {
- beans.clear();
- // reset the flag back
- createNewScope = false;
+ static class CustomScope implements org.springframework.beans.factory.config.Scope {
+
+ public boolean createNewScope = true;
+
+ private Map beans = new HashMap();
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#get(java.lang.String,
+ * org.springframework.beans.factory.ObjectFactory)
+ */
+ public Object get(String name, ObjectFactory> objectFactory) {
+ if (createNewScope) {
+ beans.clear();
+ // reset the flag back
+ createNewScope = false;
+ }
+
+ Object bean = beans.get(name);
+ // if a new object is requested or none exists under the current
+ // name, create one
+ if (bean == null) {
+ beans.put(name, objectFactory.getObject());
+ }
+
+ return beans.get(name);
}
- Object bean = beans.get(name);
- // if a new object is requested or none exists under the current
- // name, create one
- if (bean == null) {
- beans.put(name, objectFactory.getObject());
+ public String getConversationId() {
+ return null;
}
- return beans.get(name);
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.beans.factory.config.Scope#getConversationId()
- */
- public String getConversationId() {
- return null;
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String,
- * java.lang.Runnable)
- */
- public void registerDestructionCallback(String name, Runnable callback) {
- // do nothing
- }
+ public void registerDestructionCallback(String name, Runnable callback) {
+ // do nothing
+ }
- /*
- * (non-Javadoc)
- * @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
- */
- public Object remove(String name) {
- return beans.remove(name);
- }
+ public Object remove(String name) {
+ return beans.remove(name);
+ }
- public Object resolveContextualObject(String key) {
- // TODO Auto-generated method stub
- return null;
+ public Object resolveContextualObject(String key) {
+ // TODO Auto-generated method stub
+ return null;
+ }
}
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/customNameGeneratorTests.xml b/org.springframework.context/src/test/java/org/springframework/context/annotation/customNameGeneratorTests.xml
index 08d511cdac7..1451dae93d2 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/customNameGeneratorTests.xml
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/customNameGeneratorTests.xml
@@ -5,7 +5,7 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
-
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation4/FactoryMethodComponent.java b/org.springframework.context/src/test/java/org/springframework/context/annotation4/FactoryMethodComponent.java
index bc29fa2b559..a5be7247426 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation4/FactoryMethodComponent.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation4/FactoryMethodComponent.java
@@ -17,63 +17,52 @@
package org.springframework.context.annotation4;
import org.springframework.beans.TestBean;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.FactoryMethod;
import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.annotation.ScopedProxy;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.BeanAge;
+import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
/**
- * Class used to test the functionality of @FactoryMethod bean definitions declared inside
- * a Spring @Component class.
- *
+ * Class used to test the functionality of factory method bean definitions
+ * declared inside a Spring component class.
+ *
* @author Mark Pollack
+ * @author Juergen Hoeller
*/
@Component
-public class FactoryMethodComponent {
+public final class FactoryMethodComponent {
- private static TestBean staticTestBean = new TestBean("staticInstance",1);
-
- @Autowired @Qualifier("public")
- public TestBean autowiredTestBean;
-
- private static int i;
+ private int i;
- @FactoryMethod @Qualifier("static")
- public static TestBean staticInstance()
- {
- return staticTestBean;
- }
-
- public static TestBean nullInstance()
- {
+ public static TestBean nullInstance() {
return null;
}
-
- @FactoryMethod @Qualifier("public")
- public TestBean getPublicInstance() {
+
+ @Bean @Qualifier("public")
+ public TestBean publicInstance() {
return new TestBean("publicInstance");
}
- @FactoryMethod @BeanAge(1)
- protected TestBean getProtectedInstance() {
- return new TestBean("protectedInstance", 1);
+ @Bean @BeanAge(1)
+ protected TestBean protectedInstance(@Qualifier("public") TestBean spouse, @Value("#{privateInstance.age}") String country) {
+ TestBean tb = new TestBean("protectedInstance", 1);
+ tb.setSpouse(tb);
+ tb.setCountry(country);
+ return tb;
}
-
- @FactoryMethod @Scope("prototype")
- private TestBean getPrivateInstance() {
+
+ @Bean @Scope("prototype")
+ private TestBean privateInstance() {
return new TestBean("privateInstance", i++);
}
-
- @FactoryMethod @Scope("request") @ScopedProxy
- public TestBean requestScopedInstance()
- {
- TestBean testBean = new TestBean("requestScopedInstance", 3);
- return testBean;
+
+ @Bean @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
+ public TestBean requestScopedInstance() {
+ return new TestBean("requestScopedInstance", 3);
}
-
- //TODO method for test that fails if use @ScopedProxy with singleton scope.
-
+
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation4/SimpleBean.java b/org.springframework.context/src/test/java/org/springframework/context/annotation4/SimpleBean.java
index ddd52aafbf5..e6920157d4a 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation4/SimpleBean.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation4/SimpleBean.java
@@ -14,24 +14,23 @@
* limitations under the License.
*/
-
package org.springframework.context.annotation4;
import org.springframework.beans.TestBean;
-import org.springframework.beans.factory.annotation.FactoryMethod;
+import org.springframework.context.annotation.Bean;
/**
* Class to test that @FactoryMethods are detected only when inside a class with an @Component
* class annotation.
- *
+ *
* @author Mark Pollack
*/
public class SimpleBean {
-
- // This should *not* recognized as a @FactoryMethod since it does not reside inside an @Component
- @FactoryMethod
+ // This should *not* recognized as a bean since it does not reside inside an @Component
+ @Bean
public TestBean getPublicInstance() {
return new TestBean("publicInstance");
}
+
}
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/FactoryMethod.java b/org.springframework.context/src/test/java/org/springframework/context/annotation5/OtherFooDao.java
similarity index 51%
rename from org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/FactoryMethod.java
rename to org.springframework.context/src/test/java/org/springframework/context/annotation5/OtherFooDao.java
index bf37f999a51..c9972ff9d36 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/FactoryMethod.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation5/OtherFooDao.java
@@ -1,36 +1,36 @@
-/*
- * 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.beans.factory.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Marks a method as being a factory-method of the class. Use during component scanning
- * to create a bean definition that has factory-bean and factory-method metadata
- *
- * @author Mark Pollack
- * @since 3.0
- * @see RequiredAnnotationBeanPostProcessor
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD})
-public @interface FactoryMethod {
-
-}
+/*
+ * 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.annotation5;
+
+import example.scannable.FooDao;
+
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Juergen Hoeller
+ */
+@Repository
+@Primary @Lazy
+public class OtherFooDao implements FooDao {
+
+ public String findFoo(int id) {
+ return "other";
+ }
+
+}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java
index 69ccfc2f958..e202ebd7538 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java
@@ -209,7 +209,7 @@ public class ApplicationContextExpressionTests {
@Value("${code} #{systemProperties.country}")
public String country;
- @Qualifier("original")
+ @Autowired @Qualifier("original")
public TestBean tb;
}
@@ -287,7 +287,7 @@ public class ApplicationContextExpressionTests {
this.country = country;
}
- @Qualifier("original")
+ @Autowired @Qualifier("original")
public void setTb(TestBean tb) {
this.tb = tb;
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
index 9333f657003..eaec9f41447 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
@@ -138,7 +138,7 @@ public abstract class AnnotationUtils {
}
}
Class> superClass = clazz.getSuperclass();
- if (superClass == null || superClass == Object.class) {
+ if (superClass == null || superClass.equals(Object.class)) {
return null;
}
return findAnnotation(superClass, annotationType);
diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/AnnotationMetadata.java b/org.springframework.core/src/main/java/org/springframework/core/type/AnnotationMetadata.java
index d02062f28a9..4db70031c35 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/type/AnnotationMetadata.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/type/AnnotationMetadata.java
@@ -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.
@@ -70,8 +70,8 @@ public interface AnnotationMetadata extends ClassMetadata {
* annotation is defined.
*/
Map getAnnotationAttributes(String annotationType);
-
-
+
+
// TODO return null would be more consistent with other methods if no match is found
/**
diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/MethodMetadata.java b/org.springframework.core/src/main/java/org/springframework/core/type/MethodMetadata.java
index 3a1e85e9909..58c89913086 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/type/MethodMetadata.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/type/MethodMetadata.java
@@ -1,8 +1,28 @@
+/*
+ * 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;
import java.util.Map;
import java.util.Set;
+/**
+ * @author Mark Pollack
+ * @since 3.0
+ */
public interface MethodMetadata {
int getModifiers();
diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java b/org.springframework.core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java
index 0485a191586..043653b8b4c 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java
@@ -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.
@@ -43,16 +43,16 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
public Set getAnnotationTypes() {
Set types = new HashSet();
Annotation[] anns = getIntrospectedClass().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- types.add(anns[i].annotationType().getName());
+ for (Annotation ann : anns) {
+ types.add(ann.annotationType().getName());
}
return types;
}
public boolean hasAnnotation(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- if (anns[i].annotationType().getName().equals(annotationType)) {
+ for (Annotation ann : anns) {
+ if (ann.annotationType().getName().equals(annotationType)) {
return true;
}
}
@@ -61,10 +61,10 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
public Set getMetaAnnotationTypes(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- if (anns[i].annotationType().getName().equals(annotationType)) {
+ for (Annotation ann : anns) {
+ if (ann.annotationType().getName().equals(annotationType)) {
Set types = new HashSet();
- Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
+ Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation meta : metaAnns) {
types.add(meta.annotationType().getName());
}
@@ -76,8 +76,8 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
public boolean hasMetaAnnotation(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
+ for (Annotation ann : anns) {
+ Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation meta : metaAnns) {
if (meta.annotationType().getName().equals(annotationType)) {
return true;
@@ -89,8 +89,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
public Map getAnnotationAttributes(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- Annotation ann = anns[i];
+ for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(ann);
}
@@ -101,11 +100,9 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
public Set getAnnotatedMethods(String annotationType) {
Method[] methods = getIntrospectedClass().getMethods();
Set annotatedMethods = new LinkedHashSet();
- for (int i = 0; i < methods.length; i++) {
- Method method = methods[i];
+ for (Method method : methods) {
Annotation[] methodAnnotations = method.getAnnotations();
- for (int j = 0; j < methodAnnotations.length; j++) {
- Annotation ann = methodAnnotations[j];
+ for (Annotation ann : methodAnnotations) {
if (ann.annotationType().getName().equals(annotationType)) {
MethodMetadata mm = new StandardMethodMetadata(method);
annotatedMethods.add(mm);
diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java b/org.springframework.core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java
index 50af7421004..4a005effd46 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java
@@ -1,3 +1,19 @@
+/*
+ * 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;
import java.lang.annotation.Annotation;
@@ -9,12 +25,18 @@ import java.util.Map;
import java.util.Set;
import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.Assert;
+/**
+ * @author Mark Pollack
+ * @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;
}
@@ -25,8 +47,7 @@ public class StandardMethodMetadata implements MethodMetadata {
public Map getAnnotationAttributes(String annotationType) {
Annotation[] anns = getIntrospectedMethod().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- Annotation ann = anns[i];
+ for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(ann);
}
@@ -37,14 +58,11 @@ public class StandardMethodMetadata implements MethodMetadata {
public Set getAnnotationTypes() {
Set types = new HashSet();
Annotation[] anns = getIntrospectedMethod().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- types.add(anns[i].annotationType().getName());
+ for (Annotation ann : anns) {
+ types.add(ann.annotationType().getName());
}
return types;
}
-
-
-
public String getMethodName() {
return introspectedMethod.getName();
@@ -56,8 +74,8 @@ public class StandardMethodMetadata implements MethodMetadata {
public boolean hasAnnotation(String annotationType) {
Annotation[] anns = getIntrospectedMethod().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- if (anns[i].annotationType().getName().equals(annotationType)) {
+ for (Annotation ann : anns) {
+ if (ann.annotationType().getName().equals(annotationType)) {
return true;
}
}
@@ -70,10 +88,10 @@ public class StandardMethodMetadata implements MethodMetadata {
public Set getMetaAnnotationTypes(String annotationType) {
Annotation[] anns = getIntrospectedMethod().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- if (anns[i].annotationType().getName().equals(annotationType)) {
+ for (Annotation ann : anns) {
+ if (ann.annotationType().getName().equals(annotationType)) {
Set types = new HashSet();
- Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
+ Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation meta : metaAnns) {
types.add(meta.annotationType().getName());
}
@@ -83,13 +101,11 @@ public class StandardMethodMetadata implements MethodMetadata {
return null;
}
-
public boolean hasMetaAnnotation(String metaAnnotationType) {
-
//TODO can refactor into shared (utility) method with StandardAnnotationMetadata
Annotation[] anns = getIntrospectedMethod().getAnnotations();
- for (int i = 0; i < anns.length; i++) {
- Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
+ for (Annotation ann : anns) {
+ Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation meta : metaAnns) {
if (meta.annotationType().getName().equals(metaAnnotationType)) {
return true;
@@ -99,8 +115,7 @@ public class StandardMethodMetadata implements MethodMetadata {
return false;
}
- public Set getAnnotationTypesWithMetaAnnotation(
- String qualifierClassName) {
+ public Set getAnnotationTypesWithMetaAnnotation(String qualifierClassName) {
// TODO Auto-generated method stub
return null;
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java
index 9f643ad2256..30addb57175 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java
@@ -17,6 +17,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;
@@ -32,6 +33,7 @@ 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;
/**
@@ -59,23 +61,18 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
}
-
@Override
- public MethodVisitor visitMethod(int access, String name, String desc,
- String signature, String[] exceptions) {
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodMetadataReadingVisitor md = new MethodMetadataReadingVisitor(classLoader, name, access);
methodMetadataSet.add(md);
return md;
}
-
-
@Override
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
final String className = Type.getType(desc).getClassName();
final Map attributes = new LinkedHashMap();
- return new EmptyVisitor() {
- @Override
+ return new AnnotationVisitor() {
public void visit(String name, Object value) {
// Explicitly defined annotation attribute value.
Object valueToUse = value;
@@ -89,7 +86,6 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
}
attributes.put(name, valueToUse);
}
- @Override
public void visitEnum(String name, String desc, String value) {
Object valueToUse = value;
try {
@@ -104,7 +100,36 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
}
attributes.put(name, valueToUse);
}
- @Override
+ 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);
diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/ClassMetadataReadingVisitor.java b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/ClassMetadataReadingVisitor.java
index f10786d4c4c..3766215a1a3 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/ClassMetadataReadingVisitor.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/ClassMetadataReadingVisitor.java
@@ -16,7 +16,11 @@
package org.springframework.core.type.classreading;
-import org.springframework.asm.ClassAdapter;
+import org.springframework.asm.AnnotationVisitor;
+import org.springframework.asm.Attribute;
+import org.springframework.asm.ClassVisitor;
+import org.springframework.asm.FieldVisitor;
+import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.commons.EmptyVisitor;
import org.springframework.core.type.ClassMetadata;
@@ -33,7 +37,7 @@ import org.springframework.util.ClassUtils;
* @author Ramnivas Laddad
* @since 2.5
*/
-class ClassMetadataReadingVisitor extends ClassAdapter implements ClassMetadata {
+class ClassMetadataReadingVisitor implements ClassVisitor, ClassMetadata {
private String className;
@@ -49,12 +53,7 @@ class ClassMetadataReadingVisitor extends ClassAdapter implements ClassMetadata
private String[] interfaces;
- public ClassMetadataReadingVisitor()
- {
- super(new EmptyVisitor());
- }
-
- @Override
+
public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) {
this.className = ClassUtils.convertResourcePathToClassName(name);
this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
@@ -68,12 +67,10 @@ class ClassMetadataReadingVisitor extends ClassAdapter implements ClassMetadata
}
}
- @Override
public void visitOuterClass(String owner, String name, String desc) {
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(owner);
}
- @Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (outerName != null && this.className.equals(ClassUtils.convertResourcePathToClassName(name))) {
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(outerName);
@@ -81,6 +78,33 @@ class ClassMetadataReadingVisitor extends ClassAdapter implements ClassMetadata
}
}
+ public void visitSource(String source, String debug) {
+ // no-op
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ // no-op
+ return new EmptyVisitor();
+ }
+
+ public void visitAttribute(Attribute attr) {
+ // no-op
+ }
+
+ public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+ // no-op
+ return new EmptyVisitor();
+ }
+
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ // no-op
+ return new EmptyVisitor();
+ }
+
+ public void visitEnd() {
+ // no-op
+ }
+
public String getClassName() {
return this.className;
diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java
index 050c9dc8b2c..a3f38e9c6b7 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java
@@ -17,10 +17,10 @@
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.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -32,18 +32,27 @@ 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;
-public class MethodMetadataReadingVisitor extends MethodAdapter implements MethodMetadata {
-
- private final Map> attributesMap = new LinkedHashMap>();
-
- private final Map> metaAnnotationMap = new LinkedHashMap>();
+/**
+ * @author Mark Pollack
+ * @since 3.0
+ */
+class MethodMetadataReadingVisitor extends MethodAdapter implements MethodMetadata {
private ClassLoader classLoader;
+
private String name;
+
private int access;
+
private boolean isStatic;
+ private final Map> attributesMap = new LinkedHashMap>();
+
+ private final Map> metaAnnotationMap = new LinkedHashMap>();
+
+
public MethodMetadataReadingVisitor(ClassLoader classLoader, String name, int access) {
super(new EmptyVisitor());
this.classLoader = classLoader;
@@ -52,6 +61,7 @@ public class MethodMetadataReadingVisitor extends MethodAdapter implements Metho
this.isStatic = ((access & Opcodes.ACC_STATIC) != 0);
}
+
public Map getAnnotationAttributes(String annotationType) {
return this.attributesMap.get(annotationType);
}
@@ -87,29 +97,22 @@ public class MethodMetadataReadingVisitor extends MethodAdapter implements Metho
}
public boolean isStatic() {
- return isStatic;
+ return this.isStatic;
}
-
public Set getAnnotationTypesWithMetaAnnotation(String metaAnnotationType) {
-
- ///metaAnnotationMap.put(className, metaAnnotationTypeNames);
Set annotationTypes = new LinkedHashSet();
- Set< Map.Entry> > metaValues = metaAnnotationMap.entrySet();
- Iterator> > metaIterator = metaValues.iterator();
- while (metaIterator.hasNext())
- {
- Map.Entry> entry = metaIterator.next();
+ for (Map.Entry> entry : metaAnnotationMap.entrySet()) {
String attributeType = entry.getKey();
Set metaAttributes = entry.getValue();
- if (metaAttributes.contains(metaAnnotationType))
- {
+ if (metaAttributes.contains(metaAnnotationType)) {
annotationTypes.add(attributeType);
}
}
return annotationTypes;
}
+
@Override
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
final String className = Type.getType(desc).getClassName();
@@ -121,13 +124,27 @@ public class MethodMetadataReadingVisitor extends MethodAdapter implements Metho
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 (int i = 0; i < annotationAttributes.length; i++) {
- Method annotationAttribute = annotationAttributes[i];
+ for (Method annotationAttribute : annotationAttributes) {
String attributeName = annotationAttribute.getName();
Object defaultValue = annotationAttribute.getDefaultValue();
if (defaultValue != null && !attributes.containsKey(attributeName)) {
@@ -150,5 +167,4 @@ public class MethodMetadataReadingVisitor extends MethodAdapter implements Metho
};
}
-
}