From eaf3a7cec49e3a6e8ed828dbb69b9a9b007b0b39 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Thu, 5 Mar 2009 20:08:15 +0000 Subject: [PATCH] + 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 --- .../config/java/BeanDefinitionRegistrar.java | 4 +- .../{ModelMethod.java => BeanMethod.java} | 12 +- .../config/java/ConfigurationClass.java | 17 +- .../config/java/ConfigurationModel.java | 4 +- .../config/java/ModelClass.java | 1 + .../org/springframework/config/java/Util.java | 8 +- .../springframework/config/java/ext/Bean.java | 8 +- .../java/ext/BeanMethodInterceptor.java | 2 +- .../config/java/ext/BeanRegistrar.java | 12 +- .../enhancement/ConfigurationEnhancer.java | 36 +++- .../java/internal/parsing/AsmUtils.java | 4 +- .../ConfigurationClassMethodVisitor.java | 21 +- .../parsing/ConfigurationClassVisitor.java | 32 +-- .../internal/parsing/ConfigurationParser.java | 21 +- .../MutableAnnotationArrayVisitor.java | 9 +- .../parsing/MutableAnnotationVisitor.java | 15 +- .../AbstractConfigurationClassProcessor.java | 47 +++++ ...onfigurationModelBeanDefinitionReader.java | 121 ++++++----- .../support/ConfigurationPostProcessor.java | 193 +++++++++++------- .../basic/AutowiredConfigurationTests.xml | 1 + .../src/test/java/test/basic/BasicTests.java | 60 ++++++ .../java/test/basic/ValueInjectionTests.xml | 4 + 22 files changed, 410 insertions(+), 222 deletions(-) rename org.springframework.config.java/src/main/java/org/springframework/config/java/{ModelMethod.java => BeanMethod.java} (95%) create mode 100644 org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanDefinitionRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanDefinitionRegistrar.java index 02bb0cf47f..262672a75a 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanDefinitionRegistrar.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanDefinitionRegistrar.java @@ -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 method with registry. */ - void register(ModelMethod method, BeanDefinitionRegistry registry); + void register(BeanMethod method, BeanDefinitionRegistry registry); } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java similarity index 95% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java index a8b50a7caa..9799e43304 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java @@ -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 validators = new ArrayList(); - 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 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; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java index cb6fabddf9..32bc0bff51 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java @@ -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 methods = new HashSet(); + private HashSet methods = new HashSet(); private HashSet pluginAnnotations = new HashSet(); @@ -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 getMethods() { + public Set 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; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java index 337966f7dc..b0580bdd2f 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java @@ -94,7 +94,7 @@ public final class ConfigurationModel implements Validatable { * errors. * * @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); diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java index 589014f664..678260ebf4 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java @@ -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; } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Util.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Util.java index 09424039da..3a9bdfd0d4 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Util.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Util.java @@ -131,9 +131,9 @@ public class Util { * @see ClassUtils#getDefaultClassLoader() */ @SuppressWarnings("unchecked") - public static Class loadToolingSafeClass(String fqClassName) { + public static Class loadToolingSafeClass(String fqClassName, ClassLoader classLoader) { try { - return (Class) ClassUtils.getDefaultClassLoader().loadClass(fqClassName); + return (Class) 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 pathToClass 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 diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java index a3f5ab699b..7a028ad49d 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java @@ -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 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()) diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanMethodInterceptor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanMethodInterceptor.java index 996dc4e9b4..d8f1829670 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanMethodInterceptor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanMethodInterceptor.java @@ -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 diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java index 5151b1c19f..b7e824ef10 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java @@ -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 { diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java index d7ac8284d3..f1320f14a9 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java @@ -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 } }); diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AsmUtils.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AsmUtils.java index 0676400319..00cf0407d5 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AsmUtils.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AsmUtils.java @@ -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); } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassMethodVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassMethodVisitor.java index d5afedcd70..6c32a39a98 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassMethodVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassMethodVisitor.java @@ -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 annotations = new ArrayList(); + 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 annoClass = loadToolingSafeClass(annoClassName); + Class 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 methodDescriptor 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) { diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassVisitor.java index d4f2bf65f1..751ee94ee6 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassVisitor.java @@ -52,11 +52,13 @@ class ConfigurationClassVisitor extends ClassAdapter { private final HashMap innerClasses = new HashMap(); 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 annoType = loadToolingSafeClass(annoTypeName); + Class 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)) diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationParser.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationParser.java index e1c7afef12..8161f3f1e1 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationParser.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationParser.java @@ -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 model. @@ -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; + } + } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationArrayVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationArrayVisitor.java index e5cba8fb16..536db2b863 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationArrayVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationArrayVisitor.java @@ -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 annoType = loadToolingSafeClass(annoTypeName); + Class 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 diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationVisitor.java index 26558da9e9..7217faeb6d 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationVisitor.java @@ -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 mutableAnno. 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 enumClass = loadToolingSafeClass(enumClassName); + Class 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 annoType = loadToolingSafeClass(annoTypeName); + Class 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() { diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java new file mode 100644 index 0000000000..c19d7db90e --- /dev/null +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java @@ -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); + } + +} diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModelBeanDefinitionReader.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModelBeanDefinitionReader.java index dc7d3d88ed..9a86d3aef7 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModelBeanDefinitionReader.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModelBeanDefinitionReader.java @@ -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 extHandlerClass = extensionAnno.handler(); - - ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass); - extHandler.handle(anno, beanFactory); - } - - private static class PluginComparator implements Comparator { - 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 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 extHandlerClass = extensionAnno.handler(); +// +// ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass); +// extHandler.handle(anno, beanFactory); +// } +// +// private static class PluginComparator implements Comparator { +// 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(); +// } +// } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java index cd763cddd7..a4dc8caf38 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java @@ -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 beanFactory 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 configClassResources = new LinkedHashMap(); - + @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 beanFactory 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 configClassResources = new LinkedHashMap(); +// +// 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 errors = new ArrayList(); +// 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 errors = new ArrayList(); 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 beanDef is a {@link Configuration} * -annotated class. Returns false if beanDef has no class specified. - *

- * 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); + } } } diff --git a/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.xml b/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.xml index 124dee31a3..ffdd961b42 100644 --- a/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.xml +++ b/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.xml @@ -11,4 +11,5 @@ + diff --git a/org.springframework.config.java/src/test/java/test/basic/BasicTests.java b/org.springframework.config.java/src/test/java/test/basic/BasicTests.java index 116d18c89b..8e499b0579 100644 --- a/org.springframework.config.java/src/test/java/test/basic/BasicTests.java +++ b/org.springframework.config.java/src/test/java/test/basic/BasicTests.java @@ -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"); + } + } + } diff --git a/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml b/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml index a2e1988906..d89442c019 100644 --- a/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml +++ b/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml @@ -10,4 +10,8 @@ + +