revised support for annotated factory methods (merged @FactoryMethod functionality into JavaConfig facility)
This commit is contained in:
parent
736169aa2a
commit
14bd475519
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -34,14 +34,6 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||||
*/
|
*/
|
||||||
public enum Autowire {
|
public enum Autowire {
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant that indicates that autowiring information was not specified.
|
|
||||||
* In some cases it may be necessary to specify autowiring status,
|
|
||||||
* but merely confirm that this should be inherited from an enclosing
|
|
||||||
* container definition scope.
|
|
||||||
*/
|
|
||||||
INHERITED(-1),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant that indicates no autowiring at all.
|
* Constant that indicates no autowiring at all.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -54,7 +54,6 @@ import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.PriorityOrdered;
|
import org.springframework.core.PriorityOrdered;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
@ -102,8 +101,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
protected final Log logger = LogFactory.getLog(AutowiredAnnotationBeanPostProcessor.class);
|
protected final Log logger = LogFactory.getLog(AutowiredAnnotationBeanPostProcessor.class);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Class<? extends Annotation>[] autowiredAnnotationTypes =
|
private Class<? extends Annotation>[] autowiredAnnotationTypes = new Class[] {Autowired.class, Value.class};
|
||||||
new Class[] {Autowired.class, Qualifier.class, Value.class};
|
|
||||||
|
|
||||||
private String requiredParameterName = "required";
|
private String requiredParameterName = "required";
|
||||||
|
|
||||||
|
@ -317,29 +315,19 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
});
|
});
|
||||||
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
|
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
|
||||||
public void doWith(Method method) {
|
public void doWith(Method method) {
|
||||||
if (!isFactoryMethod(method)) {
|
Annotation annotation = findAutowiredAnnotation(method);
|
||||||
Annotation annotation = findAutowiredAnnotation(method);
|
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||||
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
if (Modifier.isStatic(method.getModifiers())) {
|
throw new IllegalStateException("Autowired annotation is not supported on static methods");
|
||||||
throw new IllegalStateException("Autowired annotation is not supported on static methods");
|
|
||||||
}
|
|
||||||
if (method.getParameterTypes().length == 0) {
|
|
||||||
throw new IllegalStateException("Autowired annotation requires at least one argument: " + method);
|
|
||||||
}
|
|
||||||
boolean required = determineRequiredStatus(annotation);
|
|
||||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
|
|
||||||
newMetadata.addInjectedMethod(new AutowiredMethodElement(method, required, pd));
|
|
||||||
}
|
}
|
||||||
|
if (method.getParameterTypes().length == 0) {
|
||||||
|
throw new IllegalStateException("Autowired annotation requires at least one argument: " + method);
|
||||||
|
}
|
||||||
|
boolean required = determineRequiredStatus(annotation);
|
||||||
|
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
|
||||||
|
newMetadata.addInjectedMethod(new AutowiredMethodElement(method, required, pd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFactoryMethod(Method method) {
|
|
||||||
if (AnnotationUtils.findAnnotation(method, FactoryMethod.class)!= null) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
metadata = newMetadata;
|
metadata = newMetadata;
|
||||||
this.injectionMetadataCache.put(clazz, metadata);
|
this.injectionMetadataCache.put(clazz, metadata);
|
||||||
|
@ -429,7 +417,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
|
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
|
||||||
Field field = (Field) this.member;
|
Field field = (Field) this.member;
|
||||||
try {
|
try {
|
||||||
Object value = null;
|
Object value;
|
||||||
if (this.cached) {
|
if (this.cached) {
|
||||||
if (this.cachedFieldValue instanceof DependencyDescriptor) {
|
if (this.cachedFieldValue instanceof DependencyDescriptor) {
|
||||||
DependencyDescriptor descriptor = (DependencyDescriptor) this.cachedFieldValue;
|
DependencyDescriptor descriptor = (DependencyDescriptor) this.cachedFieldValue;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.beans.factory.annotation;
|
package org.springframework.beans.factory.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -25,9 +26,9 @@ import org.springframework.beans.SimpleTypeConverter;
|
||||||
import org.springframework.beans.TypeConverter;
|
import org.springframework.beans.TypeConverter;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
|
||||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||||
import org.springframework.beans.factory.support.AutowireCandidateResolver;
|
import org.springframework.beans.factory.support.AutowireCandidateResolver;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -134,8 +135,14 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
boolean match = checkQualifiers(bdHolder, descriptor.getAnnotations());
|
boolean match = checkQualifiers(bdHolder, descriptor.getAnnotations());
|
||||||
if (match && descriptor.getMethodParameter() != null) {
|
if (match) {
|
||||||
match = checkQualifiers(bdHolder, descriptor.getMethodParameter().getAnnotations());
|
MethodParameter methodParam = descriptor.getMethodParameter();
|
||||||
|
if (methodParam != null) {
|
||||||
|
Method method = methodParam.getMethod();
|
||||||
|
if (method == null || void.class.equals(method.getReturnType())) {
|
||||||
|
match = checkQualifiers(bdHolder, methodParam.getAnnotations());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
@ -178,15 +185,21 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
|
||||||
BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
|
BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
|
||||||
|
|
||||||
Class<? extends Annotation> type = annotation.annotationType();
|
Class<? extends Annotation> type = annotation.annotationType();
|
||||||
AbstractBeanDefinition bd = (AbstractBeanDefinition) bdHolder.getBeanDefinition();
|
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
|
||||||
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
|
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
|
||||||
if (qualifier == null) {
|
if (qualifier == null) {
|
||||||
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
|
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
|
||||||
}
|
}
|
||||||
if (qualifier == null && bd.hasBeanClass()) {
|
if (qualifier == null) {
|
||||||
// look for matching annotation on the target class
|
Annotation targetAnnotation = null;
|
||||||
Class<?> beanClass = bd.getBeanClass();
|
if (bd.getFactoryMethodForIntrospection() != null) {
|
||||||
Annotation targetAnnotation = beanClass.getAnnotation(type);
|
targetAnnotation = bd.getFactoryMethodForIntrospection().getAnnotation(type);
|
||||||
|
}
|
||||||
|
if (targetAnnotation == null && bd.hasBeanClass()) {
|
||||||
|
// look for matching annotation on the target class
|
||||||
|
Class<?> beanClass = bd.getBeanClass();
|
||||||
|
targetAnnotation = beanClass.getAnnotation(type);
|
||||||
|
}
|
||||||
if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
|
if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.beans.factory.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marker annotation identical in functionality with <aop:scoped-proxy/> tag. Provides a smart
|
|
||||||
* proxy backed by a scoped bean, which can be injected into object instances (usually singletons)
|
|
||||||
* allowing the same reference to be held while delegating method invocations to the backing, scoped
|
|
||||||
* beans.
|
|
||||||
*
|
|
||||||
* @author Costin Leau
|
|
||||||
*/
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Documented
|
|
||||||
public @interface ScopedProxy {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use CGLib-based class proxies (true) or JDK interface-based (false).
|
|
||||||
*
|
|
||||||
* Default is CGLib (true).
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
boolean proxyTargetClass() default true;
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -98,7 +98,10 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
|
||||||
* through introspection of the bean class.
|
* through introspection of the bean class.
|
||||||
* @see #createBean
|
* @see #createBean
|
||||||
* @see #autowire
|
* @see #autowire
|
||||||
|
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
|
||||||
|
* prefer annotation-based autowiring for clearer demarcation of autowiring needs.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
int AUTOWIRE_AUTODETECT = 4;
|
int AUTOWIRE_AUTODETECT = 4;
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,7 +184,6 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
|
||||||
* @see #AUTOWIRE_BY_NAME
|
* @see #AUTOWIRE_BY_NAME
|
||||||
* @see #AUTOWIRE_BY_TYPE
|
* @see #AUTOWIRE_BY_TYPE
|
||||||
* @see #AUTOWIRE_CONSTRUCTOR
|
* @see #AUTOWIRE_CONSTRUCTOR
|
||||||
* @see #AUTOWIRE_AUTODETECT
|
|
||||||
*/
|
*/
|
||||||
Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
|
Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -143,6 +143,30 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
|
||||||
*/
|
*/
|
||||||
void setScope(String scope);
|
void setScope(String scope);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this bean should be lazily initialized, i.e. not
|
||||||
|
* eagerly instantiated on startup. Only applicable to a singleton bean.
|
||||||
|
*/
|
||||||
|
boolean isLazyInit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this bean should be lazily initialized.
|
||||||
|
* <p>If <code>false</code>, the bean will get instantiated on startup by bean
|
||||||
|
* factories that perform eager initialization of singletons.
|
||||||
|
*/
|
||||||
|
void setLazyInit(boolean lazyInit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the bean names that this bean depends on.
|
||||||
|
*/
|
||||||
|
String[] getDependsOn();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the names of the beans that this bean depends on being initialized.
|
||||||
|
* The bean factory will guarantee that these beans get initialized first.
|
||||||
|
*/
|
||||||
|
void setDependsOn(String[] dependsOn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether this bean is a candidate for getting autowired into some other bean.
|
* Return whether this bean is a candidate for getting autowired into some other bean.
|
||||||
*/
|
*/
|
||||||
|
@ -153,6 +177,20 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
|
||||||
*/
|
*/
|
||||||
void setAutowireCandidate(boolean autowireCandidate);
|
void setAutowireCandidate(boolean autowireCandidate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this bean is a primary autowire candidate.
|
||||||
|
* If this value is true for exactly one bean among multiple
|
||||||
|
* matching candidates, it will serve as a tie-breaker.
|
||||||
|
*/
|
||||||
|
boolean isPrimary();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this bean is a primary autowire candidate.
|
||||||
|
* <p>If this value is true for exactly one bean among multiple
|
||||||
|
* matching candidates, it will serve as a tie-breaker.
|
||||||
|
*/
|
||||||
|
void setPrimary(boolean primary);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the constructor argument values for this bean.
|
* Return the constructor argument values for this bean.
|
||||||
|
@ -180,12 +218,6 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
|
||||||
*/
|
*/
|
||||||
boolean isAbstract();
|
boolean isAbstract();
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether this bean should be lazily initialized, that is, not
|
|
||||||
* eagerly instantiated on startup.
|
|
||||||
*/
|
|
||||||
boolean isLazyInit();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the role hint for this <code>BeanDefinition</code>. The role hint
|
* Get the role hint for this <code>BeanDefinition</code>. The role hint
|
||||||
* provides tools with an indication of the importance of a particular
|
* provides tools with an indication of the importance of a particular
|
||||||
|
|
|
@ -524,7 +524,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Class[] typesToMatch) {
|
protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Class[] typesToMatch) {
|
||||||
Class beanClass = null;
|
Class beanClass;
|
||||||
if (mbd.getFactoryMethodName() != null) {
|
if (mbd.getFactoryMethodName() != null) {
|
||||||
beanClass = getTypeForFactoryMethod(beanName, mbd, typesToMatch);
|
beanClass = getTypeForFactoryMethod(beanName, mbd, typesToMatch);
|
||||||
}
|
}
|
||||||
|
@ -562,7 +562,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
* @see #createBean
|
* @see #createBean
|
||||||
*/
|
*/
|
||||||
protected Class getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class[] typesToMatch) {
|
protected Class getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class[] typesToMatch) {
|
||||||
Class factoryClass = null;
|
Class factoryClass;
|
||||||
boolean isStatic = true;
|
boolean isStatic = true;
|
||||||
|
|
||||||
String factoryBeanName = mbd.getFactoryBeanName();
|
String factoryBeanName = mbd.getFactoryBeanName();
|
||||||
|
@ -1203,7 +1203,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
}
|
}
|
||||||
|
|
||||||
MutablePropertyValues mpvs = null;
|
MutablePropertyValues mpvs = null;
|
||||||
List<PropertyValue> original = null;
|
List<PropertyValue> original;
|
||||||
|
|
||||||
if (pvs instanceof MutablePropertyValues) {
|
if (pvs instanceof MutablePropertyValues) {
|
||||||
mpvs = (MutablePropertyValues) pvs;
|
mpvs = (MutablePropertyValues) pvs;
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
|
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -83,7 +83,10 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
* Constant that indicates determining an appropriate autowire strategy
|
* Constant that indicates determining an appropriate autowire strategy
|
||||||
* through introspection of the bean class.
|
* through introspection of the bean class.
|
||||||
* @see #setAutowireMode
|
* @see #setAutowireMode
|
||||||
|
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
|
||||||
|
* use annotation-based autowiring for clearer demarcation of autowiring needs.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
|
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,11 +137,11 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
|
|
||||||
private boolean autowireCandidate = true;
|
private boolean autowireCandidate = true;
|
||||||
|
|
||||||
|
private boolean primary = false;
|
||||||
|
|
||||||
private final Map<String, AutowireCandidateQualifier> qualifiers =
|
private final Map<String, AutowireCandidateQualifier> qualifiers =
|
||||||
new LinkedHashMap<String, AutowireCandidateQualifier>();
|
new LinkedHashMap<String, AutowireCandidateQualifier>();
|
||||||
|
|
||||||
private boolean primary = false;
|
|
||||||
|
|
||||||
private ConstructorArgumentValues constructorArgumentValues;
|
private ConstructorArgumentValues constructorArgumentValues;
|
||||||
|
|
||||||
private MutablePropertyValues propertyValues;
|
private MutablePropertyValues propertyValues;
|
||||||
|
@ -317,8 +320,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
*/
|
*/
|
||||||
public void applyDefaults(BeanDefinitionDefaults defaults) {
|
public void applyDefaults(BeanDefinitionDefaults defaults) {
|
||||||
setLazyInit(defaults.isLazyInit());
|
setLazyInit(defaults.isLazyInit());
|
||||||
setDependencyCheck(defaults.getDependencyCheck());
|
|
||||||
setAutowireMode(defaults.getAutowireMode());
|
setAutowireMode(defaults.getAutowireMode());
|
||||||
|
setDependencyCheck(defaults.getDependencyCheck());
|
||||||
setInitMethodName(defaults.getInitMethodName());
|
setInitMethodName(defaults.getInitMethodName());
|
||||||
setEnforceInitMethod(false);
|
setEnforceInitMethod(false);
|
||||||
setDestroyMethodName(defaults.getDestroyMethodName());
|
setDestroyMethodName(defaults.getDestroyMethodName());
|
||||||
|
@ -336,7 +339,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
/**
|
/**
|
||||||
* Specify the class for this bean.
|
* Specify the class for this bean.
|
||||||
*/
|
*/
|
||||||
public void setBeanClass(Class beanClass) {
|
public void setBeanClass(Class<?> beanClass) {
|
||||||
this.beanClass = beanClass;
|
this.beanClass = beanClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +349,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
* @throws IllegalStateException if the bean definition does not define a bean class,
|
* @throws IllegalStateException if the bean definition does not define a bean class,
|
||||||
* or a specified bean class name has not been resolved into an actual Class
|
* or a specified bean class name has not been resolved into an actual Class
|
||||||
*/
|
*/
|
||||||
public Class getBeanClass() throws IllegalStateException {
|
public Class<?> getBeanClass() throws IllegalStateException {
|
||||||
Object beanClassObject = this.beanClass;
|
Object beanClassObject = this.beanClass;
|
||||||
if (beanClassObject == null) {
|
if (beanClassObject == null) {
|
||||||
throw new IllegalStateException("No bean class specified on bean definition");
|
throw new IllegalStateException("No bean class specified on bean definition");
|
||||||
|
@ -556,7 +559,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the names of the beans that this bean depends on being initialized.
|
* Set the names of the beans that this bean depends on being initialized.
|
||||||
* The bean factory will guarantee that these beans get initialized before.
|
* The bean factory will guarantee that these beans get initialized first.
|
||||||
* <p>Note that dependencies are normally expressed through bean properties or
|
* <p>Note that dependencies are normally expressed through bean properties or
|
||||||
* constructor arguments. This property should just be necessary for other kinds
|
* constructor arguments. This property should just be necessary for other kinds
|
||||||
* of dependencies like statics (*ugh*) or database preparation on startup.
|
* of dependencies like statics (*ugh*) or database preparation on startup.
|
||||||
|
@ -586,6 +589,24 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
return this.autowireCandidate;
|
return this.autowireCandidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this bean is a primary autowire candidate.
|
||||||
|
* If this value is true for exactly one bean among multiple
|
||||||
|
* matching candidates, it will serve as a tie-breaker.
|
||||||
|
*/
|
||||||
|
public void setPrimary(boolean primary) {
|
||||||
|
this.primary = primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this bean is a primary autowire candidate.
|
||||||
|
* If this value is true for exactly one bean among multiple
|
||||||
|
* matching candidates, it will serve as a tie-breaker.
|
||||||
|
*/
|
||||||
|
public boolean isPrimary() {
|
||||||
|
return this.primary;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a qualifier to be used for autowire candidate resolution,
|
* Register a qualifier to be used for autowire candidate resolution,
|
||||||
* keyed by the qualifier's type name.
|
* keyed by the qualifier's type name.
|
||||||
|
@ -626,24 +647,6 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
this.qualifiers.putAll(source.qualifiers);
|
this.qualifiers.putAll(source.qualifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set whether this bean is a primary autowire candidate.
|
|
||||||
* If this value is true for exactly one bean among multiple
|
|
||||||
* matching candidates, it will serve as a tie-breaker.
|
|
||||||
*/
|
|
||||||
public void setPrimary(boolean primary) {
|
|
||||||
this.primary = primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether this bean is a primary autowire candidate.
|
|
||||||
* If this value is true for exactly one bean among multiple
|
|
||||||
* matching candidates, it will serve as a tie-breaker.
|
|
||||||
*/
|
|
||||||
public boolean isPrimary() {
|
|
||||||
return this.primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify constructor argument values for this bean.
|
* Specify constructor argument values for this bean.
|
||||||
|
|
|
@ -59,6 +59,28 @@ abstract class AutowireUtils {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the given factory methods, preferring public methods and "greedy" ones
|
||||||
|
* with a maximum of arguments. The result will contain public methods first,
|
||||||
|
* with decreasing number of arguments, then non-public methods, again with
|
||||||
|
* decreasing number of arguments.
|
||||||
|
* @param factoryMethods the factory method array to sort
|
||||||
|
*/
|
||||||
|
public static void sortFactoryMethods(Method[] factoryMethods) {
|
||||||
|
Arrays.sort(factoryMethods, new Comparator<Method>() {
|
||||||
|
public int compare(Method fm1, Method fm2) {
|
||||||
|
boolean p1 = Modifier.isPublic(fm1.getModifiers());
|
||||||
|
boolean p2 = Modifier.isPublic(fm2.getModifiers());
|
||||||
|
if (p1 != p2) {
|
||||||
|
return (p1 ? -1 : 1);
|
||||||
|
}
|
||||||
|
int c1pl = fm1.getParameterTypes().length;
|
||||||
|
int c2pl = fm2.getParameterTypes().length;
|
||||||
|
return (new Integer(c1pl)).compareTo(c2pl) * -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the given bean property is excluded from dependency checks.
|
* Determine whether the given bean property is excluded from dependency checks.
|
||||||
* <p>This implementation excludes properties defined by CGLIB.
|
* <p>This implementation excludes properties defined by CGLIB.
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -45,6 +46,7 @@ import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.util.MethodInvoker;
|
import org.springframework.util.MethodInvoker;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for resolving constructors and factory methods.
|
* Helper class for resolving constructors and factory methods.
|
||||||
|
@ -284,6 +286,10 @@ class ConstructorResolver {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// It's a static factory method on the bean class.
|
// It's a static factory method on the bean class.
|
||||||
|
if (!mbd.hasBeanClass()) {
|
||||||
|
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
|
||||||
|
"bean definition declares neither a bean class nor a factory-bean reference");
|
||||||
|
}
|
||||||
factoryBean = null;
|
factoryBean = null;
|
||||||
factoryClass = mbd.getBeanClass();
|
factoryClass = mbd.getBeanClass();
|
||||||
isStatic = true;
|
isStatic = true;
|
||||||
|
@ -309,7 +315,18 @@ class ConstructorResolver {
|
||||||
if (factoryMethodToUse == null) {
|
if (factoryMethodToUse == null) {
|
||||||
// Need to determine the factory method...
|
// Need to determine the factory method...
|
||||||
// Try all methods with this name to see if they match the given arguments.
|
// Try all methods with this name to see if they match the given arguments.
|
||||||
Method[] candidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
|
factoryClass = ClassUtils.getUserClass(factoryClass);
|
||||||
|
Method[] rawCandidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
|
||||||
|
List<Method> candidateSet = new ArrayList<Method>();
|
||||||
|
for (Method candidate : rawCandidates) {
|
||||||
|
if (Modifier.isStatic(candidate.getModifiers()) == isStatic &&
|
||||||
|
candidate.getName().equals(mbd.getFactoryMethodName())) {
|
||||||
|
candidateSet.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Method[] candidates = candidateSet.toArray(new Method[candidateSet.size()]);
|
||||||
|
AutowireUtils.sortFactoryMethods(candidates);
|
||||||
|
|
||||||
boolean autowiring = (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
boolean autowiring = (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||||
int minTypeDiffWeight = Integer.MAX_VALUE;
|
int minTypeDiffWeight = Integer.MAX_VALUE;
|
||||||
ConstructorArgumentValues resolvedValues = null;
|
ConstructorArgumentValues resolvedValues = null;
|
||||||
|
@ -332,10 +349,7 @@ class ConstructorResolver {
|
||||||
Method candidate = candidates[i];
|
Method candidate = candidates[i];
|
||||||
Class[] paramTypes = candidate.getParameterTypes();
|
Class[] paramTypes = candidate.getParameterTypes();
|
||||||
|
|
||||||
if (Modifier.isStatic(candidate.getModifiers()) == isStatic &&
|
if (paramTypes.length >= minNrOfArgs) {
|
||||||
candidate.getName().equals(mbd.getFactoryMethodName()) &&
|
|
||||||
paramTypes.length >= minNrOfArgs) {
|
|
||||||
|
|
||||||
ArgumentsHolder args;
|
ArgumentsHolder args;
|
||||||
|
|
||||||
if (resolvedValues != null) {
|
if (resolvedValues != null) {
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
@ -47,7 +49,9 @@ import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -428,6 +432,32 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
*/
|
*/
|
||||||
protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor) {
|
protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor) {
|
||||||
resolveBeanClass(mbd, beanName);
|
resolveBeanClass(mbd, beanName);
|
||||||
|
// TODO: the following is duplicating the factory method resolution algorithm in
|
||||||
|
// ConstructorResolver quite a bit...
|
||||||
|
if (mbd.getFactoryMethodName() != null && mbd.factoryMethodForIntrospection == null) {
|
||||||
|
Class factoryClass;
|
||||||
|
if (mbd.getFactoryBeanName() != null) {
|
||||||
|
factoryClass = getType(mbd.getFactoryBeanName());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
factoryClass = mbd.getBeanClass();
|
||||||
|
}
|
||||||
|
factoryClass = ClassUtils.getUserClass(factoryClass);
|
||||||
|
Method[] candidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
|
||||||
|
Method uniqueCandidate = null;
|
||||||
|
for (Method candidate : candidates) {
|
||||||
|
if (candidate.getName().equals(mbd.getFactoryMethodName())) {
|
||||||
|
if (uniqueCandidate == null) {
|
||||||
|
uniqueCandidate = candidate;
|
||||||
|
}
|
||||||
|
else if (!Arrays.equals(uniqueCandidate.getParameterTypes(), candidate.getParameterTypes())) {
|
||||||
|
uniqueCandidate = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mbd.factoryMethodForIntrospection = uniqueCandidate;
|
||||||
|
}
|
||||||
return getAutowireCandidateResolver().isAutowireCandidate(
|
return getAutowireCandidateResolver().isAutowireCandidate(
|
||||||
new BeanDefinitionHolder(mbd, beanName, getAliases(beanName)), descriptor);
|
new BeanDefinitionHolder(mbd, beanName, getAliases(beanName)), descriptor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -51,6 +52,8 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||||
|
|
||||||
private final Set<String> externallyManagedDestroyMethods = Collections.synchronizedSet(new HashSet<String>());
|
private final Set<String> externallyManagedDestroyMethods = Collections.synchronizedSet(new HashSet<String>());
|
||||||
|
|
||||||
|
volatile Method factoryMethodForIntrospection;
|
||||||
|
|
||||||
/** Package-visible field for caching the resolved constructor or factory method */
|
/** Package-visible field for caching the resolved constructor or factory method */
|
||||||
volatile Object resolvedConstructorOrFactoryMethod;
|
volatile Object resolvedConstructorOrFactoryMethod;
|
||||||
|
|
||||||
|
@ -218,6 +221,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Method getFactoryMethodForIntrospection() {
|
||||||
|
return this.factoryMethodForIntrospection;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void registerExternallyManagedConfigMember(Member configMember) {
|
public void registerExternallyManagedConfigMember(Member configMember) {
|
||||||
this.externallyManagedConfigMembers.add(configMember);
|
this.externallyManagedConfigMembers.add(configMember);
|
||||||
|
|
|
@ -1,122 +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 org.springframework.beans.factory.config.BeanDefinition;
|
|
||||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract superclass for processing {@link Configuration}-annotated classes and registering
|
|
||||||
* bean definitions based on {@link Bean}-annotated methods within those classes.
|
|
||||||
*
|
|
||||||
* <p>Provides template method {@link #processConfigBeanDefinitions()} that orchestrates calling each
|
|
||||||
* of several abstract methods to be overriden by concrete implementations that allow for
|
|
||||||
* customizing how {@link Configuration} classes are found ({@link #getConfigurationBeanDefinitions}),
|
|
||||||
* customizing the creation of a {@link ConfigurationClassParser} ({@link #createConfigurationParser}),
|
|
||||||
* and customizing {@link ConfigurationModel} validation logic ({@link #validateModel}).
|
|
||||||
*
|
|
||||||
* <p>This class was expressly designed with tooling in mind. Spring IDE will maintain it's
|
|
||||||
* own implementation of this class but still take advantage of the generic parsing algorithm
|
|
||||||
* defined here by {@link #processConfigBeanDefinitions()}.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
* @since 3.0
|
|
||||||
* @see Configuration
|
|
||||||
* @see ConfigurationClassPostProcessor
|
|
||||||
*/
|
|
||||||
public abstract class AbstractConfigurationClassProcessor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to register any problems detected with {@link Configuration} or {@link Bean}
|
|
||||||
* declarations. For instance, a Bean method marked as {@literal final} is illegal
|
|
||||||
* and would be reported as a problem. Defaults to {@link FailFastProblemReporter},
|
|
||||||
* but is overridable with {@link #setProblemReporter}
|
|
||||||
*/
|
|
||||||
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populate and return a registry containing all {@link Configuration} bean definitions
|
|
||||||
* to be processed.
|
|
||||||
*
|
|
||||||
* @param includeAbstractBeanDefs whether abstract Configuration bean definitions should
|
|
||||||
* be included in the resulting BeanDefinitionRegistry. Usually false, but called as true
|
|
||||||
* during the enhancement phase.
|
|
||||||
* @see #processConfigBeanDefinitions()
|
|
||||||
*/
|
|
||||||
protected abstract BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and return a new {@link ConfigurationClassParser}, allowing for customization of
|
|
||||||
* type (ASM/JDT/Reflection) as well as providing specialized ClassLoader during
|
|
||||||
* construction.
|
|
||||||
* @see #processConfigBeanDefinitions()
|
|
||||||
*/
|
|
||||||
protected abstract ConfigurationClassParser createConfigurationParser();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override the default {@link ProblemReporter}.
|
|
||||||
* @param problemReporter custom problem reporter
|
|
||||||
*/
|
|
||||||
protected final void setProblemReporter(ProblemReporter problemReporter) {
|
|
||||||
this.problemReporter = problemReporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the currently registered {@link ProblemReporter}.
|
|
||||||
*/
|
|
||||||
protected final ProblemReporter getProblemReporter() {
|
|
||||||
return problemReporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build and validate a {@link ConfigurationModel} based on the registry of
|
|
||||||
* {@link Configuration} classes provided by {@link #getConfigurationBeanDefinitions},
|
|
||||||
* then, based on the content of that model, create and register bean definitions
|
|
||||||
* against a new {@link BeanDefinitionRegistry}, then return the registry.
|
|
||||||
*
|
|
||||||
* @return registry containing one bean definition per {@link Bean} method declared
|
|
||||||
* within the Configuration classes
|
|
||||||
*/
|
|
||||||
protected final BeanDefinitionRegistry processConfigBeanDefinitions() {
|
|
||||||
BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(false);
|
|
||||||
|
|
||||||
// return an empty registry immediately if no @Configuration classes were found
|
|
||||||
if(configBeanDefs.getBeanDefinitionCount() == 0)
|
|
||||||
return configBeanDefs;
|
|
||||||
|
|
||||||
// populate a new ConfigurationModel by parsing each @Configuration classes
|
|
||||||
ConfigurationClassParser parser = createConfigurationParser();
|
|
||||||
|
|
||||||
for(String beanName : configBeanDefs.getBeanDefinitionNames()) {
|
|
||||||
BeanDefinition beanDef = configBeanDefs.getBeanDefinition(beanName);
|
|
||||||
String className = beanDef.getBeanClassName();
|
|
||||||
|
|
||||||
parser.parse(className, beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigurationModel configModel = parser.getConfigurationModel();
|
|
||||||
|
|
||||||
configModel.validate(problemReporter);
|
|
||||||
|
|
||||||
// read the model and create bean definitions based on its content
|
|
||||||
return new ConfigurationModelBeanDefinitionReader().loadBeanDefinitions(configModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,115 +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 net.sf.cglib.asm.Constants;
|
|
||||||
|
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
|
||||||
import org.springframework.asm.ClassAdapter;
|
|
||||||
import org.springframework.asm.ClassVisitor;
|
|
||||||
import org.springframework.asm.FieldVisitor;
|
|
||||||
import org.springframework.asm.MethodVisitor;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms a class by adding bytecode for a class-level annotation. Checks to ensure that
|
|
||||||
* the desired annotation is not already present before adding. Used by
|
|
||||||
* {@link ConfigurationClassEnhancer} to dynamically add an {@link org.aspectj.lang.Aspect}
|
|
||||||
* annotation to an enhanced Configuration subclass.
|
|
||||||
*
|
|
||||||
* <p>This class was originally adapted from examples the ASM 3.0 documentation.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
*/
|
|
||||||
class AddAnnotationAdapter extends ClassAdapter {
|
|
||||||
private String annotationDesc;
|
|
||||||
private boolean isAnnotationPresent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AddAnnotationAdapter instance.
|
|
||||||
*
|
|
||||||
* @param cv the ClassVisitor delegate
|
|
||||||
* @param annotationDesc name of the annotation to be added (in type descriptor format)
|
|
||||||
*/
|
|
||||||
public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) {
|
|
||||||
super(cv);
|
|
||||||
this.annotationDesc = annotationDesc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures that the version of the resulting class is Java 5 or better.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visit(int version, int access, String name, String signature,
|
|
||||||
String superName, String[] interfaces) {
|
|
||||||
int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version;
|
|
||||||
cv.visit(v, access, name, signature, superName, interfaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks to ensure that the desired annotation is not already present.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
|
||||||
if (visible && desc.equals(annotationDesc)) {
|
|
||||||
isAnnotationPresent = true;
|
|
||||||
}
|
|
||||||
return cv.visitAnnotation(desc, visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
|
||||||
addAnnotation();
|
|
||||||
cv.visitInnerClass(name, outerName, innerName, access);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
|
||||||
addAnnotation();
|
|
||||||
return cv.visitField(access, name, desc, signature, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
|
||||||
addAnnotation();
|
|
||||||
return cv.visitMethod(access, name, desc, signature, exceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kicks off the process of actually adding the desired annotation.
|
|
||||||
*
|
|
||||||
* @see #addAnnotation()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitEnd() {
|
|
||||||
addAnnotation();
|
|
||||||
cv.visitEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actually adds the desired annotation.
|
|
||||||
*/
|
|
||||||
private void addAnnotation() {
|
|
||||||
if (!isAnnotationPresent) {
|
|
||||||
AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true);
|
|
||||||
if (av != null) {
|
|
||||||
av.visitEnd();
|
|
||||||
}
|
|
||||||
isAnnotationPresent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +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 org.springframework.asm.AnnotationVisitor;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An empty {@link AnnotationVisitor} that delegates to another AnnotationVisitor. This
|
|
||||||
* class can be used as a super class to quickly implement useful annotation adapter
|
|
||||||
* classes, just by overriding the necessary methods. Note that for some reason, ASM
|
|
||||||
* doesn't provide this class (it does provide MethodAdapter and ClassAdapter), thus
|
|
||||||
* we're following the general pattern and adding our own here.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
*/
|
|
||||||
class AnnotationAdapter implements AnnotationVisitor {
|
|
||||||
|
|
||||||
private AnnotationVisitor delegate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationAdapter instance that will delegate all its calls to
|
|
||||||
* <var>delegate</var>.
|
|
||||||
*
|
|
||||||
* @param delegate In most cases, the delegate will simply be
|
|
||||||
* {@link AsmUtils#ASM_EMPTY_VISITOR}
|
|
||||||
*/
|
|
||||||
public AnnotationAdapter(AnnotationVisitor delegate) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visit(String arg0, Object arg1) {
|
|
||||||
delegate.visit(arg0, arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
|
|
||||||
return delegate.visitAnnotation(arg0, arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitArray(String arg0) {
|
|
||||||
return delegate.visitArray(arg0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnum(String arg0, String arg1, String arg2) {
|
|
||||||
delegate.visitEnum(arg0, arg1, arg2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnd() {
|
|
||||||
delegate.visitEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -45,18 +45,18 @@ import org.springframework.util.ClassUtils;
|
||||||
*/
|
*/
|
||||||
public class AnnotationConfigUtils {
|
public class AnnotationConfigUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bean name of the internally managed Configuration annotation processor.
|
||||||
|
*/
|
||||||
|
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||||
|
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bean name of the internally managed Autowired annotation processor.
|
* The bean name of the internally managed Autowired annotation processor.
|
||||||
*/
|
*/
|
||||||
public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
|
public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||||
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
|
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
|
||||||
|
|
||||||
/**
|
|
||||||
* The bean name of the internally managed Configuration annotation processor.
|
|
||||||
*/
|
|
||||||
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
|
|
||||||
"org.springframework.context.annotation.configurationAnnotationProcessor";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bean name of the internally managed Required annotation processor.
|
* The bean name of the internally managed Required annotation processor.
|
||||||
*/
|
*/
|
||||||
|
@ -109,18 +109,18 @@ public class AnnotationConfigUtils {
|
||||||
|
|
||||||
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
|
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
|
||||||
|
|
||||||
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
|
||||||
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
|
|
||||||
def.setSource(source);
|
|
||||||
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||||
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
|
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
|
||||||
def.setSource(source);
|
def.setSource(source);
|
||||||
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
|
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||||
|
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
|
||||||
|
def.setSource(source);
|
||||||
|
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||||
RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
|
RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
|
||||||
def.setSource(source);
|
def.setSource(source);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -33,13 +33,13 @@ import org.springframework.util.Assert;
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
* @see Scope
|
* @see org.springframework.context.annotation.Scope
|
||||||
*/
|
*/
|
||||||
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||||
|
|
||||||
private Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
private Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
||||||
|
|
||||||
private ScopedProxyMode scopedProxyMode;
|
private final ScopedProxyMode defaultProxyMode;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,16 +48,16 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||||
* @see ScopedProxyMode#NO
|
* @see ScopedProxyMode#NO
|
||||||
*/
|
*/
|
||||||
public AnnotationScopeMetadataResolver() {
|
public AnnotationScopeMetadataResolver() {
|
||||||
this(ScopedProxyMode.NO);
|
this.defaultProxyMode = ScopedProxyMode.NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
||||||
* @param scopedProxyMode the desired scoped-proxy mode
|
* @param defaultProxyMode the desired scoped-proxy mode
|
||||||
*/
|
*/
|
||||||
public AnnotationScopeMetadataResolver(ScopedProxyMode scopedProxyMode) {
|
public AnnotationScopeMetadataResolver(ScopedProxyMode defaultProxyMode) {
|
||||||
Assert.notNull(scopedProxyMode, "'scopedProxyMode' must not be null");
|
Assert.notNull(defaultProxyMode, "'defaultProxyMode' must not be null");
|
||||||
this.scopedProxyMode = scopedProxyMode;
|
this.defaultProxyMode = defaultProxyMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,11 +78,16 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||||
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
|
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
|
||||||
Map<String, Object> attributes =
|
Map<String, Object> attributes =
|
||||||
annDef.getMetadata().getAnnotationAttributes(this.scopeAnnotationType.getName());
|
annDef.getMetadata().getAnnotationAttributes(this.scopeAnnotationType.getName());
|
||||||
|
ScopedProxyMode annMode = null;
|
||||||
if (attributes != null) {
|
if (attributes != null) {
|
||||||
metadata.setScopeName((String) attributes.get("value"));
|
metadata.setScopeName((String) attributes.get("value"));
|
||||||
|
annMode = (ScopedProxyMode) attributes.get("proxyMode");
|
||||||
}
|
}
|
||||||
if (!metadata.getScopeName().equals(BeanDefinition.SCOPE_SINGLETON)) {
|
if (annMode != null && annMode != ScopedProxyMode.DEFAULT) {
|
||||||
metadata.setScopedProxyMode(this.scopedProxyMode);
|
metadata.setScopedProxyMode(annMode);
|
||||||
|
}
|
||||||
|
else if (!metadata.getScopeName().equals(BeanDefinition.SCOPE_SINGLETON)) {
|
||||||
|
metadata.setScopedProxyMode(this.defaultProxyMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return metadata;
|
return metadata;
|
||||||
|
|
|
@ -1,229 +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.ClassUtils.*;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.asm.ClassReader;
|
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Various utility methods commonly used when interacting with ASM, classloading
|
|
||||||
* and creating {@link MutableAnnotation} instances.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
*/
|
|
||||||
class AsmUtils {
|
|
||||||
|
|
||||||
public static final EmptyVisitor ASM_EMPTY_VISITOR = new EmptyVisitor();
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(AsmUtils.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class' to
|
|
||||||
* pathToClass before attempting to load.
|
|
||||||
*
|
|
||||||
* @throws RuntimeException if <var>pathToClass</var>+.class cannot be found on the
|
|
||||||
* classpath
|
|
||||||
* @throws RuntimeException if an IOException occurs when creating the new ClassReader
|
|
||||||
*/
|
|
||||||
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 <var>is</var>, 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 RuntimeException("An unexpected exception occurred while creating ASM ClassReader: " + ex);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
is.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
log.error("Ignoring exception thrown while closing InputStream", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the default ClassLoader to load <var>pathToClass</var>. 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 <var>pathToClass</var>
|
|
||||||
*
|
|
||||||
* @throws RuntimeException if <var>pathToClass</var> 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 RuntimeException(
|
|
||||||
new FileNotFoundException("Class file [" + classFileName + "] not found"));
|
|
||||||
|
|
||||||
return is;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the specified class using the default class loader, rethrowing any
|
|
||||||
* {@link ClassNotFoundException} as an unchecked exception.
|
|
||||||
*
|
|
||||||
* @param <T> type of class to be returned
|
|
||||||
* @param fqClassName fully-qualified class name
|
|
||||||
*
|
|
||||||
* @return newly loaded class instance
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if configClassName cannot be loaded.
|
|
||||||
*
|
|
||||||
* @see #loadClass(String)
|
|
||||||
* @see #loadToolingSafeClass(String)
|
|
||||||
* @see ClassUtils#getDefaultClassLoader()
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> Class<? extends T> loadRequiredClass(String fqClassName) {
|
|
||||||
try {
|
|
||||||
return (Class<? extends T>) getDefaultClassLoader().loadClass(fqClassName);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
throw new IllegalArgumentException(format(
|
|
||||||
"Class [%s] could not be loaded, check your CLASSPATH.", fqClassName), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* <p>
|
|
||||||
* Because {@link ClassNotFoundException} is compensated for by returning null, callers
|
|
||||||
* must take care to handle the null case appropriately.
|
|
||||||
* <p>
|
|
||||||
* In cases where the WARN logging statement is not desired, use the
|
|
||||||
* {@link #loadClass(String)} method, which returns null but issues no logging
|
|
||||||
* statements.
|
|
||||||
* <p>
|
|
||||||
* 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 <T> 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 <T> Class<? extends T> loadToolingSafeClass(String fqClassName, ClassLoader classLoader) {
|
|
||||||
try {
|
|
||||||
return (Class<? extends T>) classLoader.loadClass(fqClassName);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
log.warn(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 MutableAnnotation} for {@code annoType}. JDK dynamic proxies are
|
|
||||||
* used, and the returned proxy implements both {@link MutableAnnotation} and annotation
|
|
||||||
* type {@code A}
|
|
||||||
*
|
|
||||||
* @param <A> annotation type that must be supplied and returned
|
|
||||||
* @param annoType type of annotation to create
|
|
||||||
*/
|
|
||||||
public static <A extends Annotation> A createMutableAnnotation(Class<A> annoType, ClassLoader classLoader) {
|
|
||||||
MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType);
|
|
||||||
Class<?>[] interfaces = new Class<?>[] { annoType, MutableAnnotation.class };
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
|
||||||
return mutableAnno;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -22,6 +22,7 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowire;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that a method produces a bean to be managed by the Spring container. The
|
* Indicates that a method produces a bean to be managed by the Spring container. The
|
||||||
|
@ -60,7 +61,7 @@ import java.lang.annotation.Target;
|
||||||
* @see Configuration
|
* @see Configuration
|
||||||
* @see Lazy
|
* @see Lazy
|
||||||
* @see Primary
|
* @see Primary
|
||||||
* @see Scope
|
* @see org.springframework.context.annotation.Scope
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@ -74,6 +75,11 @@ public @interface Bean {
|
||||||
*/
|
*/
|
||||||
String[] name() default {};
|
String[] name() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are dependencies to be injected via autowiring?
|
||||||
|
*/
|
||||||
|
Autowire autowire() default Autowire.NO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The optional name of a method to call on the bean instance during initialization.
|
* The optional name of a method to call on the bean instance during initialization.
|
||||||
* Not commonly used, given that the method may be called programmatically directly
|
* Not commonly used, given that the method may be called programmatically directly
|
||||||
|
@ -82,10 +88,9 @@ public @interface Bean {
|
||||||
String initMethod() default "";
|
String initMethod() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The optional name of a method to call on the bean instance during upon closing
|
* The optional name of a method to call on the bean instance during upon closing the
|
||||||
* the application context, for example a {@literal close()}
|
* application context, for example a {@literal close()} method on a {@literal DataSource}.
|
||||||
* method on a {@literal DataSource}. The method must have no arguments, but may
|
* The method must have no arguments, but may throw any exception.
|
||||||
* throw any exception.
|
|
||||||
* <p>Note: Only invoked on beans whose lifecycle is under the full control of the
|
* <p>Note: Only invoked on beans whose lifecycle is under the full control of the
|
||||||
* factory which is always the case for singletons, but not guaranteed
|
* factory which is always the case for singletons, but not guaranteed
|
||||||
* for any other scope.
|
* for any other scope.
|
||||||
|
@ -93,14 +98,4 @@ public @interface Bean {
|
||||||
*/
|
*/
|
||||||
String destroyMethod() default "";
|
String destroyMethod() default "";
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* <p>Note: This attribute will not be inherited by child bean definitions,
|
|
||||||
* hence it needs to be specified per concrete bean definition.
|
|
||||||
*/
|
|
||||||
String[] dependsOn() default {};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
|
||||||
|
|
||||||
import static java.lang.String.*;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import net.sf.cglib.proxy.MethodInterceptor;
|
|
||||||
import net.sf.cglib.proxy.MethodProxy;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
class BeanMethodInterceptor implements MethodInterceptor {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(BeanMethodInterceptor.class);
|
|
||||||
|
|
||||||
private final DefaultListableBeanFactory beanFactory;
|
|
||||||
|
|
||||||
public BeanMethodInterceptor(DefaultListableBeanFactory beanFactory) {
|
|
||||||
this.beanFactory = beanFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enhances 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];
|
|
||||||
|
|
||||||
// determine whether this bean is a scoped-proxy
|
|
||||||
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
|
||||||
boolean isScopedProxy = (scope != null && scope.proxyMode() != ScopedProxyMode.NO);
|
|
||||||
String scopedBeanName = ConfigurationModelBeanDefinitionReader.resolveHiddenScopedProxyBeanName(beanName);
|
|
||||||
if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
|
|
||||||
beanName = scopedBeanName;
|
|
||||||
|
|
||||||
// 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 (log.isInfoEnabled())
|
|
||||||
log.info(format("Returning cached singleton 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 <var>beanName</var> already
|
|
||||||
* exists. Accounts for the fact that the requested bean may be "in creation", i.e.:
|
|
||||||
* we're in the middle of servicing the initial request for this bean. From JavaConfig's
|
|
||||||
* perspective, this means that the bean does not actually yet exist, and that it is now
|
|
||||||
* our job to create it for the first time by executing the logic in the corresponding
|
|
||||||
* Bean method.
|
|
||||||
* <p>
|
|
||||||
* Said another way, this check repurposes
|
|
||||||
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
|
|
||||||
* the container is calling this method or the user is calling this method.
|
|
||||||
*
|
|
||||||
* @param beanName name of bean to check for
|
|
||||||
*
|
|
||||||
* @return true if <var>beanName</var> already exists in beanFactory
|
|
||||||
*/
|
|
||||||
private boolean factoryContainsBean(String beanName) {
|
|
||||||
return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -197,54 +197,39 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||||
*/
|
*/
|
||||||
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
|
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
|
||||||
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
|
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
|
||||||
for (int i = 0; i < basePackages.length; i++) {
|
for (String basePackage : basePackages) {
|
||||||
Set<BeanDefinition> candidates = findCandidateComponents(basePackages[i]);
|
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
|
||||||
for (BeanDefinition candidate : candidates) {
|
for (BeanDefinition candidate : candidates) {
|
||||||
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
|
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
|
||||||
if (candidate instanceof AbstractBeanDefinition) {
|
if (candidate instanceof AbstractBeanDefinition) {
|
||||||
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
|
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
|
||||||
}
|
}
|
||||||
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
|
if (candidate instanceof AnnotatedBeanDefinition) {
|
||||||
|
AnnotatedBeanDefinition abd = (AnnotatedBeanDefinition) candidate;
|
||||||
|
if (abd.getMetadata().hasAnnotation(Primary.class.getName())) {
|
||||||
|
abd.setPrimary(true);
|
||||||
|
}
|
||||||
|
if (abd.getMetadata().hasAnnotation(Lazy.class.getName())) {
|
||||||
|
Boolean value = (Boolean) abd.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value");
|
||||||
|
abd.setLazyInit(value);
|
||||||
|
}
|
||||||
|
if (abd.getMetadata().hasAnnotation(DependsOn.class.getName())) {
|
||||||
|
String[] value = (String[]) abd.getMetadata().getAnnotationAttributes(DependsOn.class.getName()).get("value");
|
||||||
|
abd.setDependsOn(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (checkCandidate(beanName, candidate)) {
|
if (checkCandidate(beanName, candidate)) {
|
||||||
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
|
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
|
||||||
|
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
|
||||||
definitionHolder = applyScope(definitionHolder, scopeMetadata);
|
definitionHolder = applyScope(definitionHolder, scopeMetadata);
|
||||||
beanDefinitions.add(definitionHolder);
|
beanDefinitions.add(definitionHolder);
|
||||||
registerBeanDefinition(definitionHolder, this.registry);
|
registerBeanDefinition(definitionHolder, this.registry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
postProcessComponentBeanDefinitions(beanDefinitions);
|
|
||||||
return beanDefinitions;
|
return beanDefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void postProcessComponentBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
|
|
||||||
//TODO refactor increment index count as part of naming strategy.
|
|
||||||
Set<BeanDefinitionHolder> factoryBeanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
|
|
||||||
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitions) {
|
|
||||||
Set<BeanDefinition> candidates = findCandidateFactoryMethods(beanDefinitionHolder);
|
|
||||||
for (BeanDefinition candidate : candidates ) {
|
|
||||||
|
|
||||||
|
|
||||||
BeanDefinitionHolder definitionHolder;
|
|
||||||
if (candidate.getBeanClassName().equals("org.springframework.aop.scope.ScopedProxyFactoryBean")){
|
|
||||||
String scopedFactoryBeanName = "scopedTarget." + candidate.getPropertyValues().getPropertyValue("targetBeanName").getValue();
|
|
||||||
definitionHolder = new BeanDefinitionHolder(candidate, scopedFactoryBeanName);
|
|
||||||
} else {
|
|
||||||
String configurationComponentBeanName = beanDefinitionHolder.getBeanName();
|
|
||||||
String factoryMethodName = candidate.getFactoryMethodName();
|
|
||||||
String beanName = createFactoryBeanName(configurationComponentBeanName, factoryMethodName);
|
|
||||||
definitionHolder = new BeanDefinitionHolder(candidate, beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
factoryBeanDefinitions.add(definitionHolder);
|
|
||||||
registerBeanDefinition(definitionHolder, this.registry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
beanDefinitions.addAll(factoryBeanDefinitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply further settings to the given bean definition,
|
* Apply further settings to the given bean definition,
|
||||||
* beyond the contents retrieved from scanning the component class.
|
* beyond the contents retrieved from scanning the component class.
|
||||||
|
@ -325,8 +310,7 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||||
String scope = scopeMetadata.getScopeName();
|
String scope = scopeMetadata.getScopeName();
|
||||||
ScopedProxyMode scopedProxyMode = scopeMetadata.getScopedProxyMode();
|
ScopedProxyMode scopedProxyMode = scopeMetadata.getScopedProxyMode();
|
||||||
definitionHolder.getBeanDefinition().setScope(scope);
|
definitionHolder.getBeanDefinition().setScope(scope);
|
||||||
if (BeanDefinition.SCOPE_SINGLETON.equals(scope) || BeanDefinition.SCOPE_PROTOTYPE.equals(scope) ||
|
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
|
||||||
scopedProxyMode.equals(ScopedProxyMode.NO)) {
|
|
||||||
return definitionHolder;
|
return definitionHolder;
|
||||||
}
|
}
|
||||||
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
|
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,33 +17,23 @@
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.aop.scope.ScopedProxyFactoryBean;
|
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
|
||||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
|
||||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
|
||||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
|
||||||
import org.springframework.context.ResourceLoaderAware;
|
import org.springframework.context.ResourceLoaderAware;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
|
||||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||||
import org.springframework.core.type.classreading.MetadataReader;
|
import org.springframework.core.type.classreading.MetadataReader;
|
||||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||||
|
@ -75,13 +65,8 @@ import org.springframework.util.SystemPropertyUtils;
|
||||||
*/
|
*/
|
||||||
public class ClassPathScanningCandidateComponentProvider implements ResourceLoaderAware {
|
public class ClassPathScanningCandidateComponentProvider implements ResourceLoaderAware {
|
||||||
|
|
||||||
protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||||
|
|
||||||
protected static final String QUALIFIER_CLASS_NAME = "org.springframework.beans.factory.annotation.Qualifier";
|
|
||||||
|
|
||||||
protected static final String SCOPE_CLASS_NAME = "org.springframework.context.annotation.Scope";
|
|
||||||
|
|
||||||
protected static final String SCOPEDPROXY_CLASS_NAME = "org.springframework.beans.factory.annotation.ScopedProxy";
|
|
||||||
|
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
@ -95,8 +80,6 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
||||||
|
|
||||||
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
|
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
|
||||||
|
|
||||||
private int factoryBeanCount = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a ClassPathScanningCandidateComponentProvider.
|
* Create a ClassPathScanningCandidateComponentProvider.
|
||||||
|
@ -199,33 +182,38 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
||||||
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
|
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
|
||||||
boolean traceEnabled = logger.isTraceEnabled();
|
boolean traceEnabled = logger.isTraceEnabled();
|
||||||
boolean debugEnabled = logger.isDebugEnabled();
|
boolean debugEnabled = logger.isDebugEnabled();
|
||||||
for (int i = 0; i < resources.length; i++) {
|
for (Resource resource : resources) {
|
||||||
Resource resource = resources[i];
|
|
||||||
if (traceEnabled) {
|
if (traceEnabled) {
|
||||||
logger.trace("Scanning " + resource);
|
logger.trace("Scanning " + resource);
|
||||||
}
|
}
|
||||||
if (resource.isReadable()) {
|
if (resource.isReadable()) {
|
||||||
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
|
try {
|
||||||
if (isCandidateComponent(metadataReader)) {
|
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
|
||||||
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
|
if (isCandidateComponent(metadataReader)) {
|
||||||
sbd.setResource(resource);
|
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
|
||||||
sbd.setSource(resource);
|
sbd.setResource(resource);
|
||||||
if (isCandidateComponent(sbd)) {
|
sbd.setSource(resource);
|
||||||
if (debugEnabled) {
|
if (isCandidateComponent(sbd)) {
|
||||||
logger.debug("Identified candidate component class: " + resource);
|
if (debugEnabled) {
|
||||||
|
logger.debug("Identified candidate component class: " + resource);
|
||||||
|
}
|
||||||
|
candidates.add(sbd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (debugEnabled) {
|
||||||
|
logger.debug("Ignored because not a concrete top-level class: " + resource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
candidates.add(sbd);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (debugEnabled) {
|
if (traceEnabled) {
|
||||||
logger.debug("Ignored because not a concrete top-level class: " + resource);
|
logger.trace("Ignored because not matching any filter: " + resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
catch (Throwable ex) {
|
||||||
if (traceEnabled) {
|
throw new BeanDefinitionStoreException(
|
||||||
logger.trace("Ignored because not matching any filter: " + resource);
|
"Failed to read candidate component class: " + resource, ex);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -240,116 +228,6 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
||||||
}
|
}
|
||||||
return candidates;
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<BeanDefinition> findCandidateFactoryMethods(final BeanDefinitionHolder beanDefinitionHolder) {
|
|
||||||
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
|
|
||||||
AbstractBeanDefinition containingBeanDef = (AbstractBeanDefinition)beanDefinitionHolder.getBeanDefinition();
|
|
||||||
Resource resource = containingBeanDef.getResource();
|
|
||||||
boolean debugEnabled = logger.isDebugEnabled();
|
|
||||||
boolean traceEnabled = logger.isTraceEnabled();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (resource.isReadable()) {
|
|
||||||
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
|
|
||||||
Set<MethodMetadata> factoryMethodMetadataSet = metadataReader.getAnnotationMetadata().getAnnotatedMethods("org.springframework.beans.factory.annotation.FactoryMethod");
|
|
||||||
for (MethodMetadata methodMetadata : factoryMethodMetadataSet) {
|
|
||||||
if (isCandidateFactoryMethod(methodMetadata)) {
|
|
||||||
ScannedGenericBeanDefinition factoryBeanDef = new ScannedGenericBeanDefinition(metadataReader);
|
|
||||||
|
|
||||||
if (!methodMetadata.isStatic()) {
|
|
||||||
factoryBeanDef.setFactoryBeanName(beanDefinitionHolder.getBeanName());
|
|
||||||
}
|
|
||||||
factoryBeanDef.setFactoryMethodName(methodMetadata.getMethodName());
|
|
||||||
|
|
||||||
addQualifierToFactoryMethodBeanDefinition(methodMetadata, factoryBeanDef);
|
|
||||||
addScopeToFactoryMethodBeanDefinition(containingBeanDef, methodMetadata, factoryBeanDef);
|
|
||||||
|
|
||||||
factoryBeanDef.setResource(containingBeanDef.getResource());
|
|
||||||
factoryBeanDef.setSource(containingBeanDef.getSource());
|
|
||||||
|
|
||||||
if (debugEnabled) {
|
|
||||||
logger.debug("Identified candidate factory method in class: " + resource);
|
|
||||||
}
|
|
||||||
candidates.add(factoryBeanDef);
|
|
||||||
|
|
||||||
RootBeanDefinition scopedFactoryBeanDef = null;
|
|
||||||
if (methodMetadata.hasAnnotation(SCOPEDPROXY_CLASS_NAME)) {
|
|
||||||
//TODO validate that @ScopedProxy isn't applied to singleton/prototype beans.
|
|
||||||
Map<String, Object> attributes = methodMetadata.getAnnotationAttributes(SCOPEDPROXY_CLASS_NAME);
|
|
||||||
scopedFactoryBeanDef = new RootBeanDefinition(ScopedProxyFactoryBean.class);
|
|
||||||
String t= scopedFactoryBeanDef.getBeanClassName();
|
|
||||||
String targetBeanName = createFactoryBeanName(beanDefinitionHolder.getBeanName(), factoryBeanDef.getFactoryMethodName());
|
|
||||||
scopedFactoryBeanDef.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
|
|
||||||
|
|
||||||
//TODO handle cglib options
|
|
||||||
// scopedFactoryBeanDef.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
|
|
||||||
scopedFactoryBeanDef.setAutowireCandidate(false);
|
|
||||||
scopedFactoryBeanDef.setResource(containingBeanDef.getResource());
|
|
||||||
scopedFactoryBeanDef.setSource(containingBeanDef.getSource());
|
|
||||||
|
|
||||||
candidates.add(scopedFactoryBeanDef);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (traceEnabled) {
|
|
||||||
logger.trace("Ignored because not matching any filter: " + resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return candidates;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void addScopeToFactoryMethodBeanDefinition(
|
|
||||||
AbstractBeanDefinition containingBeanDefinition,
|
|
||||||
MethodMetadata factoryMethodMetadata,
|
|
||||||
ScannedGenericBeanDefinition factoryBeanDefinition) {
|
|
||||||
if (factoryMethodMetadata.hasAnnotation(SCOPE_CLASS_NAME)) {
|
|
||||||
Map<String, Object> attributes = factoryMethodMetadata.getAnnotationAttributes(SCOPE_CLASS_NAME);
|
|
||||||
factoryBeanDefinition.setScope(attributes.get("value").toString());
|
|
||||||
} else {
|
|
||||||
factoryBeanDefinition.setScope(containingBeanDefinition.getScope());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected void addQualifierToFactoryMethodBeanDefinition(MethodMetadata methodMetadata,
|
|
||||||
ScannedGenericBeanDefinition beanDef) {
|
|
||||||
//Add qualifiers to bean definition
|
|
||||||
if (methodMetadata.hasAnnotation(QUALIFIER_CLASS_NAME))
|
|
||||||
{
|
|
||||||
Map<String, Object> attributes = methodMetadata.getAnnotationAttributes(QUALIFIER_CLASS_NAME);
|
|
||||||
beanDef.addQualifier(new AutowireCandidateQualifier(Qualifier.class, attributes.get("value")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodMetadata.hasMetaAnnotation(QUALIFIER_CLASS_NAME))
|
|
||||||
{
|
|
||||||
//Need the attribute that has a qualifier meta-annotation.
|
|
||||||
Set<String> annotationTypes = methodMetadata.getAnnotationTypesWithMetaAnnotation(QUALIFIER_CLASS_NAME);
|
|
||||||
if (annotationTypes.size() == 1)
|
|
||||||
{
|
|
||||||
String annotationType = annotationTypes.iterator().next();
|
|
||||||
Map<String, Object> attributes = methodMetadata.getAnnotationAttributes(annotationType);
|
|
||||||
beanDef.addQualifier(new AutowireCandidateQualifier(annotationType, attributes.get("value")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isCandidateFactoryMethod(MethodMetadata methodMetadata) {
|
|
||||||
|
|
||||||
//TODO decide if we can support generic wildcard return types, parameter-less method and put in appropriate checks
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -395,11 +273,4 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
||||||
return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
|
return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String createFactoryBeanName(String configurationComponentBeanName, String factoryMethodName) {
|
|
||||||
//TODO consider adding hex string and passing in definition object.
|
|
||||||
String beanName = configurationComponentBeanName + "$" + factoryMethodName;
|
|
||||||
return beanName;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -34,6 +34,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||||
import org.springframework.core.type.filter.AspectJTypeFilter;
|
import org.springframework.core.type.filter.AspectJTypeFilter;
|
||||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||||
|
@ -75,8 +76,8 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
|
|
||||||
|
|
||||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||||
String[] basePackages =
|
String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
|
||||||
StringUtils.commaDelimitedListToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE));
|
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||||
|
|
||||||
// Actually scan for bean definitions and register them.
|
// Actually scan for bean definitions and register them.
|
||||||
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
|
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
|
||||||
|
|
|
@ -57,7 +57,7 @@ import org.springframework.stereotype.Component;
|
||||||
* @see Value
|
* @see Value
|
||||||
* @see ConfigurationClassPostProcessor;
|
* @see ConfigurationClassPostProcessor;
|
||||||
*/
|
*/
|
||||||
@Target( { ElementType.TYPE })
|
@Target({ElementType.TYPE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@Documented
|
@Documented
|
||||||
|
|
|
@ -16,44 +16,99 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import static java.lang.String.*;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanMetadataElement;
|
||||||
|
import org.springframework.beans.factory.parsing.Location;
|
||||||
import org.springframework.beans.factory.parsing.Problem;
|
import org.springframework.beans.factory.parsing.Problem;
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a user-defined {@link Configuration @Configuration} class.
|
* Represents a user-defined {@link Configuration @Configuration} class.
|
||||||
* Includes a set of {@link Bean} methods, including all such methods defined in the
|
* Includes a set of {@link Bean} methods, including all such methods defined in the
|
||||||
* ancestry of the class, in a 'flattened-out' manner. Note that each {@link BeanMethod}
|
* ancestry of the class, in a 'flattened-out' manner. Note that each {@link ConfigurationClassMethod}
|
||||||
* representation contains source information about where it was originally detected
|
* representation contains source information about where it was originally detected
|
||||||
* (for the purpose of tooling with Spring IDE).
|
* (for the purpose of tooling with Spring IDE).
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @see ConfigurationModel
|
* @author Juergen Hoeller
|
||||||
* @see BeanMethod
|
* @since 3.0
|
||||||
|
* @see ConfigurationClassMethod
|
||||||
* @see ConfigurationClassParser
|
* @see ConfigurationClassParser
|
||||||
*/
|
*/
|
||||||
final class ConfigurationClass extends ModelClass {
|
final class ConfigurationClass implements BeanMetadataElement {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private transient Object source;
|
||||||
|
|
||||||
private String beanName;
|
private String beanName;
|
||||||
|
|
||||||
private int modifiers;
|
private int modifiers;
|
||||||
private HashSet<Annotation> annotations = new HashSet<Annotation>();
|
|
||||||
private HashSet<BeanMethod> methods = new HashSet<BeanMethod>();
|
private Set<Annotation> annotations = new HashSet<Annotation>();
|
||||||
|
|
||||||
|
private Set<ConfigurationClassMethod> methods = new HashSet<ConfigurationClassMethod>();
|
||||||
|
|
||||||
private ConfigurationClass declaringClass;
|
private ConfigurationClass declaringClass;
|
||||||
|
|
||||||
public String getBeanName() {
|
|
||||||
return beanName == null ? getName() : beanName;
|
/**
|
||||||
|
* Returns the fully-qualified name of this class.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBeanName(String id) {
|
/**
|
||||||
this.beanName = id;
|
* 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 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBeanName() {
|
||||||
|
return beanName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBeanName(String beanName) {
|
||||||
|
this.beanName = beanName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getModifiers() {
|
public int getModifiers() {
|
||||||
|
@ -76,10 +131,11 @@ final class ConfigurationClass extends ModelClass {
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <A extends Annotation> A getAnnotation(Class<A> annoType) {
|
public <A extends Annotation> A getAnnotation(Class<A> annoType) {
|
||||||
for (Annotation annotation : annotations)
|
for (Annotation annotation : annotations) {
|
||||||
if(annotation.annotationType().equals(annoType))
|
if (annotation.annotationType().equals(annoType)) {
|
||||||
return (A) annotation;
|
return (A) annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,19 +146,18 @@ final class ConfigurationClass extends ModelClass {
|
||||||
*/
|
*/
|
||||||
public <A extends Annotation> A getRequiredAnnotation(Class<A> annoType) {
|
public <A extends Annotation> A getRequiredAnnotation(Class<A> annoType) {
|
||||||
A anno = getAnnotation(annoType);
|
A anno = getAnnotation(annoType);
|
||||||
|
if (anno == null) {
|
||||||
if(anno == null)
|
|
||||||
throw new IllegalStateException(
|
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;
|
return anno;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<BeanMethod> getBeanMethods() {
|
public Set<ConfigurationClassMethod> getBeanMethods() {
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationClass addBeanMethod(BeanMethod method) {
|
public ConfigurationClass addMethod(ConfigurationClassMethod method) {
|
||||||
method.setDeclaringClass(this);
|
method.setDeclaringClass(this);
|
||||||
methods.add(method);
|
methods.add(method);
|
||||||
return this;
|
return this;
|
||||||
|
@ -117,93 +172,24 @@ final class ConfigurationClass extends ModelClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(ProblemReporter problemReporter) {
|
public void validate(ProblemReporter problemReporter) {
|
||||||
// configuration classes must be annotated with @Configuration
|
|
||||||
if (getAnnotation(Configuration.class) == null)
|
|
||||||
problemReporter.error(new NonAnnotatedConfigurationProblem());
|
|
||||||
|
|
||||||
// a configuration class may not be final (CGLIB limitation)
|
// a configuration class may not be final (CGLIB limitation)
|
||||||
if (Modifier.isFinal(modifiers))
|
if (getAnnotation(Configuration.class) != null) {
|
||||||
problemReporter.error(new FinalConfigurationProblem());
|
if (Modifier.isFinal(modifiers)) {
|
||||||
|
problemReporter.error(new FinalConfigurationProblem());
|
||||||
for (BeanMethod method : methods)
|
}
|
||||||
method.validate(problemReporter);
|
for (ConfigurationClassMethod method : methods) {
|
||||||
}
|
method.validate(problemReporter);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return format("%s; modifiers=%d; methods=%s", super.toString(), modifiers, methods);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = super.hashCode();
|
|
||||||
result = prime * result
|
|
||||||
+ ((annotations == null) ? 0 : annotations.hashCode());
|
|
||||||
result = prime * result
|
|
||||||
+ ((beanName == null) ? 0 : beanName.hashCode());
|
|
||||||
result = prime * result
|
|
||||||
+ ((declaringClass == null) ? 0 : declaringClass.hashCode());
|
|
||||||
result = prime * result + ((methods == null) ? 0 : methods.hashCode());
|
|
||||||
result = prime * result + modifiers;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (!super.equals(obj))
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
ConfigurationClass other = (ConfigurationClass) obj;
|
|
||||||
if (annotations == null) {
|
|
||||||
if (other.annotations != null)
|
|
||||||
return false;
|
|
||||||
} else if (!annotations.equals(other.annotations))
|
|
||||||
return false;
|
|
||||||
if (beanName == null) {
|
|
||||||
if (other.beanName != null)
|
|
||||||
return false;
|
|
||||||
} else if (!beanName.equals(other.beanName))
|
|
||||||
return false;
|
|
||||||
if (declaringClass == null) {
|
|
||||||
if (other.declaringClass != null)
|
|
||||||
return false;
|
|
||||||
} else if (!declaringClass.equals(other.declaringClass))
|
|
||||||
return false;
|
|
||||||
if (methods == null) {
|
|
||||||
if (other.methods != null)
|
|
||||||
return false;
|
|
||||||
} else if (!methods.equals(other.methods))
|
|
||||||
return false;
|
|
||||||
if (modifiers != other.modifiers)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Configuration classes must be annotated with {@link Configuration @Configuration}. */
|
|
||||||
class NonAnnotatedConfigurationProblem extends Problem {
|
|
||||||
|
|
||||||
NonAnnotatedConfigurationProblem() {
|
|
||||||
super(format("%s was specified as a @Configuration class but was not actually annotated " +
|
|
||||||
"with @Configuration. Annotate the class or do not attempt to process it.",
|
|
||||||
getSimpleName()),
|
|
||||||
ConfigurationClass.this.getLocation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Configuration classes must be non-final to accommodate CGLIB subclassing. */
|
/** Configuration classes must be non-final to accommodate CGLIB subclassing. */
|
||||||
class FinalConfigurationProblem extends Problem {
|
private class FinalConfigurationProblem extends Problem {
|
||||||
|
|
||||||
FinalConfigurationProblem() {
|
public FinalConfigurationProblem() {
|
||||||
super(format("@Configuration class [%s] may not be final. Remove the final modifier to continue.",
|
super(String.format("@Configuration class [%s] may not be final. Remove the final modifier to continue.",
|
||||||
getSimpleName()),
|
getSimpleName()), ConfigurationClass.this.getLocation());
|
||||||
ConfigurationClass.this.getLocation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,24 +16,26 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface used when dynamically creating mutable instances of annotations associated
|
* Interface used when dynamically creating mutable instances of annotations associated
|
||||||
* with {@link Configuration} class processing. This functionality is necessary given
|
* with {@link Configuration} class processing. This functionality is necessary given
|
||||||
* that parsing of Configuration classes is done with ASM. Annotation metadata (including
|
* that parsing of Configuration classes is done with ASM. Annotation metadata (including
|
||||||
* attributes) is parsed from the classfiles, and instances of those annotations are
|
* attributes) is parsed from the class files, and instances of those annotations are
|
||||||
* then created using this interface and its associated utilities. The annotation
|
* then created using this interface and its associated utilities. The annotation
|
||||||
* instances are attached to the {@link ConfigurationModel} objects at runtime, namely
|
* instances are attached to the configuration model objects at runtime, namely
|
||||||
* {@link BeanMethod}. This approach is better than the alternative of creating fine-grained
|
* {@link ConfigurationClassMethod}. This approach is better than the alternative of
|
||||||
* model representations of all annotations and attributes. It is better to simply attach
|
* creating fine-grained model representations of all annotations and attributes.
|
||||||
* annotation instances and read them as needed.
|
* It is better to simply attach annotation instances and read them as needed.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @see MutableAnnotationVisitor
|
* @author Juergen Hoeller
|
||||||
* @see MutableAnnotationInvocationHandler
|
* @since 3.0
|
||||||
* @see AsmUtils#createMutableAnnotation
|
* @see ConfigurationClassAnnotationVisitor
|
||||||
|
* @see ConfigurationClassReaderUtils#createMutableAnnotation
|
||||||
*/
|
*/
|
||||||
interface MutableAnnotation {
|
interface ConfigurationClassAnnotation extends Annotation {
|
||||||
|
|
||||||
void setAttributeValue(String attribName, Object attribValue);
|
void setAttributeValue(String attribName, Object attribValue);
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
|
import org.springframework.asm.Type;
|
||||||
|
import org.springframework.asm.commons.EmptyVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASM {@link AnnotationVisitor} that populates a given {@link ConfigurationClassAnnotation} instance
|
||||||
|
* with its attributes.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
* @see ConfigurationClassAnnotation
|
||||||
|
* @see ConfigurationClassReaderUtils#createMutableAnnotation
|
||||||
|
*/
|
||||||
|
class ConfigurationClassAnnotationVisitor implements AnnotationVisitor {
|
||||||
|
|
||||||
|
protected final ConfigurationClassAnnotation mutableAnno;
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ConfigurationClassAnnotationVisitor} instance that will populate the the
|
||||||
|
* attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation} instead of
|
||||||
|
* {@link ConfigurationClassAnnotation} to avoid the need for callers to typecast.
|
||||||
|
* @param mutableAnno {@link ConfigurationClassAnnotation} instance to visit and populate
|
||||||
|
* @see ConfigurationClassReaderUtils#createMutableAnnotation
|
||||||
|
*/
|
||||||
|
public ConfigurationClassAnnotationVisitor(ConfigurationClassAnnotation mutableAnno, ClassLoader classLoader) {
|
||||||
|
this.mutableAnno = mutableAnno;
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visit(String attribName, Object attribValue) {
|
||||||
|
Class<?> attribReturnType = mutableAnno.getAttributeType(attribName);
|
||||||
|
|
||||||
|
if (attribReturnType.equals(Class.class)) {
|
||||||
|
// the attribute type is Class -> load it and set it.
|
||||||
|
String fqClassName = ((Type) attribValue).getClassName();
|
||||||
|
Class<?> classVal = ConfigurationClassReaderUtils.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 = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(enumTypeDescriptor);
|
||||||
|
Class<? extends Enum> enumClass = ConfigurationClassReaderUtils.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 = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(attribAnnoTypeDesc);
|
||||||
|
Class<? extends Annotation> annoType = ConfigurationClassReaderUtils.loadToolingSafeClass(annoTypeName, classLoader);
|
||||||
|
if (annoType == null) {
|
||||||
|
return new EmptyVisitor();
|
||||||
|
}
|
||||||
|
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoType, classLoader);
|
||||||
|
try {
|
||||||
|
Field attribute = mutableAnno.getClass().getField(attribName);
|
||||||
|
attribute.set(mutableAnno, anno);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new IllegalStateException("Could not reflectively set annotation field", ex);
|
||||||
|
}
|
||||||
|
return new ConfigurationClassAnnotationVisitor(anno, classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationVisitor visitArray(final String attribName) {
|
||||||
|
return new MutableAnnotationArrayVisitor(mutableAnno, attribName, classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitEnd() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASM {@link AnnotationVisitor} that visits any annotation array values while populating
|
||||||
|
* a new {@link ConfigurationClassAnnotation} instance.
|
||||||
|
*/
|
||||||
|
private static class MutableAnnotationArrayVisitor implements AnnotationVisitor {
|
||||||
|
|
||||||
|
private final List<Object> values = new ArrayList<Object>();
|
||||||
|
|
||||||
|
private final ConfigurationClassAnnotation mutableAnno;
|
||||||
|
|
||||||
|
private final String attribName;
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
|
||||||
|
public MutableAnnotationArrayVisitor(ConfigurationClassAnnotation mutableAnno, String attribName, ClassLoader classLoader) {
|
||||||
|
this.mutableAnno = mutableAnno;
|
||||||
|
this.attribName = attribName;
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void visit(String na, Object value) {
|
||||||
|
values.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitEnum(String s, String s1, String s2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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<ConfigurationClass> 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<String> names = new ArrayList<String>(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 {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,60 +16,59 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
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.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
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.Callback;
|
||||||
import net.sf.cglib.proxy.CallbackFilter;
|
import net.sf.cglib.proxy.CallbackFilter;
|
||||||
import net.sf.cglib.proxy.Enhancer;
|
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 net.sf.cglib.proxy.NoOp;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.asm.ClassAdapter;
|
|
||||||
import org.springframework.asm.ClassReader;
|
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||||
import org.springframework.asm.ClassWriter;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enhances {@link Configuration} classes by generating a CGLIB subclass capable of
|
* Enhances {@link Configuration} classes by generating a CGLIB subclass capable of
|
||||||
* interacting with the Spring container to respect bean semantics.
|
* interacting with the Spring container to respect bean semantics.
|
||||||
*
|
*
|
||||||
* @see #enhance(String)
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
* @see #enhance
|
||||||
* @see ConfigurationClassPostProcessor
|
* @see ConfigurationClassPostProcessor
|
||||||
*/
|
*/
|
||||||
class ConfigurationClassEnhancer {
|
class ConfigurationClassEnhancer {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(ConfigurationClassEnhancer.class);
|
private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
|
||||||
|
|
||||||
|
private final List<Callback> callbackInstances = new ArrayList<Callback>();
|
||||||
|
|
||||||
|
private final List<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
|
||||||
|
|
||||||
private final ArrayList<Callback> callbackInstances = new ArrayList<Callback>();
|
|
||||||
private final ArrayList<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
|
|
||||||
private final CallbackFilter callbackFilter;
|
private final CallbackFilter callbackFilter;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ConfigurationClassEnhancer} instance.
|
* Creates a new {@link ConfigurationClassEnhancer} instance.
|
||||||
*/
|
*/
|
||||||
public ConfigurationClassEnhancer(DefaultListableBeanFactory beanFactory) {
|
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
|
||||||
Assert.notNull(beanFactory, "beanFactory must be non-null");
|
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||||
|
|
||||||
callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
||||||
callbackInstances.add(NoOp.INSTANCE);
|
callbackInstances.add(NoOp.INSTANCE);
|
||||||
|
|
||||||
for (Callback callback : callbackInstances)
|
for (Callback callback : callbackInstances) {
|
||||||
callbackTypes.add(callback.getClass());
|
callbackTypes.add(callback.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
// set up the callback filter to return the index of the BeanMethodInterceptor when
|
// Set up the callback filter to return the index of the BeanMethodInterceptor when
|
||||||
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
||||||
callbackFilter = new CallbackFilter() {
|
callbackFilter = new CallbackFilter() {
|
||||||
public int accept(Method candidateMethod) {
|
public int accept(Method candidateMethod) {
|
||||||
|
@ -82,24 +81,18 @@ class ConfigurationClassEnhancer {
|
||||||
/**
|
/**
|
||||||
* Loads the specified class and generates a CGLIB subclass of it equipped with
|
* Loads the specified class and generates a CGLIB subclass of it equipped with
|
||||||
* container-aware callbacks capable of respecting scoping and other bean semantics.
|
* container-aware callbacks capable of respecting scoping and other bean semantics.
|
||||||
*
|
|
||||||
* @return fully-qualified name of the enhanced subclass
|
* @return fully-qualified name of the enhanced subclass
|
||||||
*/
|
*/
|
||||||
public String enhance(String configClassName) {
|
public Class enhance(Class configClass) {
|
||||||
if (log.isInfoEnabled())
|
if (logger.isInfoEnabled()) {
|
||||||
log.info("Enhancing " + configClassName);
|
logger.info("Enhancing " + configClass.getName());
|
||||||
|
}
|
||||||
Class<?> superclass = loadRequiredClass(configClassName);
|
Class<?> enhancedClass = createClass(newEnhancer(configClass));
|
||||||
|
if (logger.isInfoEnabled()) {
|
||||||
Class<?> subclass = createClass(newEnhancer(superclass), superclass);
|
logger.info(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
||||||
|
configClass.getName(), enhancedClass.getName()));
|
||||||
subclass = nestOneClassDeeperIfAspect(superclass, subclass);
|
}
|
||||||
|
return enhancedClass;
|
||||||
if (log.isInfoEnabled())
|
|
||||||
log.info(format("Successfully enhanced %s; enhanced class name is: %s",
|
|
||||||
configClassName, subclass.getName()));
|
|
||||||
|
|
||||||
return subclass.getName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,7 +109,7 @@ class ConfigurationClassEnhancer {
|
||||||
enhancer.setSuperclass(superclass);
|
enhancer.setSuperclass(superclass);
|
||||||
enhancer.setUseFactory(false);
|
enhancer.setUseFactory(false);
|
||||||
enhancer.setCallbackFilter(callbackFilter);
|
enhancer.setCallbackFilter(callbackFilter);
|
||||||
enhancer.setCallbackTypes(callbackTypes.toArray(new Class<?>[] {}));
|
enhancer.setCallbackTypes(callbackTypes.toArray(new Class[callbackTypes.size()]));
|
||||||
|
|
||||||
return enhancer;
|
return enhancer;
|
||||||
}
|
}
|
||||||
|
@ -125,53 +118,86 @@ class ConfigurationClassEnhancer {
|
||||||
* Uses enhancer to generate a subclass of superclass, ensuring that
|
* Uses enhancer to generate a subclass of superclass, ensuring that
|
||||||
* {@link #callbackInstances} are registered for the new subclass.
|
* {@link #callbackInstances} are registered for the new subclass.
|
||||||
*/
|
*/
|
||||||
private Class<?> createClass(Enhancer enhancer, Class<?> superclass) {
|
private Class<?> createClass(Enhancer enhancer) {
|
||||||
Class<?> subclass = enhancer.createClass();
|
Class<?> subclass = enhancer.createClass();
|
||||||
|
Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[callbackInstances.size()]));
|
||||||
Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[] {}));
|
|
||||||
|
|
||||||
return subclass;
|
return subclass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Works around a constraint imposed by the AspectJ 5 annotation-style programming
|
* Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper
|
||||||
* model. See comments inline for detail.
|
* handling of bean semantics such as scoping and AOP proxying.
|
||||||
*
|
* @author Chris Beams
|
||||||
* @return original subclass instance unless superclass is annnotated with @Aspect, in
|
* @see Bean
|
||||||
* which case a subclass of the subclass is returned
|
* @see ConfigurationClassEnhancer
|
||||||
*/
|
*/
|
||||||
private Class<?> nestOneClassDeeperIfAspect(Class<?> superclass, Class<?> origSubclass) {
|
private static class BeanMethodInterceptor implements MethodInterceptor {
|
||||||
boolean superclassIsAnAspect = false;
|
|
||||||
|
|
||||||
// check for @Aspect by name rather than by class literal to avoid
|
private static final Log logger = LogFactory.getLog(BeanMethodInterceptor.class);
|
||||||
// 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)
|
private final ConfigurableBeanFactory beanFactory;
|
||||||
return origSubclass;
|
|
||||||
|
|
||||||
// the superclass is annotated with AspectJ's @Aspect.
|
public BeanMethodInterceptor(ConfigurableBeanFactory beanFactory) {
|
||||||
// this means that we must create a subclass of the subclass
|
this.beanFactory = beanFactory;
|
||||||
// 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
|
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
|
||||||
protected byte[] transform(byte[] b) throws Exception {
|
* existence of this bean object.
|
||||||
ClassWriter writer = new ClassWriter(false);
|
*/
|
||||||
ClassAdapter adapter = new AddAnnotationAdapter(writer, "Lorg/aspectj/lang/annotation/Aspect;");
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
ClassReader reader = new ClassReader(b);
|
// by default the bean name is the name of the @Bean-annotated method
|
||||||
reader.accept(adapter, false);
|
String beanName = method.getName();
|
||||||
return writer.toByteArray();
|
|
||||||
|
// 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
|
// determine whether this bean is a scoped-proxy
|
||||||
Class<?> newSubclass = createClass(enhancer, origSubclass);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 <var>beanName</var> already
|
||||||
|
* exists. Accounts for the fact that the requested bean may be "in creation", i.e.:
|
||||||
|
* we're in the middle of servicing the initial request for this bean. From JavaConfig's
|
||||||
|
* perspective, this means that the bean does not actually yet exist, and that it is now
|
||||||
|
* our job to create it for the first time by executing the logic in the corresponding
|
||||||
|
* Bean method.
|
||||||
|
* <p>Said another way, this check repurposes
|
||||||
|
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
|
||||||
|
* the container is calling this method or the user is calling this method.
|
||||||
|
* @param beanName name of bean to check for
|
||||||
|
* @return true if <var>beanName</var> already exists in the factory
|
||||||
|
*/
|
||||||
|
private boolean factoryContainsBean(String beanName) {
|
||||||
|
return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName);
|
||||||
|
}
|
||||||
|
|
||||||
return newSubclass;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
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.annotation.Annotation;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
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.Problem;
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.util.Assert;
|
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.
|
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
* @see ConfigurationClass
|
* @see ConfigurationClass
|
||||||
* @see ConfigurationModel
|
|
||||||
* @see ConfigurationClassParser
|
* @see ConfigurationClassParser
|
||||||
* @see ConfigurationModelBeanDefinitionReader
|
* @see ConfigurationClassBeanDefinitionReader
|
||||||
*/
|
*/
|
||||||
final class BeanMethod implements BeanMetadataElement {
|
final class ConfigurationClassMethod implements BeanMetadataElement {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
private final int modifiers;
|
private final int modifiers;
|
||||||
private final ModelClass returnType;
|
|
||||||
|
private final ReturnType returnType;
|
||||||
|
|
||||||
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
||||||
|
|
||||||
private transient ConfigurationClass declaringClass;
|
private transient ConfigurationClass declaringClass;
|
||||||
|
|
||||||
private transient Object source;
|
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);
|
Assert.hasText(name);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
Assert.notNull(annotations);
|
Assert.notNull(annotations);
|
||||||
for (Annotation annotation : annotations)
|
for (Annotation annotation : annotations) {
|
||||||
this.annotations.add(annotation);
|
this.annotations.add(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers);
|
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers);
|
||||||
this.modifiers = modifiers;
|
this.modifiers = modifiers;
|
||||||
|
@ -68,7 +73,7 @@ final class BeanMethod implements BeanMetadataElement {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelClass getReturnType() {
|
public ReturnType getReturnType() {
|
||||||
return returnType;
|
return returnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,18 +105,17 @@ final class BeanMethod implements BeanMetadataElement {
|
||||||
*/
|
*/
|
||||||
public <T extends Annotation> T getRequiredAnnotation(Class<T> annoType) {
|
public <T extends Annotation> T getRequiredAnnotation(Class<T> annoType) {
|
||||||
T anno = getAnnotation(annoType);
|
T anno = getAnnotation(annoType);
|
||||||
|
if (anno == null) {
|
||||||
if(anno == null)
|
|
||||||
throw new IllegalStateException(
|
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;
|
return anno;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up a bi-directional relationship between this method and its declaring class.
|
* 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) {
|
public void setDeclaringClass(ConfigurationClass declaringClass) {
|
||||||
this.declaringClass = declaringClass;
|
this.declaringClass = declaringClass;
|
||||||
|
@ -130,97 +134,118 @@ final class BeanMethod implements BeanMetadataElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Location getLocation() {
|
public Location getLocation() {
|
||||||
if (declaringClass == null)
|
if (declaringClass == null) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"declaringClass property is null. Call setDeclaringClass() before calling getLocation()");
|
"declaringClass property is null. Call setDeclaringClass() before calling getLocation()");
|
||||||
|
}
|
||||||
return new Location(declaringClass.getLocation().getResource(), getSource());
|
return new Location(declaringClass.getLocation().getResource(), getSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(ProblemReporter problemReporter) {
|
public void validate(ProblemReporter problemReporter) {
|
||||||
|
if (Modifier.isPrivate(getModifiers())) {
|
||||||
if (Modifier.isPrivate(getModifiers()))
|
|
||||||
problemReporter.error(new PrivateMethodError());
|
problemReporter.error(new PrivateMethodError());
|
||||||
|
}
|
||||||
if (Modifier.isFinal(getModifiers()))
|
if (Modifier.isFinal(getModifiers())) {
|
||||||
problemReporter.error(new FinalMethodError());
|
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 ? "<unknown>" : 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** {@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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@link Bean} methods must be non-final in order to accommodate CGLIB. */
|
|
||||||
class FinalMethodError extends Problem {
|
static class ReturnType implements BeanMetadataElement {
|
||||||
FinalMethodError() {
|
|
||||||
super(format("Method '%s' may not be final; remove the final modifier to continue", getName()),
|
private String name;
|
||||||
BeanMethod.this.getLocation());
|
|
||||||
|
private boolean isInterface;
|
||||||
|
|
||||||
|
private transient Object source;
|
||||||
|
|
||||||
|
|
||||||
|
public ReturnType(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fully-qualified name of this class.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the fully-qualified name of this class.
|
||||||
|
*/
|
||||||
|
public void setName(String className) {
|
||||||
|
this.name = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'.
|
||||||
|
*/
|
||||||
|
public String getSimpleName() {
|
||||||
|
return name == null ? null : ClassUtils.getShortName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the class represented by this ModelClass instance is an interface.
|
||||||
|
*/
|
||||||
|
public boolean isInterface() {
|
||||||
|
return isInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signifies that this class is (true) or is not (false) an interface.
|
||||||
|
*/
|
||||||
|
public void setInterface(boolean isInterface) {
|
||||||
|
this.isInterface = isInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a resource path-formatted representation of the .java file that declares this
|
||||||
|
* class
|
||||||
|
*/
|
||||||
|
public Object getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the source location for this class. Must be a resource-path formatted string.
|
||||||
|
*
|
||||||
|
* @param source resource path to the .java file that declares this class.
|
||||||
|
*/
|
||||||
|
public void setSource(Object source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location getLocation() {
|
||||||
|
if (getName() == null) {
|
||||||
|
throw new IllegalStateException("'name' property is null. Call setName() before calling getLocation()");
|
||||||
|
}
|
||||||
|
return new Location(new ClassPathResource(ClassUtils.convertClassNameToResourcePath(getName())), getSource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Annotation> annotations = new ArrayList<Annotation>();
|
|
||||||
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 - <var>lineNo</var> represents the line number of the
|
|
||||||
* first instruction in this method. Method declaration line numbers are not in any way
|
|
||||||
* tracked in the bytecode. Any tooling or output that reads this value will have to
|
|
||||||
* compensate and estimate where the actual method declaration is.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitLineNumber(int lineNo, Label start) {
|
|
||||||
this.lineNumber = lineNo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses through all {@link #annotations} on this method in order to determine whether
|
|
||||||
* it is a {@link Bean} method and if so adds it to the enclosing {@link #configClass}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitEnd() {
|
|
||||||
for (Annotation anno : annotations) {
|
|
||||||
if (Bean.class.equals(anno.annotationType())) {
|
|
||||||
// this method is annotated with @Bean -> add it to the ConfigurationClass model
|
|
||||||
Annotation[] annoArray = annotations.toArray(new Annotation[] {});
|
|
||||||
BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray);
|
|
||||||
method.setSource(lineNumber);
|
|
||||||
configClass.addBeanMethod(method);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines return type from ASM <var>methodDescriptor</var> 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,75 +16,79 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
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.asm.ClassReader;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.util.ClassUtils;
|
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
|
* This ASM-based implementation avoids reflection and eager classloading in order to
|
||||||
* interoperate effectively with tooling (Spring IDE) and OSGi environments.
|
* interoperate effectively with tooling (Spring IDE) and OSGi environments.
|
||||||
* <p>
|
*
|
||||||
* This class helps separate the concern of parsing the structure of a Configuration class
|
* <p>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
|
* from the concern of registering {@link BeanDefinition} objects based on the content of
|
||||||
* that model.
|
* that model.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @see ConfigurationModel
|
* @author Juergen Hoeller
|
||||||
* @see ConfigurationModelBeanDefinitionReader
|
* @since 3.0
|
||||||
|
* @see ConfigurationClassBeanDefinitionReader
|
||||||
*/
|
*/
|
||||||
class ConfigurationClassParser {
|
class ConfigurationClassParser {
|
||||||
|
|
||||||
/**
|
private final Set<ConfigurationClass> model;
|
||||||
* Model to be populated during calls to {@link #parse(Object, String)}
|
|
||||||
*/
|
|
||||||
private final ConfigurationModel model;
|
|
||||||
private final ProblemReporter problemReporter;
|
private final ProblemReporter problemReporter;
|
||||||
|
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ConfigurationClassParser} instance that will be used to populate a
|
* Create a new {@link ConfigurationClassParser} instance that will be used to populate a
|
||||||
* {@link ConfigurationModel}.
|
* configuration model.
|
||||||
*
|
|
||||||
* @param model model to be populated by each successive call to {@link #parse}
|
* @param model model to be populated by each successive call to {@link #parse}
|
||||||
* @see #getConfigurationModel()
|
|
||||||
*/
|
*/
|
||||||
public ConfigurationClassParser(ProblemReporter problemReporter, ClassLoader classLoader) {
|
public ConfigurationClassParser(ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||||
this.model = new ConfigurationModel();
|
this.model = new LinkedHashSet<ConfigurationClass>();
|
||||||
this.problemReporter = problemReporter;
|
this.problemReporter = problemReporter;
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the {@link Configuration @Configuration} class encapsulated by
|
* Parse the specified {@link Configuration @Configuration} class.
|
||||||
* <var>configurationSource</var>.
|
* @param className the name of the class to parse
|
||||||
*
|
* @param beanName may be null, but if populated represents the bean id
|
||||||
* @param configurationSource reader for Configuration class being parsed
|
* (assumes that this configuration class was configured via XML)
|
||||||
* @param configurationId 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);
|
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
|
||||||
|
ClassReader configClassReader = ConfigurationClassReaderUtils.newAsmClassReader(ConfigurationClassReaderUtils.getClassAsStream(resourcePath, classLoader));
|
||||||
ClassReader configClassReader = newAsmClassReader(getClassAsStream(resourcePath, classLoader));
|
|
||||||
|
|
||||||
ConfigurationClass configClass = new ConfigurationClass();
|
ConfigurationClass configClass = new ConfigurationClass();
|
||||||
configClass.setBeanName(configurationId);
|
configClass.setBeanName(beanName);
|
||||||
|
|
||||||
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
||||||
model.add(configClass);
|
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() {
|
public void validate() {
|
||||||
return model;
|
for (ConfigurationClass configClass : this.model) {
|
||||||
|
configClass.validate(this.problemReporter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<ConfigurationClass> getModel() {
|
||||||
|
return this.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,28 +16,31 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import static java.lang.String.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
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.AbstractBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
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.Ordered;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
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.MetadataReader;
|
||||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
||||||
|
@ -53,129 +56,134 @@ import org.springframework.util.StringUtils;
|
||||||
* executes.
|
* executes.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class ConfigurationClassPostProcessor extends AbstractConfigurationClassProcessor
|
public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
|
||||||
implements Ordered, BeanFactoryPostProcessor {
|
|
||||||
|
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);
|
private static final Log logger = LogFactory.getLog(ConfigurationClassPostProcessor.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A well-known class in the CGLIB API used when testing to see if CGLIB
|
* Used to register any problems detected with {@link Configuration} or {@link Bean}
|
||||||
* is present on the classpath. Package-private visibility allows for
|
* declarations. For instance, a Bean method marked as {@literal final} is illegal
|
||||||
* manipulation by tests.
|
* and would be reported as a problem. Defaults to {@link FailFastProblemReporter},
|
||||||
* @see #assertCglibIsPresent(BeanDefinitionRegistry)
|
* but is overridable with {@link #setProblemReporter}
|
||||||
*/
|
*/
|
||||||
static String CGLIB_TEST_CLASS = "net.sf.cglib.proxy.Callback";
|
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
||||||
|
|
||||||
/**
|
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||||
* Holder for the calling BeanFactory
|
|
||||||
* @see #postProcessBeanFactory(ConfigurableListableBeanFactory)
|
|
||||||
*/
|
|
||||||
private DefaultListableBeanFactory beanFactory;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {@link Ordered#HIGHEST_PRECEDENCE}.
|
* Override the default {@link ProblemReporter}.
|
||||||
|
* @param problemReporter custom problem reporter
|
||||||
*/
|
*/
|
||||||
|
public void setProblemReporter(ProblemReporter problemReporter) {
|
||||||
|
this.problemReporter = problemReporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||||
|
this.beanClassLoader = beanClassLoader;
|
||||||
|
}
|
||||||
|
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return Ordered.HIGHEST_PRECEDENCE;
|
return Ordered.HIGHEST_PRECEDENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds {@link Configuration} bean definitions within <var>clBeanFactory</var>
|
* Prepare the Configuration classes for servicing bean requests at runtime
|
||||||
* and processes them in order to register bean definitions for each Bean method
|
* by replacing them with CGLIB-enhanced subclasses.
|
||||||
* 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 {
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||||
Assert.isInstanceOf(DefaultListableBeanFactory.class, clBeanFactory);
|
if (!(beanFactory instanceof BeanDefinitionRegistry)) {
|
||||||
beanFactory = (DefaultListableBeanFactory) clBeanFactory;
|
throw new IllegalStateException(
|
||||||
|
"ConfigurationClassPostProcessor expects a BeanFactory that implements BeanDefinitionRegistry");
|
||||||
|
}
|
||||||
|
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
|
||||||
|
enhanceConfigurationClasses(beanFactory);
|
||||||
|
}
|
||||||
|
|
||||||
BeanDefinitionRegistry factoryBeanDefs = processConfigBeanDefinitions();
|
|
||||||
|
|
||||||
for(String beanName : factoryBeanDefs.getBeanDefinitionNames())
|
/**
|
||||||
beanFactory.registerBeanDefinition(beanName, factoryBeanDefs.getBeanDefinition(beanName));
|
* Build and validate a configuration model based on the registry of
|
||||||
|
* {@link Configuration} classes.
|
||||||
|
*/
|
||||||
|
protected final void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
||||||
|
Set<BeanDefinitionHolder> configBeanDefs = new LinkedHashSet<BeanDefinitionHolder>();
|
||||||
|
for (String beanName : registry.getBeanDefinitionNames()) {
|
||||||
|
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
||||||
|
if (checkConfigurationClassBeanDefinition(beanDef)) {
|
||||||
|
configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enhanceConfigurationClasses();
|
// Return immediately if no @Configuration classes were found
|
||||||
|
if (configBeanDefs.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// Read the model and create bean definitions based on its content
|
||||||
|
new ConfigurationClassBeanDefinitionReader(registry).loadBeanDefinitions(parser.getModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a ConfigurationParser that uses the enclosing BeanFactory's
|
* Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
|
||||||
* ClassLoader to load all Configuration class artifacts.
|
* any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
|
||||||
|
* Candidate status is determined by BeanDefinition attribute metadata.
|
||||||
|
* @see ConfigurationClassEnhancer
|
||||||
*/
|
*/
|
||||||
@Override
|
private void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
|
||||||
protected ConfigurationClassParser createConfigurationParser() {
|
Set<BeanDefinitionHolder> configBeanDefs = new LinkedHashSet<BeanDefinitionHolder>();
|
||||||
return new ConfigurationClassParser(this.getProblemReporter(), beanFactory.getBeanClassLoader());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return map of all non-abstract {@link BeanDefinition}s in the
|
|
||||||
* enclosing {@link #beanFactory}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs) {
|
|
||||||
|
|
||||||
BeanDefinitionRegistry configBeanDefs = new DefaultListableBeanFactory();
|
|
||||||
|
|
||||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||||
|
if ("full".equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE))) {
|
||||||
if (beanDef.isAbstract() && !includeAbstractBeanDefs)
|
configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
|
||||||
continue;
|
}
|
||||||
|
|
||||||
if (isConfigurationClassBeanDefinition(beanDef, beanFactory.getBeanClassLoader()))
|
|
||||||
configBeanDefs.registerBeanDefinition(beanName, beanDef);
|
|
||||||
}
|
}
|
||||||
|
if (configBeanDefs.isEmpty()) {
|
||||||
return configBeanDefs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
// nothing to enhance -> return immediately
|
// nothing to enhance -> return immediately
|
||||||
return;
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
if (!cglibAvailable) {
|
||||||
|
Set<String> beanNames = new LinkedHashSet<String>();
|
||||||
/**
|
for (BeanDefinitionHolder holder : configBeanDefs) {
|
||||||
* Tests for the presence of CGLIB on the classpath by trying to
|
beanNames.add(holder.getBeanName());
|
||||||
* 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) {
|
|
||||||
throw new IllegalStateException("CGLIB is required to process @Configuration classes. " +
|
throw new IllegalStateException("CGLIB is required to process @Configuration classes. " +
|
||||||
"Either add CGLIB to the classpath or remove the following @Configuration bean definitions: [" +
|
"Either add CGLIB to the classpath or remove the following @Configuration bean definitions: " +
|
||||||
StringUtils.arrayToCommaDelimitedString(configBeanDefs.getBeanDefinitionNames()) + "]");
|
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
|
* @return whether the BeanDefinition's beanClass (or its ancestry) is
|
||||||
* {@link Configuration}-annotated, false if no beanClass is specified.
|
* {@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
|
// accommodating SPR-5655
|
||||||
Assert.isInstanceOf(AbstractBeanDefinition.class, beanDef);
|
if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
|
||||||
if(((AbstractBeanDefinition) beanDef).hasBeanClass())
|
if (AnnotationUtils.findAnnotation(
|
||||||
return AnnotationUtils.findAnnotation(
|
((AbstractBeanDefinition) beanDef).getBeanClass(), Configuration.class) != null) {
|
||||||
((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();
|
String className = beanDef.getBeanClassName();
|
||||||
|
|
||||||
while (className != null && !(className.equals(Object.class.getName()))) {
|
while (className != null && !(className.equals(Object.class.getName()))) {
|
||||||
try {
|
try {
|
||||||
MetadataReader metadataReader =
|
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
|
||||||
new SimpleMetadataReaderFactory(classLoader).getMetadataReader(className);
|
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
|
||||||
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
|
if (metadata.hasAnnotation(Configuration.class.getName())) {
|
||||||
ClassMetadata classMetadata = metadataReader.getClassMetadata();
|
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "full");
|
||||||
|
|
||||||
if (annotationMetadata.hasAnnotation(Configuration.class.getName()))
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
className = classMetadata.getSuperClassName();
|
if (metadata.hasAnnotation(Component.class.getName())) {
|
||||||
} catch (IOException ex) {
|
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "lite");
|
||||||
throw new RuntimeException(ex);
|
return true;
|
||||||
|
}
|
||||||
|
className = metadata.getSuperClassName();
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new BeanDefinitionStoreException("Failed to load class file [" + className + "]", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 <var>pathToClass</var>. 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 <var>is</var>, 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 <var>pathToClass</var>. 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 <var>pathToClass</var>
|
||||||
|
* @throws RuntimeException if <var>pathToClass</var> 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.
|
||||||
|
* <p>
|
||||||
|
* Because {@link ClassNotFoundException} is compensated for by returning null, callers
|
||||||
|
* must take care to handle the null case appropriately.
|
||||||
|
* <p>
|
||||||
|
* In cases where the WARN logging statement is not desired, use the
|
||||||
|
* {@link #loadClass(String)} method, which returns null but issues no logging
|
||||||
|
* statements.
|
||||||
|
* <p>
|
||||||
|
* 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 <T> 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 <T> 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<String, Object> attributes = new HashMap<String, Object>();
|
||||||
|
private final Map<String, Class<?>> attributeTypes = new HashMap<String, Class<?>>();
|
||||||
|
|
||||||
|
public MutableAnnotationInvocationHandler(Class<? extends Annotation> annoType) {
|
||||||
|
// pre-populate the attributes hash will all the names
|
||||||
|
// and default values of the attributes defined in 'annoType'
|
||||||
|
Method[] attribs = annoType.getDeclaredMethods();
|
||||||
|
for (Method attrib : attribs) {
|
||||||
|
this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName()));
|
||||||
|
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.annoType = annoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
Assert.isInstanceOf(Annotation.class, proxy);
|
||||||
|
|
||||||
|
String methodName = method.getName();
|
||||||
|
|
||||||
|
// first -> check to see if this method is an attribute on our annotation
|
||||||
|
if (attributes.containsKey(methodName))
|
||||||
|
return attributes.get(methodName);
|
||||||
|
|
||||||
|
|
||||||
|
// second -> is it a method from java.lang.annotation.Annotation?
|
||||||
|
if (methodName.equals("annotationType"))
|
||||||
|
return annoType;
|
||||||
|
|
||||||
|
|
||||||
|
// third -> is it a method from java.lang.Object?
|
||||||
|
if (methodName.equals("toString"))
|
||||||
|
return format("@%s(%s)", annoType.getName(), getAttribs());
|
||||||
|
|
||||||
|
if (methodName.equals("equals"))
|
||||||
|
return isEqualTo(proxy, args[0]);
|
||||||
|
|
||||||
|
if (methodName.equals("hashCode"))
|
||||||
|
return calculateHashCode(proxy);
|
||||||
|
|
||||||
|
|
||||||
|
// finally -> is it a method specified by MutableAnno?
|
||||||
|
if (methodName.equals("setAttributeValue")) {
|
||||||
|
attributes.put((String) args[0], args[1]);
|
||||||
|
return null; // setAttributeValue has a 'void' return type
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methodName.equals("getAttributeType"))
|
||||||
|
return attributeTypes.get(args[0]);
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("this proxy does not support method: " + methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conforms to the hashCode() specification for Annotation.
|
||||||
|
*
|
||||||
|
* @see Annotation#hashCode()
|
||||||
|
*/
|
||||||
|
private Object calculateHashCode(Object proxy) {
|
||||||
|
int sum = 0;
|
||||||
|
|
||||||
|
for (String attribName : attributes.keySet()) {
|
||||||
|
Object attribValue = attributes.get(attribName);
|
||||||
|
|
||||||
|
final int attribNameHashCode = attribName.hashCode();
|
||||||
|
final int attribValueHashCode;
|
||||||
|
|
||||||
|
if (attribValue == null)
|
||||||
|
// memberValue may be null when a mutable annotation is being added to a
|
||||||
|
// collection
|
||||||
|
// and before it has actually been visited (and populated) by
|
||||||
|
// MutableAnnotationVisitor
|
||||||
|
attribValueHashCode = 0;
|
||||||
|
else if (attribValue.getClass().isArray())
|
||||||
|
attribValueHashCode = Arrays.hashCode((Object[]) attribValue);
|
||||||
|
else
|
||||||
|
attribValueHashCode = attribValue.hashCode();
|
||||||
|
|
||||||
|
sum += (127 * attribNameHashCode) ^ attribValueHashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares <var>proxy</var> object and <var>other</var> object by comparing the return
|
||||||
|
* values of the methods specified by their common {@link Annotation} ancestry.
|
||||||
|
* <p/>
|
||||||
|
* <var>other</var> must be the same type as or a subtype of <var>proxy</var>. Will
|
||||||
|
* return false otherwise.
|
||||||
|
* <p/>
|
||||||
|
* Eagerly returns true if {@code proxy} == <var>other</var>
|
||||||
|
* </p>
|
||||||
|
* <p/>
|
||||||
|
* Conforms strictly to the equals() specification for Annotation
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see Annotation#equals(Object)
|
||||||
|
*/
|
||||||
|
private Object isEqualTo(Object proxy, Object other) {
|
||||||
|
if (proxy == other)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (other == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!annoType.isAssignableFrom(other.getClass()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (String attribName : attributes.keySet()) {
|
||||||
|
Object thisVal;
|
||||||
|
Object thatVal;
|
||||||
|
|
||||||
|
try {
|
||||||
|
thisVal = attributes.get(attribName);
|
||||||
|
thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((thisVal == null) && (thatVal != null))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((thatVal == null) && (thisVal != null))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (thatVal.getClass().isArray()) {
|
||||||
|
if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (thisVal instanceof Double) {
|
||||||
|
if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal)))
|
||||||
|
return false;
|
||||||
|
} else if (thisVal instanceof Float) {
|
||||||
|
if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal)))
|
||||||
|
return false;
|
||||||
|
} else if (!thisVal.equals(thatVal)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAttribs() {
|
||||||
|
ArrayList<String> attribs = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (String attribName : attributes.keySet())
|
||||||
|
attribs.add(format("%s=%s", attribName, attributes.get(attribName)));
|
||||||
|
|
||||||
|
return StringUtils.collectionToDelimitedString(attribs, ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the type of the given annotation attribute.
|
||||||
|
*/
|
||||||
|
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) {
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
method = annotationType.getDeclaredMethod(attributeName);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ReflectionUtils.handleReflectionException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return method.getReturnType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,75 +16,83 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
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.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 java.util.Stack;
|
||||||
|
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
|
import org.springframework.asm.Attribute;
|
||||||
import org.springframework.asm.ClassAdapter;
|
import org.springframework.asm.ClassAdapter;
|
||||||
import org.springframework.asm.ClassReader;
|
import org.springframework.asm.ClassReader;
|
||||||
import org.springframework.asm.ClassVisitor;
|
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.MethodVisitor;
|
||||||
import org.springframework.asm.Opcodes;
|
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.Location;
|
||||||
import org.springframework.beans.factory.parsing.Problem;
|
import org.springframework.beans.factory.parsing.Problem;
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM {@link ClassVisitor} that visits a {@link Configuration} class, populating a
|
* ASM {@link ClassVisitor} that visits a {@link Configuration} class, populating a
|
||||||
* {@link ConfigurationClass} instance with information gleaned along the way.
|
* {@link ConfigurationClass} instance with information gleaned along the way.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
* @see ConfigurationClassParser
|
* @see ConfigurationClassParser
|
||||||
* @see ConfigurationClass
|
* @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 ConfigurationClass configClass;
|
||||||
private final ConfigurationModel model;
|
|
||||||
|
private final Set<ConfigurationClass> model;
|
||||||
|
|
||||||
private final ProblemReporter problemReporter;
|
private final ProblemReporter problemReporter;
|
||||||
|
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
private final Stack<ConfigurationClass> importStack;
|
||||||
|
|
||||||
private boolean processInnerClasses = true;
|
|
||||||
|
|
||||||
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model,
|
public ConfigurationClassVisitor(ConfigurationClass configClass, Set<ConfigurationClass> model,
|
||||||
ProblemReporter problemReporter, ClassLoader classLoader) {
|
ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||||
super(ASM_EMPTY_VISITOR);
|
|
||||||
this.configClass = configClass;
|
this.configClass = configClass;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.problemReporter = problemReporter;
|
this.problemReporter = problemReporter;
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
|
this.importStack = new ImportStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProcessInnerClasses(boolean processInnerClasses) {
|
private ConfigurationClassVisitor(ConfigurationClass configClass, Set<ConfigurationClass> model,
|
||||||
this.processInnerClasses = processInnerClasses;
|
ProblemReporter problemReporter, ClassLoader classLoader, Stack<ConfigurationClass> 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);
|
public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3, String superTypeDesc, String[] arg5) {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3,
|
|
||||||
String superTypeDesc, String[] arg5) {
|
|
||||||
visitSuperType(superTypeDesc);
|
visitSuperType(superTypeDesc);
|
||||||
|
|
||||||
configClass.setName(convertResourcePathToClassName(classTypeDesc));
|
configClass.setName(ClassUtils.convertResourcePathToClassName(classTypeDesc));
|
||||||
|
|
||||||
// ASM always adds ACC_SUPER to the opcodes/modifiers for class definitions.
|
// 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
|
// 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) {
|
private void visitSuperType(String superTypeDesc) {
|
||||||
// traverse up the type hierarchy unless the next ancestor is java.lang.Object
|
// traverse up the type hierarchy unless the next ancestor is java.lang.Object
|
||||||
if (OBJECT_DESC.equals(superTypeDesc))
|
if (OBJECT_DESC.equals(superTypeDesc)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
ConfigurationClassVisitor visitor =
|
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader, importStack);
|
||||||
new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader);
|
ClassReader reader = ConfigurationClassReaderUtils.newAsmClassReader(superTypeDesc, classLoader);
|
||||||
|
|
||||||
ClassReader reader = newAsmClassReader(superTypeDesc, classLoader);
|
|
||||||
reader.accept(visitor, false);
|
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);
|
||||||
|
|
||||||
|
configClass.setSource(resourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitOuterClass(String s, String s1, String s2) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a class level annotation on a {@link Configuration @Configuration} class.
|
* Visits a class level annotation on a {@link Configuration @Configuration} class.
|
||||||
*
|
|
||||||
* <p>Upon encountering such an annotation, updates the {@link #configClass} model
|
* <p>Upon encountering such an annotation, updates the {@link #configClass} model
|
||||||
* object appropriately, and then returns an {@link AnnotationVisitor} implementation
|
* object appropriately, and then returns an {@link AnnotationVisitor} implementation
|
||||||
* that can populate the annotation appropriately with its attribute data as parsed
|
* that can populate the annotation appropriately with its attribute data as parsed
|
||||||
* by ASM.
|
* by ASM.
|
||||||
*
|
* @see ConfigurationClassAnnotation
|
||||||
* @see MutableAnnotation
|
|
||||||
* @see Configuration
|
* @see Configuration
|
||||||
* @see Lazy
|
* @see Lazy
|
||||||
* @see Import
|
* @see Import
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||||
String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
||||||
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoTypeName, classLoader);
|
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
|
// 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)) {
|
if (Import.class.equals(annoClass)) {
|
||||||
ImportStack importStack = ImportStackHolder.getImportStack();
|
|
||||||
|
|
||||||
if (!importStack.contains(configClass)) {
|
if (!importStack.contains(configClass)) {
|
||||||
importStack.push(configClass);
|
importStack.push(configClass);
|
||||||
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
|
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
problemReporter.error(new CircularImportProblem(configClass, importStack));
|
problemReporter.error(new CircularImportProblem(configClass, importStack));
|
||||||
}
|
}
|
||||||
|
|
||||||
Annotation mutableAnnotation = createMutableAnnotation(annoClass, classLoader);
|
ConfigurationClassAnnotation mutableAnnotation = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
|
||||||
configClass.addAnnotation(mutableAnnotation);
|
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
|
* Delegates all {@link Configuration @Configuration} class method parsing to
|
||||||
* {@link ConfigurationClassMethodVisitor}.
|
* {@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);
|
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void visitEnd() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation deals with inner classes here even though it would have been more
|
* ASM {@link MethodVisitor} that visits a single method declared in a given
|
||||||
* intuitive to deal with outer classes. Due to limitations in ASM (resulting from
|
* {@link Configuration} class. Determines whether the method is a {@link Bean}
|
||||||
* limitations in the VM spec) we cannot directly look for outer classes in all cases,
|
* method and if so, adds it to the {@link ConfigurationClass}.
|
||||||
* so instead build up a model of {@link #innerClasses} and process declaring class
|
|
||||||
* logic in a kind of inverted manner.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
private static class ConfigurationClassMethodVisitor extends MethodAdapter {
|
||||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
|
||||||
if (processInnerClasses == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
String innerClassName = convertResourcePathToClassName(name);
|
private final ConfigurationClass configClass;
|
||||||
String configClassName = configClass.getName();
|
private final String methodName;
|
||||||
|
private final int modifiers;
|
||||||
|
private final ConfigurationClassMethod.ReturnType returnType;
|
||||||
|
private final List<Annotation> annotations = new ArrayList<Annotation>();
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
// if the innerClassName is equal to configClassName, we just
|
private int lineNumber;
|
||||||
// ran into the outermost inner class look up the outer class
|
|
||||||
// associated with this
|
/**
|
||||||
if (innerClassName.equals(configClassName)) {
|
* Create a new {@link ConfigurationClassMethodVisitor} instance.
|
||||||
if (innerClasses.containsKey(outerName)) {
|
* @param configClass model object to which this method will be added
|
||||||
configClass.setDeclaringClass(innerClasses.get(outerName));
|
* @param methodName name of the method declared in the {@link Configuration} class
|
||||||
}
|
* @param methodDescriptor ASM representation of the method signature
|
||||||
return;
|
* @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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigurationClass innerConfigClass = new ConfigurationClass();
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
ConfigurationClassVisitor ccVisitor =
|
/**
|
||||||
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), problemReporter, classLoader);
|
* Provides the line number of this method within its declaring class. In reality, this
|
||||||
ccVisitor.setProcessInnerClasses(false);
|
* number is always inaccurate - <var>lineNo</var> represents the line number of the
|
||||||
|
* first instruction in this method. Method declaration line numbers are not in any way
|
||||||
|
* tracked in the bytecode. Any tooling or output that reads this value will have to
|
||||||
|
* compensate and estimate where the actual method declaration is.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitLineNumber(int lineNo, Label start) {
|
||||||
|
this.lineNumber = lineNo;
|
||||||
|
}
|
||||||
|
|
||||||
ClassReader reader = newAsmClassReader(name, classLoader);
|
/**
|
||||||
reader.accept(ccVisitor, false);
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (innerClasses.containsKey(outerName))
|
/**
|
||||||
innerConfigClass.setDeclaringClass(innerClasses.get(outerName));
|
* Determines return type from ASM <var>methodDescriptor</var> and determines whether
|
||||||
|
* that type is an interface.
|
||||||
|
*/
|
||||||
|
private ConfigurationClassMethod.ReturnType initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||||
|
final ConfigurationClassMethod.ReturnType returnType = new ConfigurationClassMethod.ReturnType(ConfigurationClassReaderUtils.getReturnTypeFromAsmMethodDescriptor(methodDescriptor));
|
||||||
|
// detect whether the return type is an interface
|
||||||
|
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<ConfigurationClass> model;
|
||||||
|
|
||||||
|
private final ProblemReporter problemReporter;
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
private final List<String> classesToImport = new ArrayList<String>();
|
||||||
|
|
||||||
|
|
||||||
|
public ImportAnnotationVisitor(
|
||||||
|
Set<ConfigurationClass> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private static class ImportStack extends Stack<ConfigurationClass> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplified contains() implementation that tests to see if any {@link ConfigurationClass}
|
||||||
|
* exists within this stack that has the same name as <var>elem</var>. Elem must be of
|
||||||
|
* type ConfigurationClass.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object elem) {
|
||||||
|
if (!(elem instanceof ConfigurationClass)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ConfigurationClass configClass = (ConfigurationClass) elem;
|
||||||
|
Comparator<ConfigurationClass> comparator = new Comparator<ConfigurationClass>() {
|
||||||
|
public int compare(ConfigurationClass first, ConfigurationClass second) {
|
||||||
|
return first.getName().equals(second.getName()) ? 0 : 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (Collections.binarySearch(this, configClass, comparator) != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a stack containing (in order)
|
||||||
|
* <ol>
|
||||||
|
* <li>com.acme.Foo</li>
|
||||||
|
* <li>com.acme.Bar</li>
|
||||||
|
* <li>com.acme.Baz</li>
|
||||||
|
* </ol>
|
||||||
|
* Returns "Foo->Bar->Baz". In the case of an empty stack, returns empty string.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
Iterator<ConfigurationClass> iterator = iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
builder.append(iterator.next().getSimpleName());
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
builder.append("->");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Configuration classes must be annotated with {@link Configuration @Configuration}. */
|
||||||
|
private static class NonAnnotatedConfigurationProblem extends Problem {
|
||||||
|
|
||||||
|
public NonAnnotatedConfigurationProblem(String className, Location location) {
|
||||||
|
super(String.format("%s was imported as a @Configuration class but was not actually annotated " +
|
||||||
|
"with @Configuration. Annotate the class or do not attempt to process it.", className), location);
|
||||||
|
}
|
||||||
|
|
||||||
// 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}.
|
* {@link Problem} registered upon detection of a circular {@link Import}.
|
||||||
*
|
|
||||||
* @see Import
|
* @see Import
|
||||||
* @see ImportStack
|
|
||||||
* @see ImportStackHolder
|
|
||||||
*/
|
*/
|
||||||
class CircularImportProblem extends Problem {
|
private static class CircularImportProblem extends Problem {
|
||||||
|
|
||||||
CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> importStack) {
|
public CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> importStack) {
|
||||||
super(format("A circular @Import has been detected: " +
|
super(String.format("A circular @Import has been detected: " +
|
||||||
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
|
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
|
||||||
"already present in the current import stack [%s]",
|
"already present in the current import stack [%s]",
|
||||||
importStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
|
importStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
|
||||||
attemptedImport.getSimpleName(), importStack),
|
attemptedImport.getSimpleName(), importStack),
|
||||||
new Location(new ClassPathResource(convertClassNameToResourcePath(importStack.peek().getName())),
|
new Location(new ClassPathResource(
|
||||||
importStack.peek().getSource())
|
ClassUtils.convertClassNameToResourcePath(importStack.peek().getName())),
|
||||||
|
importStack.peek().getSource())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<ConfigurationClass> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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.
|
|
||||||
* <p>
|
|
||||||
* 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<String> names = new ArrayList<String>(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 <i>hidden</i> 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 <i>hidden</i> 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 {
|
|
||||||
}
|
|
|
@ -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.
|
||||||
|
* <p>Note: This attribute will not be inherited by child bean definitions,
|
||||||
|
* hence it needs to be specified per concrete bean definition.
|
||||||
|
*
|
||||||
|
* <p>May be used on any class directly or indirectly annotated with
|
||||||
|
* {@link org.springframework.stereotype.Component} or on methods annotated
|
||||||
|
* with {@link Bean}.
|
||||||
|
*
|
||||||
|
* <p>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 <bean depends-on="..."/>} 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 {};
|
||||||
|
|
||||||
|
}
|
|
@ -23,18 +23,18 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation that allows one {@link Configuration} class to import another Configuration,
|
* Annotation that allows one {@link Configuration} class to import another Configuration,
|
||||||
* and thereby all its {@link Bean} definitions.
|
* and thereby all its {@link Bean} definitions.
|
||||||
*
|
*
|
||||||
* <p>Provides functionality equivalent to the {@literal <import/>} element in Spring XML.
|
* <p>Provides functionality equivalent to the {@literal <import/>} element in Spring XML.
|
||||||
|
* Only supported for actual {@link Configuration} classes.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see Configuration
|
* @see Configuration
|
||||||
*/
|
*/
|
||||||
@Target({ ElementType.TYPE })
|
@Target({ElementType.TYPE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@Documented
|
@Documented
|
||||||
|
|
|
@ -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<String> classesToImport = new ArrayList<String>();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<ConfigurationClass> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simplified contains() implementation that tests to see if any {@link ConfigurationClass}
|
|
||||||
* exists within this stack that has the same name as <var>elem</var>. Elem must be of
|
|
||||||
* type ConfigurationClass.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean contains(Object elem) {
|
|
||||||
Assert.isInstanceOf(ConfigurationClass.class, elem);
|
|
||||||
|
|
||||||
ConfigurationClass configClass = (ConfigurationClass) elem;
|
|
||||||
|
|
||||||
Comparator<ConfigurationClass> comparator = new Comparator<ConfigurationClass>() {
|
|
||||||
public int compare(ConfigurationClass first, ConfigurationClass second) {
|
|
||||||
return first.getName().equals(second.getName()) ? 0 : 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int index = Collections.binarySearch(this, configClass, comparator);
|
|
||||||
|
|
||||||
return index >= 0 ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a stack containing (in order)
|
|
||||||
* <ol>
|
|
||||||
* <li>com.acme.Foo</li>
|
|
||||||
* <li>com.acme.Bar</li>
|
|
||||||
* <li>com.acme.Baz</li>
|
|
||||||
* </ol>
|
|
||||||
* Returns "Foo->Bar->Baz". In the case of an empty stack, returns empty string.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public synchronized String toString() {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
Iterator<ConfigurationClass> iterator = this.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
builder.append(iterator.next().getSimpleName());
|
|
||||||
if (iterator.hasNext())
|
|
||||||
builder.append("->");
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<ImportStack> stackHolder = new ThreadLocal<ImportStack>() {
|
|
||||||
@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";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -21,7 +21,7 @@ import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether a bean is to be lazily initialized.
|
* Indicates whether a bean is to be lazily initialized.
|
||||||
|
@ -51,8 +51,9 @@ import java.lang.annotation.Target;
|
||||||
* @see Configuration
|
* @see Configuration
|
||||||
* @see org.springframework.stereotype.Component
|
* @see org.springframework.stereotype.Component
|
||||||
*/
|
*/
|
||||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Lazy {
|
public @interface Lazy {
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* ModelClass: name=Foo
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Object> values = new ArrayList<Object>();
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String, Object> attributes = new HashMap<String, Object>();
|
|
||||||
private final HashMap<String, Class<?>> attributeTypes = new HashMap<String, Class<?>>();
|
|
||||||
|
|
||||||
public MutableAnnotationInvocationHandler(Class<? extends Annotation> annoType) {
|
|
||||||
// pre-populate the attributes hash will all the names
|
|
||||||
// and default values of the attributes defined in 'annoType'
|
|
||||||
Method[] attribs = annoType.getDeclaredMethods();
|
|
||||||
for (Method attrib : attribs) {
|
|
||||||
this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName()));
|
|
||||||
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.annoType = annoType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
||||||
Assert.isInstanceOf(Annotation.class, proxy);
|
|
||||||
|
|
||||||
String methodName = method.getName();
|
|
||||||
|
|
||||||
// first -> check to see if this method is an attribute on our annotation
|
|
||||||
if (attributes.containsKey(methodName))
|
|
||||||
return attributes.get(methodName);
|
|
||||||
|
|
||||||
|
|
||||||
// second -> is it a method from java.lang.annotation.Annotation?
|
|
||||||
if (methodName.equals("annotationType"))
|
|
||||||
return annoType;
|
|
||||||
|
|
||||||
|
|
||||||
// third -> is it a method from java.lang.Object?
|
|
||||||
if (methodName.equals("toString"))
|
|
||||||
return format("@%s(%s)", annoType.getName(), getAttribs());
|
|
||||||
|
|
||||||
if (methodName.equals("equals"))
|
|
||||||
return isEqualTo(proxy, args[0]);
|
|
||||||
|
|
||||||
if (methodName.equals("hashCode"))
|
|
||||||
return calculateHashCode(proxy);
|
|
||||||
|
|
||||||
|
|
||||||
// finally -> is it a method specified by MutableAnno?
|
|
||||||
if (methodName.equals("setAttributeValue")) {
|
|
||||||
attributes.put((String) args[0], args[1]);
|
|
||||||
return null; // setAttributeValue has a 'void' return type
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodName.equals("getAttributeType"))
|
|
||||||
return attributeTypes.get(args[0]);
|
|
||||||
|
|
||||||
throw new UnsupportedOperationException("this proxy does not support method: " + methodName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Conforms to the hashCode() specification for Annotation.
|
|
||||||
*
|
|
||||||
* @see Annotation#hashCode()
|
|
||||||
*/
|
|
||||||
private Object calculateHashCode(Object proxy) {
|
|
||||||
int sum = 0;
|
|
||||||
|
|
||||||
for (String attribName : attributes.keySet()) {
|
|
||||||
Object attribValue = attributes.get(attribName);
|
|
||||||
|
|
||||||
final int attribNameHashCode = attribName.hashCode();
|
|
||||||
final int attribValueHashCode;
|
|
||||||
|
|
||||||
if (attribValue == null)
|
|
||||||
// memberValue may be null when a mutable annotation is being added to a
|
|
||||||
// collection
|
|
||||||
// and before it has actually been visited (and populated) by
|
|
||||||
// MutableAnnotationVisitor
|
|
||||||
attribValueHashCode = 0;
|
|
||||||
else if (attribValue.getClass().isArray())
|
|
||||||
attribValueHashCode = Arrays.hashCode((Object[]) attribValue);
|
|
||||||
else
|
|
||||||
attribValueHashCode = attribValue.hashCode();
|
|
||||||
|
|
||||||
sum += (127 * attribNameHashCode) ^ attribValueHashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares <var>proxy</var> object and <var>other</var> object by comparing the return
|
|
||||||
* values of the methods specified by their common {@link Annotation} ancestry.
|
|
||||||
* <p/>
|
|
||||||
* <var>other</var> must be the same type as or a subtype of <var>proxy</var>. Will
|
|
||||||
* return false otherwise.
|
|
||||||
* <p/>
|
|
||||||
* Eagerly returns true if {@code proxy} == <var>other</var>
|
|
||||||
* </p>
|
|
||||||
* <p/>
|
|
||||||
* Conforms strictly to the equals() specification for Annotation
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @see Annotation#equals(Object)
|
|
||||||
*/
|
|
||||||
private Object isEqualTo(Object proxy, Object other) {
|
|
||||||
if (proxy == other)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (other == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!annoType.isAssignableFrom(other.getClass()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (String attribName : attributes.keySet()) {
|
|
||||||
Object thisVal;
|
|
||||||
Object thatVal;
|
|
||||||
|
|
||||||
try {
|
|
||||||
thisVal = attributes.get(attribName);
|
|
||||||
thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new 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<String> attribs = new ArrayList<String>();
|
|
||||||
|
|
||||||
for (String attribName : attributes.keySet())
|
|
||||||
attribs.add(format("%s=%s", attribName, attributes.get(attribName)));
|
|
||||||
|
|
||||||
return StringUtils.collectionToDelimitedString(attribs, ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the type of the given annotation attribute.
|
|
||||||
*/
|
|
||||||
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) {
|
|
||||||
Method method = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
method = annotationType.getDeclaredMethod(attributeName);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ReflectionUtils.handleReflectionException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return method.getReturnType();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 <var>mutableAnno</var>. 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 <var>mutableAnno</var> 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() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -18,11 +18,11 @@ package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that a bean should be given preference when multiple candidates
|
* Indicates that a bean should be given preference when multiple candidates
|
||||||
* are qualified to autowire a single-valued dependency. If exactly one 'primary'
|
* are qualified to autowire a single-valued dependency. If exactly one 'primary'
|
||||||
|
@ -43,8 +43,9 @@ import java.lang.annotation.Target;
|
||||||
* @see Bean
|
* @see Bean
|
||||||
* @see org.springframework.stereotype.Component
|
* @see org.springframework.stereotype.Component
|
||||||
*/
|
*/
|
||||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Primary {
|
public @interface Primary {
|
||||||
|
|
||||||
|
|
|
@ -59,13 +59,11 @@ public @interface Scope {
|
||||||
/**
|
/**
|
||||||
* Specifies whether a component should be configured as a scoped proxy
|
* Specifies whether a component should be configured as a scoped proxy
|
||||||
* and if so, whether the proxy should be interface-based or subclass-based.
|
* and if so, whether the proxy should be interface-based or subclass-based.
|
||||||
*
|
|
||||||
* <p>Defaults to {@link ScopedProxyMode#NO}, indicating no scoped proxy
|
* <p>Defaults to {@link ScopedProxyMode#NO}, indicating no scoped proxy
|
||||||
* should be created.
|
* should be created.
|
||||||
*
|
|
||||||
* <p>Analogous to {@literal <aop:scoped-proxy/>} support in Spring XML. Valid
|
* <p>Analogous to {@literal <aop:scoped-proxy/>} support in Spring XML. Valid
|
||||||
* only in conjunction with a non-singleton, non-prototype {@link #value()}.
|
* only in conjunction with a non-singleton, non-prototype {@link #value()}.
|
||||||
*/
|
*/
|
||||||
ScopedProxyMode proxyMode() default ScopedProxyMode.NO;
|
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes scope characteristics for a Spring-managed bean including the scope
|
* Describes scope characteristics for a Spring-managed bean including the scope
|
||||||
|
@ -26,6 +27,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
* scoped-proxies.
|
* scoped-proxies.
|
||||||
*
|
*
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
* @see ScopeMetadataResolver
|
* @see ScopeMetadataResolver
|
||||||
* @see ScopedProxyMode
|
* @see ScopedProxyMode
|
||||||
|
@ -41,6 +43,7 @@ public class ScopeMetadata {
|
||||||
* Set the name of the scope.
|
* Set the name of the scope.
|
||||||
*/
|
*/
|
||||||
public void setScopeName(String scopeName) {
|
public void setScopeName(String scopeName) {
|
||||||
|
Assert.notNull(scopeName, "'scopeName' must not be null");
|
||||||
this.scopeName = scopeName;
|
this.scopeName = scopeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +58,7 @@ public class ScopeMetadata {
|
||||||
* Set the proxy-mode to be applied to the scoped instance.
|
* Set the proxy-mode to be applied to the scoped instance.
|
||||||
*/
|
*/
|
||||||
public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
|
public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
|
||||||
|
Assert.notNull(scopedProxyMode, "'scopedProxyMode' must not be null");
|
||||||
this.scopedProxyMode = scopedProxyMode;
|
this.scopedProxyMode = scopedProxyMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
*
|
*
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
* @see Scope
|
* @see org.springframework.context.annotation.Scope
|
||||||
*/
|
*/
|
||||||
public interface ScopeMetadataResolver {
|
public interface ScopeMetadataResolver {
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,12 @@ package org.springframework.context.annotation;
|
||||||
*/
|
*/
|
||||||
public enum ScopedProxyMode {
|
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.
|
* Do not create a scoped proxy.
|
||||||
* <p>This proxy-mode is not typically useful when used with a
|
* <p>This proxy-mode is not typically useful when used with a
|
||||||
|
|
|
@ -16,17 +16,16 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumerates the names of the scopes supported out of the box in Spring.
|
* Enumerates the names of the scopes supported out of the box in Spring.
|
||||||
*
|
*
|
||||||
* <p>Not modeled as an actual java enum because annotations that accept a scope attribute
|
* <p>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 allow for user-defined scope names. Given that java enums are not extensible, these
|
||||||
* must remain simple string constants.
|
* must remain simple string constants.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see Scope
|
* @see org.springframework.context.annotation.Scope
|
||||||
*/
|
*/
|
||||||
public class StandardScopes {
|
public class StandardScopes {
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
import org.springframework.beans.support.ResourceEditorRegistrar;
|
import org.springframework.beans.support.ResourceEditorRegistrar;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
@ -487,9 +488,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
*/
|
*/
|
||||||
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
|
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
|
||||||
// Invoke factory processors registered with the context instance.
|
// Invoke factory processors registered with the context instance.
|
||||||
for (BeanFactoryPostProcessor factoryProcessor : getBeanFactoryPostProcessors()) {
|
invokeBeanFactoryPostProcessors(getBeanFactoryPostProcessors(), beanFactory);
|
||||||
factoryProcessor.postProcessBeanFactory(beanFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not initialize FactoryBeans here: We need to leave all regular beans
|
// Do not initialize FactoryBeans here: We need to leave all regular beans
|
||||||
// uninitialized to let the bean factory post-processors apply to them!
|
// 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.
|
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
|
||||||
OrderComparator.sort(priorityOrderedPostProcessors);
|
OrderComparator.sort(priorityOrderedPostProcessors);
|
||||||
invokeBeanFactoryPostProcessors(beanFactory, priorityOrderedPostProcessors);
|
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
|
||||||
|
|
||||||
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
|
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
|
||||||
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
|
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
|
||||||
|
@ -523,21 +522,21 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
orderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));
|
orderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));
|
||||||
}
|
}
|
||||||
OrderComparator.sort(orderedPostProcessors);
|
OrderComparator.sort(orderedPostProcessors);
|
||||||
invokeBeanFactoryPostProcessors(beanFactory, orderedPostProcessors);
|
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
|
||||||
|
|
||||||
// Finally, invoke all other BeanFactoryPostProcessors.
|
// Finally, invoke all other BeanFactoryPostProcessors.
|
||||||
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
|
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
|
||||||
for (String postProcessorName : nonOrderedPostProcessorNames) {
|
for (String postProcessorName : nonOrderedPostProcessorNames) {
|
||||||
nonOrderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));
|
nonOrderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));
|
||||||
}
|
}
|
||||||
invokeBeanFactoryPostProcessors(beanFactory, nonOrderedPostProcessors);
|
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke the given BeanFactoryPostProcessor beans.
|
* Invoke the given BeanFactoryPostProcessor beans.
|
||||||
*/
|
*/
|
||||||
private void invokeBeanFactoryPostProcessors(
|
private void invokeBeanFactoryPostProcessors(
|
||||||
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> postProcessors) {
|
List<BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
|
||||||
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
|
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
|
||||||
postProcessor.postProcessBeanFactory(beanFactory);
|
postProcessor.postProcessBeanFactory(beanFactory);
|
||||||
|
|
|
@ -21,12 +21,14 @@ import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.scheduling.annotation.AsyncResult;
|
import org.springframework.scheduling.annotation.AsyncResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
|
@Lazy
|
||||||
public class AutowiredQualifierFooService implements FooService {
|
public class AutowiredQualifierFooService implements FooService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.MessageSource;
|
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.context.support.AbstractApplicationContext;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
|
@ -38,7 +40,7 @@ import org.springframework.util.Assert;
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service @Lazy @DependsOn("myNamedComponent")
|
||||||
public class FooServiceImpl implements FooService {
|
public class FooServiceImpl implements FooService {
|
||||||
|
|
||||||
@Autowired private FooDao fooDao;
|
@Autowired private FooDao fooDao;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,11 +17,12 @@
|
||||||
package example.scannable;
|
package example.scannable;
|
||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
*/
|
*/
|
||||||
@Component("myNamedComponent")
|
@Component("myNamedComponent") @Lazy
|
||||||
public class NamedComponent {
|
public class NamedComponent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,8 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
boolean threw = false;
|
boolean threw = false;
|
||||||
try {
|
try {
|
||||||
newParser().parse(loadAsConfigurationSource(X.class), null);
|
newParser().parse(loadAsConfigurationSource(X.class), null);
|
||||||
} catch (BeanDefinitionParsingException ex) {
|
}
|
||||||
|
catch (BeanDefinitionParsingException ex) {
|
||||||
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
||||||
ex.getMessage().contains(
|
ex.getMessage().contains(
|
||||||
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.Z2' " +
|
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.Z2' " +
|
||||||
|
@ -71,6 +72,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
assertTrue(threw);
|
assertTrue(threw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
@Import(B.class)
|
@Import(B.class)
|
||||||
static class A {
|
static class A {
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -79,6 +81,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
@Import(A.class)
|
@Import(A.class)
|
||||||
static class B {
|
static class B {
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -87,6 +90,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
@Import( { Y.class, Z.class })
|
@Import( { Y.class, Z.class })
|
||||||
class X {
|
class X {
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -95,6 +99,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
class Y {
|
class Y {
|
||||||
@Bean
|
@Bean
|
||||||
TestBean y() {
|
TestBean y() {
|
||||||
|
@ -102,6 +107,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
@Import( { Z1.class, Z2.class })
|
@Import( { Z1.class, Z2.class })
|
||||||
class Z {
|
class Z {
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -110,6 +116,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
class Z1 {
|
class Z1 {
|
||||||
@Bean
|
@Bean
|
||||||
TestBean z1() {
|
TestBean z1() {
|
||||||
|
@ -117,6 +124,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
@Import(Z.class)
|
@Import(Z.class)
|
||||||
class Z2 {
|
class Z2 {
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -124,4 +132,5 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
return new TestBean();
|
return new TestBean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||||
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
|
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for the {@link AnnotationScopeMetadataResolver} class.
|
|
||||||
*
|
|
||||||
* @author Rick Evans
|
* @author Rick Evans
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
public final class AnnotationScopeMetadataResolverTests {
|
public final class AnnotationScopeMetadataResolverTests {
|
||||||
|
|
||||||
|
@ -34,7 +49,6 @@ public final class AnnotationScopeMetadataResolverTests {
|
||||||
assertEquals(ScopedProxyMode.NO, scopeMetadata.getScopedProxyMode());
|
assertEquals(ScopedProxyMode.NO, scopeMetadata.getScopedProxyMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testThatResolveScopeMetadataDoesApplyScopedProxyModeToAPrototype() {
|
public void testThatResolveScopeMetadataDoesApplyScopedProxyModeToAPrototype() {
|
||||||
this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(ScopedProxyMode.INTERFACES);
|
this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(ScopedProxyMode.INTERFACES);
|
||||||
|
@ -45,6 +59,16 @@ public final class AnnotationScopeMetadataResolverTests {
|
||||||
assertEquals(ScopedProxyMode.INTERFACES, scopeMetadata.getScopedProxyMode());
|
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)
|
@Test(expected=IllegalArgumentException.class)
|
||||||
public void testCtorWithNullScopedProxyMode() {
|
public void testCtorWithNullScopedProxyMode() {
|
||||||
new AnnotationScopeMetadataResolver(null);
|
new AnnotationScopeMetadataResolver(null);
|
||||||
|
@ -65,4 +89,9 @@ public final class AnnotationScopeMetadataResolverTests {
|
||||||
private static final class AnnotatedWithPrototypeScope {
|
private static final class AnnotatedWithPrototypeScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Scope(value="request", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
|
private static final class AnnotatedWithScopedProxy {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,42 +13,40 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.context.annotation;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
package org.springframework.context.annotation;
|
||||||
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.*;
|
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||||
import org.springframework.beans.factory.parsing.Location;
|
import org.springframework.beans.factory.parsing.Location;
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
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.core.io.ClassPathResource;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link BeanMethod}.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
public class BeanMethodTests {
|
public class BeanMethodTests {
|
||||||
|
|
||||||
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
||||||
private String beanName = "foo";
|
private String beanName = "foo";
|
||||||
private Bean beanAnno = createMutableAnnotation(Bean.class, ClassUtils.getDefaultClassLoader());
|
private Bean beanAnno = (Bean) createMutableAnnotation(Bean.class, ClassUtils.getDefaultClassLoader());
|
||||||
private ModelClass returnType = new ModelClass("FooType");
|
private ConfigurationClassMethod.ReturnType returnType = new ConfigurationClassMethod.ReturnType("FooType");
|
||||||
private ConfigurationClass declaringClass = new ConfigurationClass();
|
private ConfigurationClass declaringClass = new ConfigurationClass();
|
||||||
{ declaringClass.setName("test.Config"); }
|
{ declaringClass.setName("test.Config"); }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWellFormedMethod() {
|
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.getName(), sameInstance(beanName));
|
||||||
assertThat(beanMethod.getModifiers(), equalTo(0));
|
assertThat(beanMethod.getModifiers(), equalTo(0));
|
||||||
|
@ -84,7 +82,7 @@ public class BeanMethodTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void finalMethodsAreIllegal() {
|
public void finalMethodsAreIllegal() {
|
||||||
BeanMethod beanMethod = new BeanMethod(beanName, Modifier.FINAL, returnType, beanAnno);
|
ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, Modifier.FINAL, returnType, beanAnno);
|
||||||
beanMethod.setDeclaringClass(declaringClass);
|
beanMethod.setDeclaringClass(declaringClass);
|
||||||
try {
|
try {
|
||||||
beanMethod.validate(problemReporter);
|
beanMethod.validate(problemReporter);
|
||||||
|
@ -96,7 +94,7 @@ public class BeanMethodTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void privateMethodsAreIllegal() {
|
public void privateMethodsAreIllegal() {
|
||||||
BeanMethod beanMethod = new BeanMethod(beanName, Modifier.PRIVATE, returnType, beanAnno);
|
ConfigurationClassMethod beanMethod = new ConfigurationClassMethod(beanName, Modifier.PRIVATE, returnType, beanAnno);
|
||||||
beanMethod.setDeclaringClass(declaringClass);
|
beanMethod.setDeclaringClass(declaringClass);
|
||||||
try {
|
try {
|
||||||
beanMethod.validate(problemReporter);
|
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
|
@Test
|
||||||
public void singletonsSansProxyAreLegal() {
|
public void singletonsSansProxyAreLegal() {
|
||||||
Scope scope = SingletonNoProxy.class.getAnnotation(Scope.class);
|
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.setDeclaringClass(declaringClass);
|
||||||
beanMethod.validate(problemReporter); // should validate without problems - it's legal
|
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
|
@Test
|
||||||
public void sessionInterfaceScopedProxiesAreLegal() {
|
public void sessionInterfaceScopedProxiesAreLegal() {
|
||||||
Scope scope = SessionInterfaceProxy.class.getAnnotation(Scope.class);
|
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.setDeclaringClass(declaringClass);
|
||||||
beanMethod.validate(problemReporter); // should validate without problems - it's legal
|
beanMethod.validate(problemReporter); // should validate without problems - it's legal
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,15 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
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 org.aspectj.lang.annotation.Aspect;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.TestBean;
|
import org.springframework.beans.TestBean;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
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.core.type.filter.AssignableTypeFilter;
|
||||||
import org.springframework.stereotype.Component;
|
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 Mark Fisher
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
@ -49,7 +48,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||||
|
|
||||||
private static final String BASE_PACKAGE = "example.scannable";
|
private static final String BASE_PACKAGE = "example.scannable";
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleScanWithDefaultFiltersAndPostProcessors() {
|
public void testSimpleScanWithDefaultFiltersAndPostProcessors() {
|
||||||
GenericApplicationContext context = new GenericApplicationContext();
|
GenericApplicationContext context = new GenericApplicationContext();
|
||||||
|
@ -62,9 +61,34 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||||
assertTrue(context.containsBean("myNamedComponent"));
|
assertTrue(context.containsBean("myNamedComponent"));
|
||||||
assertTrue(context.containsBean("myNamedDao"));
|
assertTrue(context.containsBean("myNamedDao"));
|
||||||
assertTrue(context.containsBean("thoreau"));
|
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.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_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
|
@Test
|
||||||
|
@ -354,7 +378,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||||
assertEquals(10, beanCount);
|
assertEquals(10, beanCount);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
FooServiceImpl fooService = (FooServiceImpl) context.getBean("fooService");
|
FooServiceImpl fooService = context.getBean("fooService", FooServiceImpl.class);
|
||||||
StaticListableBeanFactory myBf = (StaticListableBeanFactory) context.getBean("myBf");
|
StaticListableBeanFactory myBf = (StaticListableBeanFactory) context.getBean("myBf");
|
||||||
MessageSource ms = (MessageSource) context.getBean("messageSource");
|
MessageSource ms = (MessageSource) context.getBean("messageSource");
|
||||||
assertTrue(fooService.isInitCalled());
|
assertTrue(fooService.isInitCalled());
|
||||||
|
@ -415,6 +439,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||||
scanner.scan(BASE_PACKAGE);
|
scanner.scan(BASE_PACKAGE);
|
||||||
try {
|
try {
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
context.getBean("fooService");
|
||||||
fail("BeanCreationException expected; fooDao should not have been an autowire-candidate");
|
fail("BeanCreationException expected; fooDao should not have been an autowire-candidate");
|
||||||
}
|
}
|
||||||
catch (BeanCreationException expected) {
|
catch (BeanCreationException expected) {
|
||||||
|
|
|
@ -16,91 +16,75 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import org.springframework.aop.scope.ScopedObject;
|
import org.springframework.aop.scope.ScopedObject;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.beans.TestBean;
|
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.config.SimpleMapScope;
|
||||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
|
||||||
import org.springframework.context.annotation4.FactoryMethodComponent;
|
import org.springframework.context.annotation4.FactoryMethodComponent;
|
||||||
import org.springframework.context.support.GenericApplicationContext;
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Pollack
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
*/
|
||||||
public class ClassPathFactoryBeanDefinitionScannerTests extends TestCase {
|
public class ClassPathFactoryBeanDefinitionScannerTests extends TestCase {
|
||||||
|
|
||||||
private static final String BASE_PACKAGE = FactoryMethodComponent.class.getPackage().getName();
|
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();
|
GenericApplicationContext context = new GenericApplicationContext();
|
||||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
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
|
scanner.scan(BASE_PACKAGE);
|
||||||
assertEquals("staticInstance", staticTestBean.getName());
|
context.registerBeanDefinition("clientBean", new RootBeanDefinition(QualifiedClientBean.class));
|
||||||
TestBean staticTestBean2 = (TestBean)context.getBean("factoryMethodComponent$staticInstance");//1
|
context.refresh();
|
||||||
assertSame(staticTestBean, staticTestBean2);
|
|
||||||
|
FactoryMethodComponent fmc = context.getBean("factoryMethodComponent", FactoryMethodComponent.class);
|
||||||
TestBean tb = (TestBean)context.getBean("factoryMethodComponent$getPublicInstance"); //2
|
assertFalse(fmc.getClass().getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR));
|
||||||
|
|
||||||
|
TestBean tb = (TestBean)context.getBean("publicInstance"); //2
|
||||||
assertEquals("publicInstance", tb.getName());
|
assertEquals("publicInstance", tb.getName());
|
||||||
TestBean tb2 = (TestBean)context.getBean("factoryMethodComponent$getPublicInstance"); //2
|
TestBean tb2 = (TestBean)context.getBean("publicInstance"); //2
|
||||||
assertEquals("publicInstance", tb2.getName());
|
assertEquals("publicInstance", tb2.getName());
|
||||||
assertSame(tb2, tb);
|
assertSame(tb2, tb);
|
||||||
|
|
||||||
//Were qualifiers applied to bean definition
|
tb = (TestBean)context.getBean("protectedInstance"); //3
|
||||||
ConfigurableListableBeanFactory cbf = (ConfigurableListableBeanFactory)context.getAutowireCapableBeanFactory();
|
|
||||||
AbstractBeanDefinition abd = (AbstractBeanDefinition)cbf.getBeanDefinition("factoryMethodComponent$getPublicInstance"); //2
|
|
||||||
Set<AutowireCandidateQualifier> qualifierSet = abd.getQualifiers();
|
|
||||||
assertEquals(1, qualifierSet.size());
|
|
||||||
|
|
||||||
|
|
||||||
tb = (TestBean)context.getBean("factoryMethodComponent$getProtectedInstance"); //3
|
|
||||||
assertEquals("protectedInstance", tb.getName());
|
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());
|
assertEquals("protectedInstance", tb2.getName());
|
||||||
assertSame(tb2, tb);
|
assertSame(tb2, tb);
|
||||||
|
|
||||||
tb = (TestBean)context.getBean("factoryMethodComponent$getPrivateInstance"); //4
|
tb = (TestBean)context.getBean("privateInstance"); //4
|
||||||
assertEquals("privateInstance", tb.getName());
|
assertEquals("privateInstance", tb.getName());
|
||||||
assertEquals(0, tb.getAge());
|
assertEquals(1, tb.getAge());
|
||||||
tb2 = (TestBean)context.getBean("factoryMethodComponent$getPrivateInstance"); //4
|
tb2 = (TestBean)context.getBean("privateInstance"); //4
|
||||||
assertEquals(1, tb2.getAge());
|
assertEquals(2, tb2.getAge());
|
||||||
assertNotSame(tb2, tb);
|
assertNotSame(tb2, tb);
|
||||||
|
|
||||||
Object bean = context.getBean("scopedTarget.factoryMethodComponent$requestScopedInstance"); //5
|
Object bean = context.getBean("requestScopedInstance"); //5
|
||||||
assertNotNull(bean);
|
|
||||||
assertTrue(bean instanceof ScopedObject);
|
|
||||||
|
|
||||||
//Scope assertions
|
|
||||||
assertTrue(AopUtils.isCglibProxy(bean));
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,104 +13,24 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
|
||||||
import static org.junit.Assert.*;
|
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.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
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
public class ConfigurationClassPostProcessorTests {
|
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<Class<?>> classes = (Vector<Class<?>>) classesField.get(classLoader);
|
|
||||||
|
|
||||||
Vector<Class<?>> cglibClassesAlreadyLoaded = new Vector<Class<?>>();
|
|
||||||
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
|
* Enhanced {@link Configuration} classes are only necessary for respecting
|
||||||
* certain bean semantics, like singleton-scoping, scoped proxies, etc.
|
* certain bean semantics, like singleton-scoping, scoped proxies, etc.
|
||||||
*
|
* <p>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.
|
* registered Configuration classes and many use cases would still work.
|
||||||
* Certain cases, however, like inter-bean singleton references would not.
|
* 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
|
* We test for such a case below, and in doing so prove that enhancement is
|
||||||
|
@ -118,30 +38,13 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEnhancementIsPresentBecauseSingletonSemanticsAreRespected() {
|
public void testEnhancementIsPresentBecauseSingletonSemanticsAreRespected() {
|
||||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||||
beanFactory.registerBeanDefinition("config",
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||||
rootBeanDefinition(SingletonBeanConfig.class).getBeanDefinition());
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
Foo foo = (Foo) beanFactory.getBean("foo");
|
Foo foo = (Foo) beanFactory.getBean("foo");
|
||||||
Bar bar = (Bar) beanFactory.getBean("bar");
|
Bar bar = (Bar) beanFactory.getBean("bar");
|
||||||
assertThat(foo, sameInstance(bar.foo));
|
assertSame(foo, bar.foo);
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class SingletonBeanConfig {
|
|
||||||
public @Bean Foo foo() {
|
|
||||||
return new Foo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Bean Bar bar() {
|
|
||||||
return new Bar(foo());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Foo { }
|
|
||||||
static class Bar {
|
|
||||||
final Foo foo;
|
|
||||||
public Bar(Foo foo) { this.foo = foo; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,14 +55,38 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
public void testAlreadyLoadedConfigurationClasses() {
|
public void testAlreadyLoadedConfigurationClasses() {
|
||||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||||
beanFactory.registerBeanDefinition("unloadedConfig",
|
beanFactory.registerBeanDefinition("unloadedConfig",
|
||||||
rootBeanDefinition(UnloadedConfig.class.getName()).getBeanDefinition());
|
new RootBeanDefinition(UnloadedConfig.class.getName(), null, null));
|
||||||
beanFactory.registerBeanDefinition("loadedConfig",
|
beanFactory.registerBeanDefinition("loadedConfig", new RootBeanDefinition(LoadedConfig.class));
|
||||||
rootBeanDefinition(LoadedConfig.class).getBeanDefinition());
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
new ConfigurationClassPostProcessor() .postProcessBeanFactory(beanFactory);
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
beanFactory.getBean("foo");
|
beanFactory.getBean("foo");
|
||||||
beanFactory.getBean("bar");
|
beanFactory.getBean("bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class SingletonBeanConfig {
|
||||||
|
|
||||||
|
public @Bean Foo foo() {
|
||||||
|
return new Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Bean Bar bar() {
|
||||||
|
return new Bar(foo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class Foo {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class Bar {
|
||||||
|
final Foo foo;
|
||||||
|
public Bar(Foo foo) { this.foo = foo; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class UnloadedConfig {
|
static class UnloadedConfig {
|
||||||
public @Bean Foo foo() {
|
public @Bean Foo foo() {
|
||||||
|
@ -167,10 +94,12 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class LoadedConfig {
|
static class LoadedConfig {
|
||||||
public @Bean Bar bar() {
|
public @Bean Bar bar() {
|
||||||
return new Bar(new Foo());
|
return new Bar(new Foo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -45,9 +45,11 @@ public class InvalidConfigurationClassDefinitionTests {
|
||||||
beanFactory.registerBeanDefinition("config", configBeanDef);
|
beanFactory.registerBeanDefinition("config", configBeanDef);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
fail("expected exception");
|
fail("expected exception");
|
||||||
} catch (BeanDefinitionParsingException ex) {
|
}
|
||||||
|
catch (BeanDefinitionParsingException ex) {
|
||||||
assertTrue(ex.getMessage(), ex.getMessage().contains("Remove the final modifier"));
|
assertTrue(ex.getMessage(), ex.getMessage().contains("Remove the final modifier"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.context.annotation.DependsOn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests proving that the various attributes available via the {@link Bean}
|
* Unit tests proving that the various attributes available via the {@link Bean}
|
||||||
|
@ -67,7 +67,7 @@ public class BeanAnnotationAttributePropagationTests {
|
||||||
@Test
|
@Test
|
||||||
public void dependsOnMetadataIsPropagated() {
|
public void dependsOnMetadataIsPropagated() {
|
||||||
@Configuration class Config {
|
@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",
|
assertArrayEquals("dependsOn metadata was not propagated",
|
||||||
|
@ -149,8 +149,8 @@ public class BeanAnnotationAttributePropagationTests {
|
||||||
private AbstractBeanDefinition beanDef(Class<?> configClass) {
|
private AbstractBeanDefinition beanDef(Class<?> configClass) {
|
||||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||||
factory.registerBeanDefinition("config", new RootBeanDefinition(configClass));
|
factory.registerBeanDefinition("config", new RootBeanDefinition(configClass));
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(factory);
|
||||||
return (AbstractBeanDefinition) factory.getBeanDefinition("foo");
|
return (AbstractBeanDefinition) factory.getBeanDefinition("foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,27 +16,24 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation.configuration;
|
package org.springframework.context.annotation.configuration;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import test.beans.ITestBean;
|
||||||
|
import test.beans.TestBean;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.annotation.StandardScopes;
|
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
|
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
|
||||||
* handling within {@link Configuration} class definitions.
|
* handling within {@link Configuration} class definitions.
|
||||||
|
@ -51,27 +48,20 @@ public class ConfigurationClassProcessingTests {
|
||||||
* post-processes the factory using JavaConfig's {@link ConfigurationClassPostProcessor}.
|
* post-processes the factory using JavaConfig's {@link ConfigurationClassPostProcessor}.
|
||||||
* When complete, the factory is ready to service requests for any {@link Bean} methods
|
* When complete, the factory is ready to service requests for any {@link Bean} methods
|
||||||
* declared by <var>configClasses</var>.
|
* declared by <var>configClasses</var>.
|
||||||
*
|
|
||||||
* @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();
|
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||||
|
|
||||||
for (Class<?> configClass : configClasses) {
|
for (Class<?> configClass : configClasses) {
|
||||||
String configBeanName = configClass.getName();
|
String configBeanName = configClass.getName();
|
||||||
factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass).getBeanDefinition());
|
factory.registerBeanDefinition(configBeanName, new RootBeanDefinition(configClass));
|
||||||
}
|
}
|
||||||
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
|
pp.postProcessBeanFactory(factory);
|
||||||
|
|
||||||
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
|
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
|
||||||
|
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customBeanNameIsRespected() {
|
public void customBeanNameIsRespected() {
|
||||||
BeanFactory factory = initBeanFactory(ConfigWithBeanWithCustomName.class);
|
BeanFactory factory = initBeanFactory(ConfigWithBeanWithCustomName.class);
|
||||||
|
@ -132,20 +122,10 @@ public class ConfigurationClassProcessingTests {
|
||||||
@Test
|
@Test
|
||||||
public void simplestPossibleConfiguration() {
|
public void simplestPossibleConfiguration() {
|
||||||
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
|
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
|
||||||
|
|
||||||
String stringBean = factory.getBean("stringBean", String.class);
|
String stringBean = factory.getBean("stringBean", String.class);
|
||||||
|
assertEquals(stringBean, "foo");
|
||||||
assertThat(stringBean, equalTo("foo"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class SimplestPossibleConfig {
|
|
||||||
public @Bean String stringBean() {
|
|
||||||
return "foo";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void configurationWithPrototypeScopedBeans() {
|
public void configurationWithPrototypeScopedBeans() {
|
||||||
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
|
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
|
||||||
|
@ -154,12 +134,22 @@ public class ConfigurationClassProcessingTests {
|
||||||
ITestBean bar = factory.getBean("bar", ITestBean.class);
|
ITestBean bar = factory.getBean("bar", ITestBean.class);
|
||||||
ITestBean baz = factory.getBean("baz", ITestBean.class);
|
ITestBean baz = factory.getBean("baz", ITestBean.class);
|
||||||
|
|
||||||
assertThat(foo.getSpouse(), sameInstance(bar));
|
assertSame(foo.getSpouse(), bar);
|
||||||
assertThat(bar.getSpouse(), not(sameInstance(baz)));
|
assertNotSame(bar.getSpouse(), baz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class SimplestPossibleConfig {
|
||||||
|
public @Bean String stringBean() {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class ConfigWithPrototypeBean {
|
static class ConfigWithPrototypeBean {
|
||||||
|
|
||||||
public @Bean TestBean foo() {
|
public @Bean TestBean foo() {
|
||||||
TestBean foo = new TestBean("foo");
|
TestBean foo = new TestBean("foo");
|
||||||
foo.setSpouse(bar());
|
foo.setSpouse(bar());
|
||||||
|
|
|
@ -41,9 +41,11 @@ public class ImportTests {
|
||||||
|
|
||||||
private DefaultListableBeanFactory processConfigurationClasses(Class<?>... classes) {
|
private DefaultListableBeanFactory processConfigurationClasses(Class<?>... classes) {
|
||||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||||
for (Class<?> clazz : classes)
|
for (Class<?> clazz : classes) {
|
||||||
beanFactory.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
|
beanFactory.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
}
|
||||||
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
return beanFactory;
|
return beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +139,8 @@ public class ImportTests {
|
||||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(
|
||||||
WithMultipleArgumentsThatWillCauseDuplication.class));
|
WithMultipleArgumentsThatWillCauseDuplication.class));
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
assertThat(beanFactory.getBeanDefinitionCount(), equalTo(4));
|
assertThat(beanFactory.getBeanDefinitionCount(), equalTo(4));
|
||||||
assertThat(beanFactory.getBean("foo", ITestBean.class).getName(), equalTo("foo2"));
|
assertThat(beanFactory.getBean("foo", ITestBean.class).getName(), equalTo("foo2"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,18 +40,23 @@ public class PolymorphicConfigurationTests {
|
||||||
public void subclassNeedNotDeclareConfigurationAnnotation() {
|
public void subclassNeedNotDeclareConfigurationAnnotation() {
|
||||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
beanFactory.getBean("testBean", TestBean.class);
|
beanFactory.getBean("testBean", TestBean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class SuperConfig {
|
static class SuperConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public TestBean testBean() {
|
public TestBean testBean() {
|
||||||
return new TestBean();
|
return new TestBean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Config extends SuperConfig { }
|
|
||||||
|
static class Config extends SuperConfig {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,21 +13,24 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.context.annotation.configuration;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
package org.springframework.context.annotation.configuration;
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import test.beans.ITestBean;
|
||||||
|
import test.beans.TestBean;
|
||||||
|
|
||||||
import org.springframework.aop.scope.ScopedObject;
|
import org.springframework.aop.scope.ScopedObject;
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
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.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
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.annotation.StandardScopes;
|
||||||
import org.springframework.context.support.GenericApplicationContext;
|
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
|
* Tests that scopes are properly supported by using a custom Scope implementations
|
||||||
* and scoped proxy {@link Bean} declarations.
|
* and scoped proxy {@link Bean} declarations.
|
||||||
*
|
*
|
||||||
* @author Costin Leau
|
* @author Costin Leau
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
public class ScopingTests {
|
public class ScopingTests {
|
||||||
|
|
||||||
|
@ -82,19 +80,16 @@ public class ScopingTests {
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testScopeOnClasses() throws Exception {
|
public void testScopeOnClasses() throws Exception {
|
||||||
genericTestScope("scopedClass");
|
genericTestScope("scopedClass");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testScopeOnInterfaces() throws Exception {
|
public void testScopeOnInterfaces() throws Exception {
|
||||||
genericTestScope("scopedInterface");
|
genericTestScope("scopedInterface");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSameScopeOnDifferentBeans() throws Exception {
|
public void testSameScopeOnDifferentBeans() throws Exception {
|
||||||
Object beanAInScope = ctx.getBean("scopedClass");
|
Object beanAInScope = ctx.getBean("scopedClass");
|
||||||
|
@ -112,22 +107,8 @@ public class ScopingTests {
|
||||||
assertNotSame(newBeanBInScope, beanBInScope);
|
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
|
@Test
|
||||||
public void testRawScopes() throws Exception {
|
public void testRawScopes() throws Exception {
|
||||||
|
|
||||||
String beanName = "scopedProxyInterface";
|
String beanName = "scopedProxyInterface";
|
||||||
|
|
||||||
// get hidden bean
|
// get hidden bean
|
||||||
|
@ -368,71 +349,56 @@ public class ScopingTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
/**
|
* Simple scope implementation which creates object based on a flag.
|
||||||
* Simple scope implementation which creates object based on a flag.
|
* @author Costin Leau
|
||||||
*
|
* @author Chris Beams
|
||||||
* @author Costin Leau
|
|
||||||
* @author Chris Beams
|
|
||||||
*/
|
|
||||||
class CustomScope implements org.springframework.beans.factory.config.Scope {
|
|
||||||
|
|
||||||
public boolean createNewScope = true;
|
|
||||||
|
|
||||||
private Map<String, Object> beans = new HashMap<String, Object>();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (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) {
|
static class CustomScope implements org.springframework.beans.factory.config.Scope {
|
||||||
if (createNewScope) {
|
|
||||||
beans.clear();
|
public boolean createNewScope = true;
|
||||||
// reset the flag back
|
|
||||||
createNewScope = false;
|
private Map<String, Object> beans = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (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);
|
public String getConversationId() {
|
||||||
// if a new object is requested or none exists under the current
|
return null;
|
||||||
// name, create one
|
|
||||||
if (bean == null) {
|
|
||||||
beans.put(name, objectFactory.getObject());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return beans.get(name);
|
public void registerDestructionCallback(String name, Runnable callback) {
|
||||||
}
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
public Object remove(String name) {
|
||||||
* (non-Javadoc)
|
return beans.remove(name);
|
||||||
* @see org.springframework.beans.factory.config.Scope#getConversationId()
|
}
|
||||||
*/
|
|
||||||
public String getConversationId() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
public Object resolveContextualObject(String key) {
|
||||||
* (non-Javadoc)
|
// TODO Auto-generated method stub
|
||||||
* @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String,
|
return null;
|
||||||
* java.lang.Runnable)
|
}
|
||||||
*/
|
|
||||||
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 resolveContextualObject(String key) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
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">
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
|
||||||
|
|
||||||
<context:component-scan base-package="example.scannable"
|
<context:component-scan base-package="test, example.scannable"
|
||||||
name-generator="org.springframework.context.annotation.TestBeanNameGenerator"/>
|
name-generator="org.springframework.context.annotation.TestBeanNameGenerator"/>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|
|
@ -17,63 +17,52 @@
|
||||||
package org.springframework.context.annotation4;
|
package org.springframework.context.annotation4;
|
||||||
|
|
||||||
import org.springframework.beans.TestBean;
|
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.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.BeanAge;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used to test the functionality of @FactoryMethod bean definitions declared inside
|
* Class used to test the functionality of factory method bean definitions
|
||||||
* a Spring @Component class.
|
* declared inside a Spring component class.
|
||||||
*
|
*
|
||||||
* @author Mark Pollack
|
* @author Mark Pollack
|
||||||
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class FactoryMethodComponent {
|
public final class FactoryMethodComponent {
|
||||||
|
|
||||||
private static TestBean staticTestBean = new TestBean("staticInstance",1);
|
private int i;
|
||||||
|
|
||||||
@Autowired @Qualifier("public")
|
|
||||||
public TestBean autowiredTestBean;
|
|
||||||
|
|
||||||
private static int i;
|
|
||||||
|
|
||||||
@FactoryMethod @Qualifier("static")
|
public static TestBean nullInstance() {
|
||||||
public static TestBean staticInstance()
|
|
||||||
{
|
|
||||||
return staticTestBean;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TestBean nullInstance()
|
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FactoryMethod @Qualifier("public")
|
@Bean @Qualifier("public")
|
||||||
public TestBean getPublicInstance() {
|
public TestBean publicInstance() {
|
||||||
return new TestBean("publicInstance");
|
return new TestBean("publicInstance");
|
||||||
}
|
}
|
||||||
|
|
||||||
@FactoryMethod @BeanAge(1)
|
@Bean @BeanAge(1)
|
||||||
protected TestBean getProtectedInstance() {
|
protected TestBean protectedInstance(@Qualifier("public") TestBean spouse, @Value("#{privateInstance.age}") String country) {
|
||||||
return new TestBean("protectedInstance", 1);
|
TestBean tb = new TestBean("protectedInstance", 1);
|
||||||
|
tb.setSpouse(tb);
|
||||||
|
tb.setCountry(country);
|
||||||
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FactoryMethod @Scope("prototype")
|
@Bean @Scope("prototype")
|
||||||
private TestBean getPrivateInstance() {
|
private TestBean privateInstance() {
|
||||||
return new TestBean("privateInstance", i++);
|
return new TestBean("privateInstance", i++);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FactoryMethod @Scope("request") @ScopedProxy
|
@Bean @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
public TestBean requestScopedInstance()
|
public TestBean requestScopedInstance() {
|
||||||
{
|
return new TestBean("requestScopedInstance", 3);
|
||||||
TestBean testBean = new TestBean("requestScopedInstance", 3);
|
|
||||||
return testBean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO method for test that fails if use @ScopedProxy with singleton scope.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,23 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package org.springframework.context.annotation4;
|
package org.springframework.context.annotation4;
|
||||||
|
|
||||||
import org.springframework.beans.TestBean;
|
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 to test that @FactoryMethods are detected only when inside a class with an @Component
|
||||||
* class annotation.
|
* class annotation.
|
||||||
*
|
*
|
||||||
* @author Mark Pollack
|
* @author Mark Pollack
|
||||||
*/
|
*/
|
||||||
public class SimpleBean {
|
public class SimpleBean {
|
||||||
|
|
||||||
|
// This should *not* recognized as a bean since it does not reside inside an @Component
|
||||||
// This should *not* recognized as a @FactoryMethod since it does not reside inside an @Component
|
@Bean
|
||||||
@FactoryMethod
|
|
||||||
public TestBean getPublicInstance() {
|
public TestBean getPublicInstance() {
|
||||||
return new TestBean("publicInstance");
|
return new TestBean("publicInstance");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2009 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.beans.factory.annotation;
|
package org.springframework.context.annotation5;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import example.scannable.FooDao;
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import java.lang.annotation.Target;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
/**
|
|
||||||
* 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 Juergen Hoeller
|
||||||
*
|
*/
|
||||||
* @author Mark Pollack
|
@Repository
|
||||||
* @since 3.0
|
@Primary @Lazy
|
||||||
* @see RequiredAnnotationBeanPostProcessor
|
public class OtherFooDao implements FooDao {
|
||||||
*/
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
public String findFoo(int id) {
|
||||||
@Target({ElementType.METHOD})
|
return "other";
|
||||||
public @interface FactoryMethod {
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -209,7 +209,7 @@ public class ApplicationContextExpressionTests {
|
||||||
@Value("${code} #{systemProperties.country}")
|
@Value("${code} #{systemProperties.country}")
|
||||||
public String country;
|
public String country;
|
||||||
|
|
||||||
@Qualifier("original")
|
@Autowired @Qualifier("original")
|
||||||
public TestBean tb;
|
public TestBean tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ public class ApplicationContextExpressionTests {
|
||||||
this.country = country;
|
this.country = country;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Qualifier("original")
|
@Autowired @Qualifier("original")
|
||||||
public void setTb(TestBean tb) {
|
public void setTb(TestBean tb) {
|
||||||
this.tb = tb;
|
this.tb = tb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ public abstract class AnnotationUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Class<?> superClass = clazz.getSuperclass();
|
Class<?> superClass = clazz.getSuperclass();
|
||||||
if (superClass == null || superClass == Object.class) {
|
if (superClass == null || superClass.equals(Object.class)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return findAnnotation(superClass, annotationType);
|
return findAnnotation(superClass, annotationType);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -70,8 +70,8 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||||
* annotation is defined.
|
* annotation is defined.
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getAnnotationAttributes(String annotationType);
|
Map<String, Object> getAnnotationAttributes(String annotationType);
|
||||||
|
|
||||||
|
|
||||||
// TODO return null would be more consistent with other methods if no match is found
|
// TODO return null would be more consistent with other methods if no match is found
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
package org.springframework.core.type;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Pollack
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
public interface MethodMetadata {
|
public interface MethodMetadata {
|
||||||
|
|
||||||
int getModifiers();
|
int getModifiers();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2009 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -43,16 +43,16 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
public Set<String> getAnnotationTypes() {
|
public Set<String> getAnnotationTypes() {
|
||||||
Set<String> types = new HashSet<String>();
|
Set<String> types = new HashSet<String>();
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
types.add(anns[i].annotationType().getName());
|
types.add(ann.annotationType().getName());
|
||||||
}
|
}
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasAnnotation(String annotationType) {
|
public boolean hasAnnotation(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
if (anns[i].annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,10 +61,10 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
|
|
||||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
if (anns[i].annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
Set<String> types = new HashSet<String>();
|
Set<String> types = new HashSet<String>();
|
||||||
Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
|
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
||||||
for (Annotation meta : metaAnns) {
|
for (Annotation meta : metaAnns) {
|
||||||
types.add(meta.annotationType().getName());
|
types.add(meta.annotationType().getName());
|
||||||
}
|
}
|
||||||
|
@ -76,8 +76,8 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
|
|
||||||
public boolean hasMetaAnnotation(String annotationType) {
|
public boolean hasMetaAnnotation(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
|
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
||||||
for (Annotation meta : metaAnns) {
|
for (Annotation meta : metaAnns) {
|
||||||
if (meta.annotationType().getName().equals(annotationType)) {
|
if (meta.annotationType().getName().equals(annotationType)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -89,8 +89,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
Annotation ann = anns[i];
|
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
return AnnotationUtils.getAnnotationAttributes(ann);
|
return AnnotationUtils.getAnnotationAttributes(ann);
|
||||||
}
|
}
|
||||||
|
@ -101,11 +100,9 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
||||||
Method[] methods = getIntrospectedClass().getMethods();
|
Method[] methods = getIntrospectedClass().getMethods();
|
||||||
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||||
for (int i = 0; i < methods.length; i++) {
|
for (Method method : methods) {
|
||||||
Method method = methods[i];
|
|
||||||
Annotation[] methodAnnotations = method.getAnnotations();
|
Annotation[] methodAnnotations = method.getAnnotations();
|
||||||
for (int j = 0; j < methodAnnotations.length; j++) {
|
for (Annotation ann : methodAnnotations) {
|
||||||
Annotation ann = methodAnnotations[j];
|
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
MethodMetadata mm = new StandardMethodMetadata(method);
|
MethodMetadata mm = new StandardMethodMetadata(method);
|
||||||
annotatedMethods.add(mm);
|
annotatedMethods.add(mm);
|
||||||
|
|
|
@ -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;
|
package org.springframework.core.type;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
@ -9,12 +25,18 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Pollack
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
public class StandardMethodMetadata implements MethodMetadata {
|
public class StandardMethodMetadata implements MethodMetadata {
|
||||||
|
|
||||||
private final Method introspectedMethod;
|
private final Method introspectedMethod;
|
||||||
|
|
||||||
public StandardMethodMetadata(Method method) {
|
public StandardMethodMetadata(Method method) {
|
||||||
|
Assert.notNull(method, "Method must not be null");
|
||||||
introspectedMethod = method;
|
introspectedMethod = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +47,7 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
Annotation ann = anns[i];
|
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
return AnnotationUtils.getAnnotationAttributes(ann);
|
return AnnotationUtils.getAnnotationAttributes(ann);
|
||||||
}
|
}
|
||||||
|
@ -37,14 +58,11 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||||
public Set<String> getAnnotationTypes() {
|
public Set<String> getAnnotationTypes() {
|
||||||
Set<String> types = new HashSet<String>();
|
Set<String> types = new HashSet<String>();
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
types.add(anns[i].annotationType().getName());
|
types.add(ann.annotationType().getName());
|
||||||
}
|
}
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public String getMethodName() {
|
public String getMethodName() {
|
||||||
return introspectedMethod.getName();
|
return introspectedMethod.getName();
|
||||||
|
@ -56,8 +74,8 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||||
|
|
||||||
public boolean hasAnnotation(String annotationType) {
|
public boolean hasAnnotation(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
if (anns[i].annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,10 +88,10 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||||
|
|
||||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
if (anns[i].annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
Set<String> types = new HashSet<String>();
|
Set<String> types = new HashSet<String>();
|
||||||
Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
|
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
||||||
for (Annotation meta : metaAnns) {
|
for (Annotation meta : metaAnns) {
|
||||||
types.add(meta.annotationType().getName());
|
types.add(meta.annotationType().getName());
|
||||||
}
|
}
|
||||||
|
@ -83,13 +101,11 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
||||||
|
|
||||||
//TODO can refactor into shared (utility) method with StandardAnnotationMetadata
|
//TODO can refactor into shared (utility) method with StandardAnnotationMetadata
|
||||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (Annotation ann : anns) {
|
||||||
Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
|
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
||||||
for (Annotation meta : metaAnns) {
|
for (Annotation meta : metaAnns) {
|
||||||
if (meta.annotationType().getName().equals(metaAnnotationType)) {
|
if (meta.annotationType().getName().equals(metaAnnotationType)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -99,8 +115,7 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getAnnotationTypesWithMetaAnnotation(
|
public Set<String> getAnnotationTypesWithMetaAnnotation(String qualifierClassName) {
|
||||||
String qualifierClassName) {
|
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.core.type.classreading;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -32,6 +33,7 @@ import org.springframework.asm.Type;
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
import org.springframework.asm.commons.EmptyVisitor;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,23 +61,18 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodVisitor visitMethod(int access, String name, String desc,
|
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||||
String signature, String[] exceptions) {
|
|
||||||
MethodMetadataReadingVisitor md = new MethodMetadataReadingVisitor(classLoader, name, access);
|
MethodMetadataReadingVisitor md = new MethodMetadataReadingVisitor(classLoader, name, access);
|
||||||
methodMetadataSet.add(md);
|
methodMetadataSet.add(md);
|
||||||
return md;
|
return md;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
||||||
final String className = Type.getType(desc).getClassName();
|
final String className = Type.getType(desc).getClassName();
|
||||||
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||||
return new EmptyVisitor() {
|
return new AnnotationVisitor() {
|
||||||
@Override
|
|
||||||
public void visit(String name, Object value) {
|
public void visit(String name, Object value) {
|
||||||
// Explicitly defined annotation attribute value.
|
// Explicitly defined annotation attribute value.
|
||||||
Object valueToUse = value;
|
Object valueToUse = value;
|
||||||
|
@ -89,7 +86,6 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
}
|
}
|
||||||
attributes.put(name, valueToUse);
|
attributes.put(name, valueToUse);
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public void visitEnum(String name, String desc, String value) {
|
public void visitEnum(String name, String desc, String value) {
|
||||||
Object valueToUse = value;
|
Object valueToUse = value;
|
||||||
try {
|
try {
|
||||||
|
@ -104,7 +100,36 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
}
|
}
|
||||||
attributes.put(name, valueToUse);
|
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() {
|
public void visitEnd() {
|
||||||
try {
|
try {
|
||||||
Class<?> annotationClass = classLoader.loadClass(className);
|
Class<?> annotationClass = classLoader.loadClass(className);
|
||||||
|
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package org.springframework.core.type.classreading;
|
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.Opcodes;
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
import org.springframework.asm.commons.EmptyVisitor;
|
||||||
import org.springframework.core.type.ClassMetadata;
|
import org.springframework.core.type.ClassMetadata;
|
||||||
|
@ -33,7 +37,7 @@ import org.springframework.util.ClassUtils;
|
||||||
* @author Ramnivas Laddad
|
* @author Ramnivas Laddad
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
class ClassMetadataReadingVisitor extends ClassAdapter implements ClassMetadata {
|
class ClassMetadataReadingVisitor implements ClassVisitor, ClassMetadata {
|
||||||
|
|
||||||
private String className;
|
private String className;
|
||||||
|
|
||||||
|
@ -49,12 +53,7 @@ class ClassMetadataReadingVisitor extends ClassAdapter implements ClassMetadata
|
||||||
|
|
||||||
private String[] interfaces;
|
private String[] interfaces;
|
||||||
|
|
||||||
public ClassMetadataReadingVisitor()
|
|
||||||
{
|
|
||||||
super(new EmptyVisitor());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) {
|
public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) {
|
||||||
this.className = ClassUtils.convertResourcePathToClassName(name);
|
this.className = ClassUtils.convertResourcePathToClassName(name);
|
||||||
this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
|
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) {
|
public void visitOuterClass(String owner, String name, String desc) {
|
||||||
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(owner);
|
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||||
if (outerName != null && this.className.equals(ClassUtils.convertResourcePathToClassName(name))) {
|
if (outerName != null && this.className.equals(ClassUtils.convertResourcePathToClassName(name))) {
|
||||||
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(outerName);
|
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() {
|
public String getClassName() {
|
||||||
return this.className;
|
return this.className;
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
package org.springframework.core.type.classreading;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -32,17 +32,26 @@ import org.springframework.asm.Opcodes;
|
||||||
import org.springframework.asm.Type;
|
import org.springframework.asm.Type;
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
import org.springframework.asm.commons.EmptyVisitor;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
public class MethodMetadataReadingVisitor extends MethodAdapter implements MethodMetadata {
|
/**
|
||||||
|
* @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<String, Map<String, Object>> attributesMap = new LinkedHashMap<String, Map<String, Object>>();
|
private final Map<String, Map<String, Object>> attributesMap = new LinkedHashMap<String, Map<String, Object>>();
|
||||||
|
|
||||||
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
||||||
|
|
||||||
private ClassLoader classLoader;
|
|
||||||
private String name;
|
|
||||||
private int access;
|
|
||||||
private boolean isStatic;
|
|
||||||
|
|
||||||
public MethodMetadataReadingVisitor(ClassLoader classLoader, String name, int access) {
|
public MethodMetadataReadingVisitor(ClassLoader classLoader, String name, int access) {
|
||||||
super(new EmptyVisitor());
|
super(new EmptyVisitor());
|
||||||
|
@ -52,6 +61,7 @@ public class MethodMetadataReadingVisitor extends MethodAdapter implements Metho
|
||||||
this.isStatic = ((access & Opcodes.ACC_STATIC) != 0);
|
this.isStatic = ((access & Opcodes.ACC_STATIC) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
return this.attributesMap.get(annotationType);
|
return this.attributesMap.get(annotationType);
|
||||||
}
|
}
|
||||||
|
@ -87,29 +97,22 @@ public class MethodMetadataReadingVisitor extends MethodAdapter implements Metho
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStatic() {
|
public boolean isStatic() {
|
||||||
return isStatic;
|
return this.isStatic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Set<String> getAnnotationTypesWithMetaAnnotation(String metaAnnotationType) {
|
public Set<String> getAnnotationTypesWithMetaAnnotation(String metaAnnotationType) {
|
||||||
|
|
||||||
///metaAnnotationMap.put(className, metaAnnotationTypeNames);
|
|
||||||
Set<String> annotationTypes = new LinkedHashSet<String>();
|
Set<String> annotationTypes = new LinkedHashSet<String>();
|
||||||
Set< Map.Entry<String, Set<String>> > metaValues = metaAnnotationMap.entrySet();
|
for (Map.Entry<String, Set<String>> entry : metaAnnotationMap.entrySet()) {
|
||||||
Iterator<Map.Entry<String, Set<String>> > metaIterator = metaValues.iterator();
|
|
||||||
while (metaIterator.hasNext())
|
|
||||||
{
|
|
||||||
Map.Entry<String, Set<String>> entry = metaIterator.next();
|
|
||||||
String attributeType = entry.getKey();
|
String attributeType = entry.getKey();
|
||||||
Set<String> metaAttributes = entry.getValue();
|
Set<String> metaAttributes = entry.getValue();
|
||||||
if (metaAttributes.contains(metaAnnotationType))
|
if (metaAttributes.contains(metaAnnotationType)) {
|
||||||
{
|
|
||||||
annotationTypes.add(attributeType);
|
annotationTypes.add(attributeType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return annotationTypes;
|
return annotationTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
||||||
final String className = Type.getType(desc).getClassName();
|
final String className = Type.getType(desc).getClassName();
|
||||||
|
@ -121,13 +124,27 @@ public class MethodMetadataReadingVisitor extends MethodAdapter implements Metho
|
||||||
attributes.put(name, value);
|
attributes.put(name, value);
|
||||||
}
|
}
|
||||||
@Override
|
@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() {
|
public void visitEnd() {
|
||||||
try {
|
try {
|
||||||
Class<?> annotationClass = classLoader.loadClass(className);
|
Class<?> annotationClass = classLoader.loadClass(className);
|
||||||
// Check declared default values of attributes in the annotation type.
|
// Check declared default values of attributes in the annotation type.
|
||||||
Method[] annotationAttributes = annotationClass.getMethods();
|
Method[] annotationAttributes = annotationClass.getMethods();
|
||||||
for (int i = 0; i < annotationAttributes.length; i++) {
|
for (Method annotationAttribute : annotationAttributes) {
|
||||||
Method annotationAttribute = annotationAttributes[i];
|
|
||||||
String attributeName = annotationAttribute.getName();
|
String attributeName = annotationAttribute.getName();
|
||||||
Object defaultValue = annotationAttribute.getDefaultValue();
|
Object defaultValue = annotationAttribute.getDefaultValue();
|
||||||
if (defaultValue != null && !attributes.containsKey(attributeName)) {
|
if (defaultValue != null && !attributes.containsKey(attributeName)) {
|
||||||
|
@ -150,5 +167,4 @@ public class MethodMetadataReadingVisitor extends MethodAdapter implements Metho
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue