+ Adding POC tooling integration points, namely AbstractConfigurationPostProcessor and allowing for tooling-specified ClassLoader for use with ASM parsing

+ Eliminated ModelMethod in favor of BeanMethod throughout
This commit is contained in:
Chris Beams 2009-03-05 20:08:15 +00:00
parent 61a1c4d0c6
commit eaf3a7cec4
22 changed files with 410 additions and 222 deletions

View File

@ -13,7 +13,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
* declare a no-arg constructor.
*
* @see FactoryMethod
* @see ModelMethod
* @see BeanMethod
*
* @author Chris Beams
*/
@ -31,6 +31,6 @@ public interface BeanDefinitionRegistrar {
/**
* Registers any bean definitions for <var>method</var> with <var>registry</var>.
*/
void register(ModelMethod method, BeanDefinitionRegistry registry);
void register(BeanMethod method, BeanDefinitionRegistry registry);
}

View File

@ -31,7 +31,7 @@ import org.springframework.util.Assert;
/** TODO: JAVADOC */
public final class ModelMethod implements Validatable {
public final class BeanMethod implements Validatable {
private final String name;
private final int modifiers;
@ -42,7 +42,7 @@ public final class ModelMethod implements Validatable {
private transient FactoryMethod factoryAnno;
private transient final List<Validator> validators = new ArrayList<Validator>();
public ModelMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
public BeanMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
Assert.hasText(name);
this.name = name;
@ -109,7 +109,7 @@ public final class ModelMethod implements Validatable {
/**
* Sets up bi-directional relationship between this method and its declaring class.
*
* @see ConfigurationClass#addMethod(ModelMethod)
* @see ConfigurationClass#addMethod(BeanMethod)
*/
public void setDeclaringClass(ConfigurationClass declaringClass) {
this.declaringClass = declaringClass;
@ -132,8 +132,8 @@ public final class ModelMethod implements Validatable {
}
public void validate(List<UsageError> errors) {
for (Validator validator : validators)
validator.validate(this, errors);
// for (Validator validator : validators)
// validator.validate(this, errors);
if (Modifier.isPrivate(getModifiers()))
errors.add(new PrivateMethodError());
@ -190,7 +190,7 @@ public final class ModelMethod implements Validatable {
return false;
if (getClass() != obj.getClass())
return false;
ModelMethod other = (ModelMethod) obj;
BeanMethod other = (BeanMethod) obj;
if (annotations == null) {
if (other.annotations != null)
return false;

View File

@ -24,6 +24,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.config.java.ext.Bean;
import org.springframework.util.Assert;
import sun.security.x509.Extension;
@ -55,7 +56,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
private Configuration metadata;
private HashSet<ModelMethod> methods = new HashSet<ModelMethod>();
private HashSet<BeanMethod> methods = new HashSet<BeanMethod>();
private HashSet<Annotation> pluginAnnotations = new HashSet<Annotation>();
@ -112,7 +113,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
setModifiers(modifiers);
}
public ConfigurationClass addMethod(ModelMethod method) {
public ConfigurationClass addMethod(BeanMethod method) {
method.setDeclaringClass(this);
methods.add(method);
return this;
@ -127,7 +128,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
return this;
}
public Set<ModelMethod> getMethods() {
public Set<BeanMethod> getMethods() {
return methods;
}
@ -184,7 +185,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
if (Modifier.isFinal(modifiers))
errors.add(new FinalConfigurationError());
for (ModelMethod method : methods)
for (BeanMethod method : methods)
method.validate(errors);
}
@ -299,7 +300,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
*/
public class IllegalBeanOverrideError extends UsageError {
private final ConfigurationClass authoritativeClass;
private final ModelMethod finalMethodInQuestion;
private final BeanMethod finalMethodInQuestion;
/**
* Creates a new IllegalBeanOverrideError object.
@ -310,7 +311,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
* @param finalMethodInQuestion the method that has been marked
* 'allowOverriding=false'
*/
public IllegalBeanOverrideError(ConfigurationClass violatingClass, ModelMethod finalMethodInQuestion) {
public IllegalBeanOverrideError(ConfigurationClass violatingClass, BeanMethod finalMethodInQuestion) {
super(violatingClass, -1);
this.authoritativeClass = ConfigurationClass.this;
this.finalMethodInQuestion = finalMethodInQuestion;
@ -329,9 +330,9 @@ public final class ConfigurationClass extends ModelClass implements Validatable
return getMethod(methodName) != null;
}
public ModelMethod getMethod(String methodName) {
public BeanMethod getMethod(String methodName) {
for (ModelMethod method : methods)
for (BeanMethod method : methods)
if (methodName.equals(method.getName()))
return method;

View File

@ -94,7 +94,7 @@ public final class ConfigurationModel implements Validatable {
* <var>errors</var>.
*
* @see ConfigurationClass#validate(java.util.List)
* @see ModelMethod#validate(java.util.List)
* @see BeanMethod#validate(java.util.List)
* @see Validator
* @see UsageError
*/
@ -107,7 +107,7 @@ public final class ConfigurationModel implements Validatable {
// depending on where they are registered (with the model, the class, or the method)
// they will be called directly or indirectly below
for (ConfigurationClass configClass : getAllConfigurationClasses()) {
for (ModelMethod method : configClass.getMethods()) {
for (BeanMethod method : configClass.getMethods()) {
for (Validator validator : method.getValidators()) {
if (validator.supports(method))
method.registerValidator(validator);

View File

@ -98,6 +98,7 @@ public class ModelClass implements BeanMetadataElement {
* Returns a resource path-formatted representation of the .java file that declares this
* class
*/
// TODO: return type should be Object here. Spring IDE will return a JDT representation...
public String getSource() {
return source;
}

View File

@ -131,9 +131,9 @@ public class Util {
* @see ClassUtils#getDefaultClassLoader()
*/
@SuppressWarnings("unchecked")
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName) {
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName, ClassLoader classLoader) {
try {
return (Class<? extends T>) ClassUtils.getDefaultClassLoader().loadClass(fqClassName);
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);
@ -152,10 +152,10 @@ public class Util {
*
* @throws RuntimeException if <var>pathToClass</var> does not exist
*/
public static InputStream getClassAsStream(String pathToClass) {
public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) {
String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX;
InputStream is = ClassUtils.getDefaultClassLoader().getResourceAsStream(classFileName);
InputStream is = classLoader.getResourceAsStream(classFileName);
if (is == null)
throw new RuntimeException(new FileNotFoundException("Class file [" + classFileName

View File

@ -26,11 +26,11 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.config.java.BeanMethod;
import org.springframework.config.java.Configuration;
import org.springframework.config.java.ConfigurationClass;
import org.springframework.config.java.ConfigurationModel;
import org.springframework.config.java.FactoryMethod;
import org.springframework.config.java.ModelMethod;
import org.springframework.config.java.Scopes;
import org.springframework.config.java.UsageError;
import org.springframework.config.java.Validator;
@ -164,11 +164,11 @@ public @interface Bean {
class BeanValidator implements Validator {
public boolean supports(Object object) {
return object instanceof ModelMethod;
return object instanceof BeanMethod;
}
public void validate(Object object, List<UsageError> errors) {
ModelMethod method = (ModelMethod) object;
BeanMethod method = (BeanMethod) object;
// TODO: re-enable for @ScopedProxy support
// if (method.getAnnotation(ScopedProxy.class) == null)
@ -204,7 +204,7 @@ class IllegalBeanOverrideValidator implements Validator {
ConfigurationClass[] allClasses = model.getAllConfigurationClasses();
for (int i = 0; i < allClasses.length; i++) {
for (ModelMethod method : allClasses[i].getMethods()) {
for (BeanMethod method : allClasses[i].getMethods()) {
Bean bean = method.getAnnotation(Bean.class);
if (bean == null || bean.allowOverriding())

View File

@ -33,7 +33,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
*
* @author Chris Beams
*/
class BeanMethodInterceptor extends AbstractMethodInterceptor {
public class BeanMethodInterceptor extends AbstractMethodInterceptor {
/**
* Enhances a {@link Bean @Bean} method to check the supplied BeanFactory for the

View File

@ -14,18 +14,17 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.config.java.BeanDefinitionRegistrar;
import org.springframework.config.java.BeanMethod;
import org.springframework.config.java.Configuration;
import org.springframework.config.java.ConfigurationClass;
import org.springframework.config.java.MalformedConfigurationException;
import org.springframework.config.java.ModelMethod;
import org.springframework.config.java.UsageError;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
// TODO: SJC-242 document BeanHandler
// TODO: SJC-242 make package-private
class BeanRegistrar implements BeanDefinitionRegistrar {
public class BeanRegistrar implements BeanDefinitionRegistrar {
private static final Log logger = LogFactory.getLog(BeanRegistrar.class);
@ -38,7 +37,7 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
}
// TODO: SJC-242 method too long
public void register(ModelMethod method, BeanDefinitionRegistry registry) {
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
ConfigurationClass configClass = method.getDeclaringClass();
@ -171,7 +170,10 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
}
private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, registry);
if(!(registry instanceof ConfigurableListableBeanFactory)) {
return registry.getBeanDefinition(beanName);
}
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) registry;
do {

View File

@ -39,11 +39,13 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.config.java.BeanDefinitionRegistrar;
import org.springframework.config.java.BeanMethod;
import org.springframework.config.java.Configuration;
import org.springframework.config.java.ConfigurationClass;
import org.springframework.config.java.ConfigurationModel;
import org.springframework.config.java.ModelMethod;
import org.springframework.config.java.NoOpInterceptor;
import org.springframework.config.java.ext.BeanMethodInterceptor;
import org.springframework.config.java.ext.BeanRegistrar;
/**
@ -81,11 +83,31 @@ public class ConfigurationEnhancer {
/**
* Creates a new {@link ConfigurationEnhancer} instance.
*/
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory) {
notNull(beanFactory, "beanFactory must be non-null");
notNull(model, "model must be non-null");
//notNull(model, "model must be non-null");
populateRegistrarsAndCallbacks(beanFactory, model);
//populateRegistrarsAndCallbacks(beanFactory, model);
registrars.add(new BeanRegistrar());
BeanMethodInterceptor beanMethodInterceptor = new BeanMethodInterceptor();
beanMethodInterceptor.setBeanFactory(beanFactory);
callbackInstances.add(beanMethodInterceptor);
registrars.add(new BeanDefinitionRegistrar() {
public boolean accepts(Method method) {
return true;
}
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
// no-op
}
});
callbackInstances.add(NoOpInterceptor.INSTANCE);
for (Callback callback : callbackInstances)
callbackTypes.add(callback.getClass());
}
@ -97,9 +119,9 @@ public class ConfigurationEnhancer {
*/
private void populateRegistrarsAndCallbacks(DefaultListableBeanFactory beanFactory,
ConfigurationModel model) {
for (ConfigurationClass configClass : model.getAllConfigurationClasses()) {
for (ModelMethod method : configClass.getMethods()) {
for (BeanMethod method : configClass.getMethods()) {
registrars.add(method.getRegistrar());
Callback callback = method.getCallback();
@ -118,7 +140,7 @@ public class ConfigurationEnhancer {
return true;
}
public void register(ModelMethod method, BeanDefinitionRegistry registry) {
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
// no-op
}
});

View File

@ -97,8 +97,8 @@ class AsmUtils {
* classpath
* @throws RuntimeException if an IOException occurs when creating the new ClassReader
*/
public static ClassReader newClassReader(String pathToClass) {
InputStream is = Util.getClassAsStream(pathToClass);
public static ClassReader newClassReader(String pathToClass, ClassLoader classLoader) {
InputStream is = Util.getClassAsStream(pathToClass, classLoader);
return newClassReader(is);
}

View File

@ -28,11 +28,12 @@ import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.Opcodes;
import org.springframework.config.java.BeanMethod;
import org.springframework.config.java.Configuration;
import org.springframework.config.java.ConfigurationClass;
import org.springframework.config.java.FactoryMethod;
import org.springframework.config.java.ModelClass;
import org.springframework.config.java.ModelMethod;
import org.springframework.config.java.ext.Bean;
/**
@ -49,6 +50,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
private final int modifiers;
private final ModelClass returnType;
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
private final ClassLoader classLoader;
private boolean isModelMethod = false;
private int lineNumber;
@ -62,13 +64,14 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
* @param modifiers modifiers for this method
*/
public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName,
String methodDescriptor, int modifiers) {
String methodDescriptor, int modifiers, ClassLoader classLoader) {
super(AsmUtils.EMPTY_VISITOR);
this.configClass = configClass;
this.methodName = methodName;
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor);
this.classLoader = classLoader;
this.modifiers = modifiers;
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor);
}
/**
@ -79,7 +82,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName);
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName, classLoader);
if (annoClass == null)
return super.visitAnnotation(annoTypeDesc, visible);
@ -88,7 +91,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
annotations.add(annotation);
return new MutableAnnotationVisitor(annotation);
return new MutableAnnotationVisitor(annotation, classLoader);
}
/**
@ -111,7 +114,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
@Override
public void visitEnd() {
for (Annotation anno : annotations) {
if (anno.annotationType().getAnnotation(FactoryMethod.class) != null) {
if (anno.annotationType().equals(Bean.class)) {
isModelMethod = true;
break;
}
@ -121,7 +124,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
return;
Annotation[] annoArray = annotations.toArray(new Annotation[] {});
ModelMethod method = new ModelMethod(methodName, modifiers, returnType, annoArray);
BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray);
method.setLineNumber(lineNumber);
configClass.addMethod(method);
}
@ -130,11 +133,11 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
* Determines return type from ASM <var>methodDescriptor</var> and determines whether
* that type is an interface.
*/
private static ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
private ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor));
// detect whether the return type is an interface
newClassReader(convertClassNameToResourcePath(returnType.getName())).accept(
newClassReader(convertClassNameToResourcePath(returnType.getName()), classLoader).accept(
new ClassAdapter(AsmUtils.EMPTY_VISITOR) {
@Override
public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) {

View File

@ -52,11 +52,13 @@ class ConfigurationClassVisitor extends ClassAdapter {
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
private boolean processInnerClasses = true;
private final ClassLoader classLoader;
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model) {
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model, ClassLoader classLoader) {
super(AsmUtils.EMPTY_VISITOR);
this.configClass = configClass;
this.model = model;
this.classLoader = classLoader;
}
public void setProcessInnerClasses(boolean processInnerClasses) {
@ -89,9 +91,9 @@ class ConfigurationClassVisitor extends ClassAdapter {
if (OBJECT_DESC.equals(superTypeDesc))
return;
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model);
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, classLoader);
ClassReader reader = AsmUtils.newClassReader(superTypeDesc);
ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader);
reader.accept(visitor, false);
}
@ -113,7 +115,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
if (Configuration.class.getName().equals(annoTypeName)) {
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class);
configClass.setMetadata(mutableConfiguration);
return new MutableAnnotationVisitor(mutableConfiguration);
return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
}
// TODO: re-enable for @Import support
@ -131,39 +133,41 @@ class ConfigurationClassVisitor extends ClassAdapter {
// -------------------------------------
// Detect @Plugin annotations
// -------------------------------------
PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor();
PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor(classLoader);
String className = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
ClassReader reader = AsmUtils.newClassReader(resourcePath);
ClassReader reader = AsmUtils.newClassReader(resourcePath, classLoader);
reader.accept(classVisitor, false);
if (!classVisitor.hasPluginAnnotation())
return super.visitAnnotation(annoTypeDesc, visible);
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
if (annoType == null)
return super.visitAnnotation(annoTypeDesc, visible);
Annotation pluginAnno = createMutableAnnotation(annoType);
configClass.addPluginAnnotation(pluginAnno);
return new MutableAnnotationVisitor(pluginAnno);
return new MutableAnnotationVisitor(pluginAnno, classLoader);
}
private static class PluginAnnotationDetectingClassVisitor extends ClassAdapter {
private boolean hasPluginAnnotation = false;
private final Extension pluginAnnotation = createMutableAnnotation(Extension.class);
private final ClassLoader classLoader;
public PluginAnnotationDetectingClassVisitor() {
public PluginAnnotationDetectingClassVisitor(ClassLoader classLoader) {
super(AsmUtils.EMPTY_VISITOR);
this.classLoader = classLoader;
}
@Override
public AnnotationVisitor visitAnnotation(String typeDesc, boolean arg1) {
if (Extension.class.getName().equals(AsmUtils.convertTypeDescriptorToClassName(typeDesc))) {
hasPluginAnnotation = true;
return new MutableAnnotationVisitor(pluginAnnotation);
return new MutableAnnotationVisitor(pluginAnnotation, classLoader);
}
return super.visitAnnotation(typeDesc, arg1);
}
@ -185,7 +189,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, String arg3,
String[] arg4) {
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers);
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers, classLoader);
}
/**
@ -215,11 +219,11 @@ class ConfigurationClassVisitor extends ClassAdapter {
ConfigurationClass innerConfigClass = new ConfigurationClass();
ConfigurationClassVisitor ccVisitor = new ConfigurationClassVisitor(innerConfigClass,
new ConfigurationModel());
ConfigurationClassVisitor ccVisitor =
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), classLoader);
ccVisitor.setProcessInnerClasses(false);
ClassReader reader = AsmUtils.newClassReader(name);
ClassReader reader = AsmUtils.newClassReader(name, classLoader);
reader.accept(ccVisitor, false);
if (innerClasses.containsKey(outerName))

View File

@ -21,7 +21,7 @@ import org.springframework.config.java.Configuration;
import org.springframework.config.java.ConfigurationClass;
import org.springframework.config.java.ConfigurationModel;
import org.springframework.config.java.Util;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ClassUtils;
/**
@ -44,6 +44,7 @@ public class ConfigurationParser {
* Model to be populated during calls to {@link #parse(Object, String)}
*/
private final ConfigurationModel model;
private final ClassLoader classLoader;
/**
* Creates a new parser instance that will be used to populate <var>model</var>.
@ -51,8 +52,9 @@ public class ConfigurationParser {
* @param model model to be populated by each successive call to
* {@link #parse(Object, String)}
*/
public ConfigurationParser(ConfigurationModel model) {
this.model = model;
public ConfigurationParser(ClassLoader classLoader) {
this.classLoader = classLoader;
this.model = new ConfigurationModel();
}
/**
@ -63,16 +65,21 @@ public class ConfigurationParser {
* @param configurationId may be null, but if populated represents the bean id (assumes
* that this configuration class was configured via XML)
*/
public void parse(ClassPathResource resource, String configurationId) {
public void parse(String className, String configurationId) {
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
String resourcePath = resource.getPath();
ClassReader configClassReader = AsmUtils.newClassReader(Util.getClassAsStream(resourcePath));
ClassReader configClassReader = AsmUtils.newClassReader(Util.getClassAsStream(resourcePath, classLoader));
ConfigurationClass configClass = new ConfigurationClass();
configClass.setBeanName(configurationId);
configClassReader.accept(new ConfigurationClassVisitor(configClass, model), false);
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, classLoader), false);
model.add(configClass);
}
public ConfigurationModel getConfigurationModel() {
return model;
}
}

View File

@ -36,11 +36,14 @@ class MutableAnnotationArrayVisitor extends AnnotationAdapter {
private final MutableAnnotation mutableAnno;
private final String attribName;
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName) {
private final ClassLoader classLoader;
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName, ClassLoader classLoader) {
super(AsmUtils.EMPTY_VISITOR);
this.mutableAnno = mutableAnno;
this.attribName = attribName;
this.classLoader = classLoader;
}
@Override
@ -51,14 +54,14 @@ class MutableAnnotationArrayVisitor extends AnnotationAdapter {
@Override
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
if (annoType == null)
return super.visitAnnotation(na, annoTypeDesc);
Annotation anno = createMutableAnnotation(annoType);
values.add(anno);
return new MutableAnnotationVisitor(anno);
return new MutableAnnotationVisitor(anno, classLoader);
}
@Override

View File

@ -37,6 +37,8 @@ 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
@ -49,13 +51,14 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
*
* @see MutableAnnotationUtils#createMutableAnnotation(Class)
*/
public MutableAnnotationVisitor(Annotation mutableAnno) {
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);
return new MutableAnnotationArrayVisitor(mutableAnno, attribName, classLoader);
}
public void visit(String attribName, Object attribValue) {
@ -65,7 +68,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
// the attribute type is Class -> load it and set it.
String fqClassName = ((Type) attribValue).getClassName();
Class<?> classVal = loadToolingSafeClass(fqClassName);
Class<?> classVal = loadToolingSafeClass(fqClassName, classLoader);
if (classVal == null)
return;
@ -82,7 +85,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor);
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName);
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName, classLoader);
if (enumClass == null)
return;
@ -93,7 +96,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) {
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDesc);
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
if (annoType == null)
return AsmUtils.EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc);
@ -107,7 +110,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
throw new RuntimeException(ex);
}
return new MutableAnnotationVisitor(anno);
return new MutableAnnotationVisitor(anno, classLoader);
}
public void visitEnd() {

View File

@ -0,0 +1,47 @@
package org.springframework.config.java.support;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.config.java.ConfigurationModel;
import org.springframework.config.java.internal.parsing.ConfigurationParser;
public abstract class AbstractConfigurationClassProcessor {
protected abstract BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs);
protected abstract ConfigurationParser createConfigurationParser();
protected abstract void validateModel(ConfigurationModel configModel);
protected BeanDefinitionRegistry processConfigBeanDefinitions() {
BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(false);
if(configBeanDefs.getBeanDefinitionCount() == 0)
return configBeanDefs; // nothing to do - don't waste any more cycles
ConfigurationModel configModel = createConfigurationModelFor(configBeanDefs);
validateModel(configModel);
return renderModelAsBeanDefinitions(configModel);
}
private ConfigurationModel createConfigurationModelFor(BeanDefinitionRegistry configBeanDefinitions) {
ConfigurationParser parser = createConfigurationParser();
for(String beanName : configBeanDefinitions.getBeanDefinitionNames()) {
BeanDefinition beanDef = configBeanDefinitions.getBeanDefinition(beanName);
String className = beanDef.getBeanClassName();
parser.parse(className, beanName);
}
return parser.getConfigurationModel();
}
private BeanDefinitionRegistry renderModelAsBeanDefinitions(ConfigurationModel configModel) {
return new ConfigurationModelBeanDefinitionReader().loadBeanDefinitions(configModel);
}
}

View File

@ -16,11 +16,6 @@
package org.springframework.config.java.support;
import static java.lang.String.*;
import static org.springframework.config.java.Util.*;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Comparator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -28,19 +23,16 @@ import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostPr
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.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.config.java.BeanMethod;
import org.springframework.config.java.Configuration;
import org.springframework.config.java.ConfigurationClass;
import org.springframework.config.java.ConfigurationModel;
import org.springframework.config.java.FactoryMethod;
import org.springframework.config.java.ModelMethod;
import org.springframework.config.java.plugin.Extension;
import org.springframework.config.java.plugin.ExtensionAnnotationBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
@ -57,29 +49,28 @@ class ConfigurationModelBeanDefinitionReader {
private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class);
private final DefaultListableBeanFactory beanFactory;
private BeanDefinitionRegistry registry;
/**
* Creates a new {@link ConfigurationModelBeanDefinitionReader} instance.
*/
public ConfigurationModelBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
public ConfigurationModelBeanDefinitionReader() {
}
/**
* Reads {@code model}, registering bean definitions with {@link #beanFactory} based on
* Reads {@code model}, registering bean definitions with {@link #registry} based on
* its contents.
*
* @return number of bean definitions generated
*/
public int loadBeanDefinitions(ConfigurationModel model) {
int initialBeanDefCount = beanFactory.getBeanDefinitionCount();
public BeanDefinitionRegistry loadBeanDefinitions(ConfigurationModel model) {
registry = new SimpleBeanDefinitionRegistry();
for (ConfigurationClass configClass : model.getAllConfigurationClasses())
loadBeanDefinitionsForConfigurationClass(configClass);
return beanFactory.getBeanDefinitionCount() - initialBeanDefCount;
return registry;
}
/**
@ -90,21 +81,23 @@ class ConfigurationModelBeanDefinitionReader {
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
doLoadBeanDefinitionForConfigurationClass(configClass);
for (ModelMethod method : configClass.getMethods())
for (BeanMethod method : configClass.getMethods())
loadBeanDefinitionsForModelMethod(method);
Annotation[] pluginAnnotations = configClass.getPluginAnnotations();
Arrays.sort(pluginAnnotations, new PluginComparator());
for (Annotation annotation : pluginAnnotations)
loadBeanDefinitionsForExtensionAnnotation(annotation);
// Annotation[] pluginAnnotations = configClass.getPluginAnnotations();
// Arrays.sort(pluginAnnotations, new PluginComparator());
// for (Annotation annotation : pluginAnnotations)
// loadBeanDefinitionsForExtensionAnnotation(beanDefs, annotation);
}
/**
* Registers the {@link Configuration} class itself as a bean definition.
* @param beanDefs
*/
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
Configuration metadata = configClass.getMetadata();
// TODO: think about implications with annotation-config
if (metadata.checkRequired() == true) {
RootBeanDefinition requiredAnnotationPostProcessor = new RootBeanDefinition();
Class<?> beanClass = RequiredAnnotationBeanPostProcessor.class;
@ -112,7 +105,7 @@ class ConfigurationModelBeanDefinitionReader {
requiredAnnotationPostProcessor.setBeanClass(beanClass);
requiredAnnotationPostProcessor
.setResourceDescription("ensures @Required methods have been invoked");
beanFactory.registerBeanDefinition(beanName, requiredAnnotationPostProcessor);
registry.registerBeanDefinition(beanName, requiredAnnotationPostProcessor);
}
GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
@ -122,13 +115,13 @@ class ConfigurationModelBeanDefinitionReader {
// consider the case where it's already been defined (probably in XML)
// and potentially has PropertyValues and ConstructorArgs)
if (beanFactory.containsBeanDefinition(configBeanName)) {
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) beanFactory
.getBeanDefinition(configBeanName);
AbstractBeanDefinition existing =
(AbstractBeanDefinition) registry.getBeanDefinition(configBeanName);
configBeanDef.setPropertyValues(existing.getPropertyValues());
configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues());
configBeanDef.setResource(existing.getResource());
@ -137,52 +130,52 @@ class ConfigurationModelBeanDefinitionReader {
if (log.isInfoEnabled())
log.info(format("Registering bean definition for @Configuration class %s", configBeanName));
beanFactory.registerBeanDefinition(configBeanName, configBeanDef);
registry.registerBeanDefinition(configBeanName, configBeanDef);
}
/**
* Reads a particular {@link ModelMethod}, registering bean definitions with
* {@link #beanFactory} based on its contents.
* Reads a particular {@link BeanMethod}, registering bean definitions with
* {@link #registry} based on its contents.
*
* @see FactoryMethod
*/
private void loadBeanDefinitionsForModelMethod(ModelMethod method) {
method.getRegistrar().register(method, beanFactory);
private void loadBeanDefinitionsForModelMethod(BeanMethod method) {
method.getRegistrar().register(method, registry);
}
@SuppressWarnings("unchecked")
private void loadBeanDefinitionsForExtensionAnnotation(Annotation anno) {
// ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory);
// there is a fixed assumption that in order for this annotation to have
// been registered in the first place, it must be meta-annotated with @Plugin
// assert this as an invariant now
Class<?> annoClass = anno.getClass();
Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class);
Assert.isTrue(extensionAnno != null, format("%s annotation is not annotated as a @%s", annoClass,
Extension.class.getSimpleName()));
Class<? extends ExtensionAnnotationBeanDefinitionRegistrar> extHandlerClass = extensionAnno.handler();
ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass);
extHandler.handle(anno, beanFactory);
}
private static class PluginComparator implements Comparator<Annotation> {
public int compare(Annotation a1, Annotation a2) {
Integer i1 = getOrder(a1);
Integer i2 = getOrder(a2);
return i1.compareTo(i2);
}
private Integer getOrder(Annotation a) {
Extension plugin = a.annotationType().getAnnotation(Extension.class);
if (plugin == null)
throw new IllegalArgumentException("annotation was not annotated with @Plugin: "
+ a.annotationType());
return plugin.order();
}
}
// @SuppressWarnings("unchecked")
// private void loadBeanDefinitionsForExtensionAnnotation(Map<String, BeanDefinition> beanDefs, Annotation anno) {
// // ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory);
// // there is a fixed assumption that in order for this annotation to have
// // been registered in the first place, it must be meta-annotated with @Plugin
// // assert this as an invariant now
// Class<?> annoClass = anno.getClass();
// Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class);
// Assert.isTrue(extensionAnno != null, format("%s annotation is not annotated as a @%s", annoClass,
// Extension.class.getSimpleName()));
//
// Class<? extends ExtensionAnnotationBeanDefinitionRegistrar> extHandlerClass = extensionAnno.handler();
//
// ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass);
// extHandler.handle(anno, beanFactory);
// }
//
// private static class PluginComparator implements Comparator<Annotation> {
// public int compare(Annotation a1, Annotation a2) {
// Integer i1 = getOrder(a1);
// Integer i2 = getOrder(a2);
// return i1.compareTo(i2);
// }
//
// private Integer getOrder(Annotation a) {
// Extension plugin = a.annotationType().getAnnotation(Extension.class);
// if (plugin == null)
// throw new IllegalArgumentException("annotation was not annotated with @Plugin: "
// + a.annotationType());
// return plugin.order();
// }
// }
}

View File

@ -15,10 +15,10 @@
*/
package org.springframework.config.java.support;
import static org.springframework.config.java.Util.*;
import static java.lang.String.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -27,6 +27,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.config.java.Configuration;
import org.springframework.config.java.ConfigurationModel;
@ -35,17 +36,21 @@ import org.springframework.config.java.UsageError;
import org.springframework.config.java.internal.enhancement.ConfigurationEnhancer;
import org.springframework.config.java.internal.parsing.ConfigurationParser;
import org.springframework.core.Ordered;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ClassUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.Assert;
/**
* {@link BeanFactoryPostProcessor} used for bootstrapping {@link Configuration
* @Configuration} beans from Spring XML files.
* @Configuration} beans. Usually used in conjunction with Spring XML files.
*/
public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProcessor {
public class ConfigurationPostProcessor extends AbstractConfigurationClassProcessor
implements Ordered, BeanFactoryPostProcessor {
private static final Log logger = LogFactory.getLog(ConfigurationPostProcessor.class);
private DefaultListableBeanFactory beanFactory;
/**
@ -55,58 +60,102 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
Assert.isInstanceOf(DefaultListableBeanFactory.class, clBeanFactory);
beanFactory = (DefaultListableBeanFactory) clBeanFactory;
BeanDefinitionRegistry factoryBeanDefs = processConfigBeanDefinitions();
for(String beanName : factoryBeanDefs.getBeanDefinitionNames())
beanFactory.registerBeanDefinition(beanName, factoryBeanDefs.getBeanDefinition(beanName));
enhanceConfigurationClasses();
}
@Override
protected ConfigurationParser createConfigurationParser() {
return new ConfigurationParser(beanFactory.getBeanClassLoader());
}
/**
* Searches <var>beanFactory</var> for any {@link Configuration} classes in order to
* parse and enhance them. Also registers any {@link BeanPostProcessor} objects
* necessary to fulfill JavaConfig requirements.
* @return map of all non-abstract {@link BeanDefinition}s in the enclosing {@link #beanFactory}
*/
public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
if (!(clBeanFactory instanceof DefaultListableBeanFactory))
throw new IllegalStateException("beanFactory must be of type "
+ DefaultListableBeanFactory.class.getSimpleName());
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory;
ConfigurationModel model = new ConfigurationModel();
parseAnyConfigurationClasses(beanFactory, model);
enhanceAnyConfigurationClasses(beanFactory, model);
}
private void parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
// linked map is important for maintaining predictable ordering of configuration
// classes.
// this is important in bean / value override situations.
LinkedHashMap<String, ClassPathResource> configClassResources = new LinkedHashMap<String, ClassPathResource>();
@Override
protected BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs) {
BeanDefinitionRegistry configBeanDefs = new DefaultListableBeanFactory();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
if (beanDef.isAbstract())
if (beanDef.isAbstract() && !includeAbstractBeanDefs)
continue;
if (isConfigClass(beanDef)) {
String path = ClassUtils.convertClassNameToResourcePath(beanDef.getBeanClassName());
configClassResources.put(beanName, new ClassPathResource(path));
}
if (isConfigClass(beanDef))
configBeanDefs.registerBeanDefinition(beanName, beanDef);
}
ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(
beanFactory);
ConfigurationParser parser = new ConfigurationParser(model);
for (String id : configClassResources.keySet())
parser.parse(configClassResources.get(id), id);
return configBeanDefs;
}
// /**
// * Searches <var>beanFactory</var> for any {@link Configuration} classes in order to
// * parse and enhance them. Also registers any {@link BeanPostProcessor} objects
// * necessary to fulfill JavaConfig requirements.
// */
// public void postProcessBeanFactory2(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
// Assert.isInstanceOf(DefaultListableBeanFactory.class, clBeanFactory);
// DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory;
//
// ConfigurationModel model = parseAnyConfigurationClasses(beanFactory);
//
// enhanceAnyConfigurationClasses(beanFactory, model);
// }
//
// private ConfigurationModel parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory) {
//
// // linked map is important for maintaining predictable ordering of configuration
// // classes.
// // this is important in bean / value override situations.
// LinkedHashMap<String, ClassPathResource> configClassResources = new LinkedHashMap<String, ClassPathResource>();
//
// for (String beanName : beanFactory.getBeanDefinitionNames()) {
// BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// if (beanDef.isAbstract())
// continue;
//
// if (isConfigClass(beanDef)) {
// String path = ClassUtils.convertClassNameToResourcePath(beanDef.getBeanClassName());
// configClassResources.put(beanName, new ClassPathResource(path));
// }
// }
//
// ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(
// beanFactory);
// ConfigurationParser parser = new ConfigurationParser(ClassUtils.getDefaultClassLoader());
//
// for (String id : configClassResources.keySet())
// parser.parse(configClassResources.get(id).getPath(), id);
//
// ConfigurationModel model = parser.getConfigurationModel();
//
// ArrayList<UsageError> errors = new ArrayList<UsageError>();
// model.validate(errors);
// if (errors.size() > 0)
// throw new MalformedConfigurationException(errors.toArray(new UsageError[] {}));
//
// modelBeanDefinitionReader.loadBeanDefinitions(model);
//
// return model;
// }
@Override
protected void validateModel(ConfigurationModel model) {
ArrayList<UsageError> errors = new ArrayList<UsageError>();
model.validate(errors);
if (errors.size() > 0)
throw new MalformedConfigurationException(errors.toArray(new UsageError[] {}));
modelBeanDefinitionReader.loadBeanDefinitions(model);
}
/**
@ -118,55 +167,43 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
* @see ConfigurationEnhancer
* @see BeanFactoryPostProcessor
*/
private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory,
ConfigurationModel model) {
private void enhanceConfigurationClasses() {
ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model);
int configClassesEnhanced = 0;
for (String beanName : beanFactory.getBeanDefinitionNames()) {
ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory);
BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(true);
for(String beanName : configBeanDefs.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
if (!isConfigClass(beanDef))
continue;
String configClassName = beanDef.getBeanClassName();
String enhancedClassName = enhancer.enhance(configClassName);
if (logger.isDebugEnabled())
logger
.debug(String
.format(
"Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'",
beanName, configClassName, enhancedClassName));
logger.debug(format("Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'",
beanName, configClassName, enhancedClassName));
beanDef.setBeanClassName(enhancedClassName);
configClassesEnhanced++;
}
if (configClassesEnhanced == 0)
logger.warn("Found no @Configuration class BeanDefinitions within " + beanFactory);
}
/**
* Determines whether the class for <var>beanDef</var> is a {@link Configuration}
* -annotated class. Returns false if <var>beanDef</var> has no class specified.
* <p>
* Note: the classloading used within should not be problematic or interfere with
* tooling in any way. BeanFactoryPostProcessing happens only during actual runtime
* processing via {@link JavaConfigApplicationContext} or via XML using
* {@link ConfigurationPostProcessor}. In any case, tooling (Spring IDE) will hook in at
* a lower level than this class and thus never encounter this classloading. Should this
* become problematic, it would not be too difficult to replace the following with ASM
* logic that traverses the class hierarchy in order to find whether the class is
* directly or indirectly annotated with {@link Configuration}.
*/
private static boolean isConfigClass(BeanDefinition beanDef) {
String className = beanDef.getBeanClassName();
return className != null && loadRequiredClass(className).isAnnotationPresent(Configuration.class);
if(className == null)
return false;
try {
MetadataReader metadataReader = new SimpleMetadataReaderFactory().getMetadataReader(className);
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
return annotationMetadata.hasAnnotation(Configuration.class.getName());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@ -11,4 +11,5 @@
<bean class="test.basic.AutowiredConfigurationTests$AutowiredConfig"/>
<bean class="test.basic.AutowiredConfigurationTests$ColorConfig"/>
</beans>

View File

@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostP
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.config.java.Configuration;
import org.springframework.config.java.MalformedConfigurationException;
import org.springframework.config.java.Scopes;
import org.springframework.config.java.ext.Bean;
import org.springframework.config.java.support.ConfigurationPostProcessor;
@ -100,4 +101,63 @@ public class BasicTests {
return new TestBean("bar");
}
}
@Test
public void legalBeanOverriding() {
{
BeanFactory factory = initBeanFactory(ConfigWithBeanThatAllowsOverriding.class, ConfigWithBeanOverride.class);
TestBean testBean = factory.getBean("testBean", TestBean.class);
assertThat(testBean.getName(), equalTo("overridden"));
}
// now try it the other way around - order matters!
{
BeanFactory factory = initBeanFactory(ConfigWithBeanOverride.class, ConfigWithBeanThatAllowsOverriding.class);
TestBean testBean = factory.getBean("testBean", TestBean.class);
assertThat(testBean.getName(), equalTo("original"));
}
}
@Test(expected=MalformedConfigurationException.class)
public void illegalBeanOverriding() {
initBeanFactory(ConfigWithBeanThatDisallowsOverriding.class, ConfigWithBeanOverride.class);
}
@Test
public void illegalBeanOverriding2() {
// should be okay when the class that disallows overriding is the one doing the overriding
initBeanFactory(ConfigWithBeanOverride.class, ConfigWithBeanThatDisallowsOverriding.class);
}
@Configuration
static class ConfigWithBeanThatAllowsOverriding {
@Bean
public TestBean testBean() {
return new TestBean("original");
}
}
@Configuration
static class ConfigWithBeanThatDisallowsOverriding {
@Bean(allowOverriding = false)
public TestBean testBean() {
return new TestBean("original");
}
}
@Configuration
static class ConfigWithBeanOverride {
@Bean
public TestBean testBean() {
return new TestBean("overridden");
}
}
}

View File

@ -10,4 +10,8 @@
<bean class="org.springframework.config.java.support.ConfigurationPostProcessor"/>
<bean class="test.basic.AutowiredConfigurationTests$ValueConfig"/>
<!--
<context:component-scan base-package="test.basic.value"/>
-->
</beans>