diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/BeanMethod.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/BeanMethod.java index 5006bdb5d7b90be75589cc9ca900f12712bbae81..cf76944bd5d33ae59926fd970b3d02337a81785f 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/BeanMethod.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/BeanMethod.java @@ -23,24 +23,31 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; +import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.config.java.Bean; +import org.springframework.config.java.Configuration; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; -import org.springframework.core.io.FileSystemResource; import org.springframework.util.Assert; -final class BeanMethod { +/** + * Represents a {@link Configuration} class method marked with the {@link Bean} annotation. + * + * @author Chris Beams + */ +final class BeanMethod implements BeanMetadataElement { private final String name; private final int modifiers; private final ModelClass returnType; private final List annotations = new ArrayList(); + private transient ConfigurationClass declaringClass; - private transient int lineNumber; + private transient Object source; public BeanMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) { Assert.hasText(name); @@ -104,7 +111,7 @@ final class BeanMethod { /** * Set up a bi-directional relationship between this method and its declaring class. * - * @see ConfigurationClass#addMethod(BeanMethod) + * @see ConfigurationClass#addBeanMethod(BeanMethod) */ public void setDeclaringClass(ConfigurationClass declaringClass) { this.declaringClass = declaringClass; @@ -114,12 +121,16 @@ final class BeanMethod { return declaringClass; } - public void setLineNumber(int lineNumber) { - this.lineNumber = lineNumber; + public void setSource(Object source) { + this.source = source; + } + + public Object getSource() { + return source; } - public int getLineNumber() { - return lineNumber; + public Location getLocation() { + return new Location(declaringClass.getLocation().getResource(), getSource()); } public void validate(ProblemReporter problemReporter) { @@ -188,7 +199,7 @@ final class BeanMethod { public class PrivateMethodError extends Problem { public PrivateMethodError() { super(format("method '%s' may not be private", getName()), - new Location(new FileSystemResource("/dev/null"))); + BeanMethod.this.getLocation()); } } @@ -196,7 +207,7 @@ final class BeanMethod { public class FinalMethodError extends Problem { public FinalMethodError() { super(format("method '%s' may not be final. remove the final modifier to continue", getName()), - new Location(new FileSystemResource("/dev/null"))); + BeanMethod.this.getLocation()); } } @@ -204,7 +215,7 @@ final class BeanMethod { public InvalidScopedProxyDeclarationError(BeanMethod method) { super(format("method %s contains an invalid annotation declaration: scoped proxies " + "cannot be created for singleton/prototype beans", method.getName()), - new Location(new FileSystemResource("/dev/null"))); + BeanMethod.this.getLocation()); } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClass.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClass.java index d6f974da2ac95beb9ff19c8bc316f7dafd2cf6aa..a5fafab2049fb88b7383845a3437229f2cd95a90 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClass.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClass.java @@ -17,21 +17,16 @@ package org.springframework.config.java.support; import static java.lang.String.*; -import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Set; -import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.config.java.Bean; import org.springframework.config.java.Configuration; -import org.springframework.core.io.FileSystemResource; import org.springframework.util.Assert; -import sun.security.x509.Extension; - /** * Abstract representation of a user-defined {@link Configuration @Configuration} class. @@ -41,137 +36,65 @@ import sun.security.x509.Extension; * (for the purpose of tooling with Spring IDE). * * @author Chris Beams + * @see ConfigurationModel + * @see BeanMethod + * @see ConfigurationParser */ final class ConfigurationClass extends ModelClass { private String beanName; - private int modifiers; - - private Configuration metadata; - + private Configuration configurationAnnotation; private HashSet methods = new HashSet(); - - private HashSet pluginAnnotations = new HashSet(); - private ConfigurationClass declaringClass; - public ConfigurationClass() { - } - - // TODO: get rid of constructors used only for testing. put in testing util. - /** - * Creates a new ConfigurationClass named className. - * - * @param name fully-qualified Configuration class being represented - * - * @see #setClassName(String) - */ - ConfigurationClass(String name) { - this(name, null, defaultAnnotation(), 0); - } - - ConfigurationClass(String name, Configuration metadata) { - this(name, null, metadata, 0); - } - - ConfigurationClass(String name, int modifiers) { - this(name, null, defaultAnnotation(), modifiers); - } - - private static Configuration defaultAnnotation() { - @Configuration - class Prototype { - } - return Prototype.class.getAnnotation(Configuration.class); - } - - /** - * Creates a new ConfigurationClass object. - * - * @param name Fully qualified name of the class being represented - * @param id Bean name/id (if any) of this configuration class. used only in the case of - * XML integration where {@link Configuration} beans may have a user-specified - * id. - * @param metadata Configuration annotation resident on this class. May be null - * indicating that the user specified this class to be processed but failed to - * properly annotate it. - * @param modifiers Per {@link java.lang.reflect.Modifier} - */ - public ConfigurationClass(String name, String id, Configuration metadata, int modifiers) { - super(name); - Assert.hasText(name, "Configuration class name must have text"); - - setBeanName(id); - setMetadata(metadata); - setModifiers(modifiers); - } - - public ConfigurationClass addMethod(BeanMethod method) { - method.setDeclaringClass(this); - methods.add(method); - return this; - } - public String getBeanName() { return beanName == null ? getName() : beanName; } - public ConfigurationClass setBeanName(String id) { + public void setBeanName(String id) { this.beanName = id; - return this; } - public Set getMethods() { - return methods; + public int getModifiers() { + return modifiers; } - public Annotation[] getPluginAnnotations() { - return pluginAnnotations.toArray(new Annotation[pluginAnnotations.size()]); + public void setModifiers(int modifiers) { + Assert.isTrue(modifiers >= 0, "modifiers must be non-negative"); + this.modifiers = modifiers; } - /** - * Add a {@link Extension @Plugin}-annotated annotation to this configuration class. - * - * @param pluginAnno type-level Plugin annotation - */ - public ConfigurationClass addPluginAnnotation(Annotation pluginAnno) { - pluginAnnotations.add(pluginAnno); - return this; + public Configuration getConfigurationAnnotation() { + return this.configurationAnnotation; } - public ConfigurationClass setDeclaringClass(ConfigurationClass configurationClass) { - this.declaringClass = configurationClass; - return this; + public void setConfigurationAnnotation(Configuration configAnno) { + Assert.notNull(configAnno, "configuration annotation must be non-null"); + this.configurationAnnotation = configAnno; } - public ConfigurationClass getDeclaringClass() { - return declaringClass; - } - - public int getModifiers() { - return modifiers; + public Set getBeanMethods() { + return methods; } - public ConfigurationClass setModifiers(int modifiers) { - Assert.isTrue(modifiers >= 0, "modifiers must be non-negative"); - this.modifiers = modifiers; + public ConfigurationClass addBeanMethod(BeanMethod method) { + method.setDeclaringClass(this); + methods.add(method); return this; } - public Configuration getMetadata() { - return this.metadata; + public ConfigurationClass getDeclaringClass() { + return declaringClass; } - public ConfigurationClass setMetadata(Configuration configAnno) { - this.metadata = configAnno; - return this; + public void setDeclaringClass(ConfigurationClass configurationClass) { + this.declaringClass = configurationClass; } public void validate(ProblemReporter problemReporter) { - // configuration classes must be annotated with @Configuration - if (metadata == null) + if (configurationAnnotation == null) problemReporter.error(new NonAnnotatedConfigurationProblem()); // a configuration class may not be final (CGLIB limitation) @@ -193,10 +116,9 @@ final class ConfigurationClass extends ModelClass { int result = super.hashCode(); result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode()); result = prime * result + ((beanName == null) ? 0 : beanName.hashCode()); - result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); + result = prime * result + ((configurationAnnotation == null) ? 0 : configurationAnnotation.hashCode()); result = prime * result + ((methods == null) ? 0 : methods.hashCode()); result = prime * result + modifiers; - result = prime * result + ((pluginAnnotations == null) ? 0 : pluginAnnotations.hashCode()); return result; } @@ -219,10 +141,10 @@ final class ConfigurationClass extends ModelClass { return false; } else if (!beanName.equals(other.beanName)) return false; - if (metadata == null) { - if (other.metadata != null) + if (configurationAnnotation == null) { + if (other.configurationAnnotation != null) return false; - } else if (!metadata.equals(other.metadata)) + } else if (!configurationAnnotation.equals(other.configurationAnnotation)) return false; if (methods == null) { if (other.methods != null) @@ -231,37 +153,30 @@ final class ConfigurationClass extends ModelClass { return false; if (modifiers != other.modifiers) return false; - if (pluginAnnotations == null) { - if (other.pluginAnnotations != null) - return false; - } else if (!pluginAnnotations.equals(other.pluginAnnotations)) - return false; return true; } /** Configuration classes must be annotated with {@link Configuration @Configuration}. */ - public class NonAnnotatedConfigurationProblem extends Problem { + class NonAnnotatedConfigurationProblem extends Problem { - public NonAnnotatedConfigurationProblem() { + NonAnnotatedConfigurationProblem() { super(format("%s was specified as a @Configuration class but was not actually annotated " + "with @Configuration. Annotate the class or do not attempt to process it.", getSimpleName()), - new Location(new FileSystemResource("/dev/null")) - ); + ConfigurationClass.this.getLocation()); } } /** Configuration classes must be non-final to accommodate CGLIB subclassing. */ - public class FinalConfigurationProblem extends Problem { + class FinalConfigurationProblem extends Problem { - public FinalConfigurationProblem() { + FinalConfigurationProblem() { super(format("@Configuration class [%s] may not be final. Remove the final modifier to continue.", - ConfigurationClass.this.getSimpleName()), - new Location(new FileSystemResource("/dev/null")) - ); + getSimpleName()), + ConfigurationClass.this.getLocation()); } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassMethodVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassMethodVisitor.java index f33ec10b3a4bc0627a3700d420408fbee46cc181..b22d9144a5e8b11e15cf9bf4289b290f63c4d924 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassMethodVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassMethodVisitor.java @@ -48,7 +48,6 @@ class ConfigurationClassMethodVisitor extends MethodAdapter { private final ArrayList annotations = new ArrayList(); private final ClassLoader classLoader; - private boolean isModelMethod = false; private int lineNumber; /** @@ -104,24 +103,20 @@ class ConfigurationClassMethodVisitor extends MethodAdapter { /** * Parses through all {@link #annotations} on this method in order to determine whether - * it is a {@link Bean} method or not and if so adds it to the enclosing {@link #configClass}. + * it is a {@link Bean} method and if so adds it to the enclosing {@link #configClass}. */ @Override public void visitEnd() { for (Annotation anno : annotations) { - if (anno.annotationType().equals(Bean.class)) { - isModelMethod = true; + if (Bean.class.equals(anno.annotationType())) { + // this method is annotated with @Bean -> add it to the ConfigurationClass model + Annotation[] annoArray = annotations.toArray(new Annotation[] {}); + BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray); + method.setSource(lineNumber); + configClass.addBeanMethod(method); break; } } - - if (!isModelMethod) - return; - - Annotation[] annoArray = annotations.toArray(new Annotation[] {}); - BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray); - method.setLineNumber(lineNumber); - configClass.addMethod(method); } /** diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassVisitor.java index ffca110287fa5db8180f281a1c9e2212588fd923..8615ae82de190222e63eb16963e378022697a0bd 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassVisitor.java @@ -17,10 +17,8 @@ package org.springframework.config.java.support; import static java.lang.String.*; import static org.springframework.config.java.support.MutableAnnotationUtils.*; -import static org.springframework.config.java.support.Util.*; import static org.springframework.util.ClassUtils.*; -import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Stack; @@ -29,13 +27,12 @@ import org.springframework.asm.ClassAdapter; import org.springframework.asm.ClassReader; import org.springframework.asm.MethodVisitor; import org.springframework.asm.Opcodes; -import org.springframework.asm.commons.EmptyVisitor; import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.config.java.Configuration; import org.springframework.config.java.Import; -import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.ClassPathResource; /** @@ -51,10 +48,11 @@ class ConfigurationClassVisitor extends ClassAdapter { private final ConfigurationClass configClass; private final ConfigurationModel model; private final ProblemReporter problemReporter; + private final ClassLoader classLoader; + private final HashMap innerClasses = new HashMap(); private boolean processInnerClasses = true; - private final ClassLoader classLoader; public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) { @@ -120,77 +118,23 @@ class ConfigurationClassVisitor extends ClassAdapter { if (Configuration.class.getName().equals(annoTypeName)) { Configuration mutableConfiguration = createMutableAnnotation(Configuration.class); - configClass.setMetadata(mutableConfiguration); + configClass.setConfigurationAnnotation(mutableConfiguration); return new MutableAnnotationVisitor(mutableConfiguration, classLoader); } if (Import.class.getName().equals(annoTypeName)) { ImportStack importStack = ImportStackHolder.getImportStack(); - if (importStack.contains(configClass)) { - //throw new CircularImportException(configClass, importStack); - problemReporter.error(new CircularImportProblem(configClass, importStack)); - return new EmptyVisitor(); - } - - importStack.push(configClass); - - return new ImportAnnotationVisitor(model, problemReporter, classLoader); - } - - /* ------------------------------------- - // Detect @Extension annotations - // ------------------------------------- - PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor(classLoader); - - String className = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); - String resourcePath = ClassUtils.convertClassNameToResourcePath(className); - ClassReader reader = AsmUtils.newClassReader(resourcePath, classLoader); - reader.accept(classVisitor, false); - - if (!classVisitor.hasPluginAnnotation()) - return super.visitAnnotation(annoTypeDesc, visible); - */ - - Class annoType = loadToolingSafeClass(annoTypeName, classLoader); - - if (annoType == null) - return super.visitAnnotation(annoTypeDesc, visible); - - Annotation pluginAnno = createMutableAnnotation(annoType); - configClass.addPluginAnnotation(pluginAnno); - return new MutableAnnotationVisitor(pluginAnno, classLoader); - } - - /* Support for @Extension annotation processing - private static class PluginAnnotationDetectingClassVisitor extends ClassAdapter { - private boolean hasPluginAnnotation = false; - private final Extension pluginAnnotation = createMutableAnnotation(Extension.class); - private final ClassLoader classLoader; - - 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, classLoader); + if (!importStack.contains(configClass)) { + importStack.push(configClass); + return new ImportAnnotationVisitor(model, problemReporter, classLoader); } - return super.visitAnnotation(typeDesc, arg1); - } - public boolean hasPluginAnnotation() { - return hasPluginAnnotation; + problemReporter.error(new CircularImportProblem(configClass, importStack)); } - public Extension getPluginAnnotation() { - return pluginAnnotation; - } + return super.visitAnnotation(annoTypeDesc, visible); } - */ /** * Delegates all {@link Configuration @Configuration} class method parsing to @@ -241,7 +185,7 @@ class ConfigurationClassVisitor extends ClassAdapter { innerConfigClass.setDeclaringClass(innerClasses.get(outerName)); // is the inner class a @Configuration class? If so, add it to the list - if (innerConfigClass.getMetadata() != null) + if (innerConfigClass.getConfigurationAnnotation() != null) innerClasses.put(name, innerConfigClass); } @@ -255,13 +199,14 @@ class ConfigurationClassVisitor extends ClassAdapter { */ class CircularImportProblem extends Problem { - public CircularImportProblem(ConfigurationClass attemptedImport, Stack currentImportStack) { + CircularImportProblem(ConfigurationClass attemptedImport, Stack importStack) { super(format("A circular @Import has been detected: " + "Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " + "already present in the current import stack [%s]", - currentImportStack.peek().getSimpleName(), attemptedImport.getSimpleName(), - attemptedImport.getSimpleName(), currentImportStack), - new Location(new FileSystemResource("/dev/null")) + importStack.peek().getSimpleName(), attemptedImport.getSimpleName(), + attemptedImport.getSimpleName(), importStack), + new Location(new ClassPathResource(convertClassNameToResourcePath(importStack.peek().getName())), + importStack.peek().getSource()) ); } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModel.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModel.java index 1fa5b9c2c792c5d35e53edb1d830725fe18cd7f9..c2ea8230a1db260ed02aec79c7f29f2cde2eb149 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModel.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModel.java @@ -19,11 +19,8 @@ import static java.lang.String.*; import java.util.ArrayList; -import org.springframework.beans.factory.parsing.Location; -import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.config.java.Configuration; -import org.springframework.core.io.FileSystemResource; /** @@ -60,31 +57,6 @@ final class ConfigurationModel { return this; } - /** - * Return configuration classes that have been directly added to this model. - * - * @see #getAllConfigurationClasses() - */ - public ConfigurationClass[] getConfigurationClasses() { - return configurationClasses.toArray(new ConfigurationClass[] {}); - } - - // /** - // * Return all configuration classes, including all imported configuration classes. - // This method - // * should be generally preferred over {@link #getConfigurationClasses()} - // * - // * @see #getConfigurationClasses() - // */ - // public ConfigurationClass[] getAllConfigurationClasses() { - // ArrayList allConfigClasses = new ArrayList(); - // - // for (ConfigurationClass configClass : configurationClasses) - // allConfigClasses.addAll(configClass.getSelfAndAllImports()); - // - // return allConfigClasses.toArray(new ConfigurationClass[allConfigClasses.size()]); - // } - public ConfigurationClass[] getAllConfigurationClasses() { return configurationClasses.toArray(new ConfigurationClass[configurationClasses.size()]); } @@ -94,37 +66,8 @@ final class ConfigurationModel { * errors. * * @see ConfigurationClass#validate - * @see BeanMethod#validate */ public void validate(ProblemReporter problemReporter) { - // user must specify at least one configuration - if (configurationClasses.isEmpty()) - problemReporter.error(new EmptyModelError()); - - // TODO: prune this -// // check for any illegal @Bean overriding -// ConfigurationClass[] allClasses = getAllConfigurationClasses(); -// for (int i = 0; i < allClasses.length; i++) { -// for (BeanMethod method : allClasses[i].getMethods()) { -// Bean bean = method.getAnnotation(Bean.class); -// -// if (bean == null || bean.allowOverriding()) -// continue; -// -// for (int j = i + 1; j < allClasses.length; j++) -// if (allClasses[j].hasMethod(method.getName())) -// problemReporter.error( -// new Problem( -// allClasses[i].new IllegalBeanOverrideError(allClasses[j], method).getDescription(), -// new Location(new ClassPathResource(allClasses[i].getName().replace('.', '/').concat(".class"))) -// ) -// ); -// } -// } - - // each individual configuration class must be well-formed - // note that each configClass detects usage errors on its imports recursively - // note that each configClass will recursively process its respective methods for (ConfigurationClass configClass : configurationClasses) configClass.validate(problemReporter); } @@ -159,13 +102,4 @@ final class ConfigurationModel { return true; } - - public class EmptyModelError extends Problem { - public EmptyModelError() { - super(format("Configuration model was empty. Make sure at least one " - + "@%s class has been specified.", Configuration.class.getSimpleName()), - new Location(new FileSystemResource("/dev/null"))); - } - } - } \ No newline at end of file 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 e0cd8c1ab24517aabbc785ebb081b40227a1ae40..c58bb45c14179278db494f757aece980fc930c5f 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 @@ -74,7 +74,7 @@ class ConfigurationModelBeanDefinitionReader { private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { doLoadBeanDefinitionForConfigurationClass(configClass); - for (BeanMethod method : configClass.getMethods()) + for (BeanMethod method : configClass.getBeanMethods()) loadBeanDefinitionsForModelMethod(method); // Annotation[] pluginAnnotations = configClass.getPluginAnnotations(); diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationParser.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationParser.java index dc8a36bc943068a93f8e575e0dd4f774df0274c1..96915e865f578cefe924f97ec4c315450d7d0e26 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationParser.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationParser.java @@ -31,10 +31,10 @@ import org.springframework.util.ClassUtils; * from the concern of registering {@link BeanDefinition} objects based on the content of * that model. * - * @see org.springframework.config.java.support.ConfigurationModel - * @see org.springframework.config.java.support.ConfigurationModelBeanDefinitionReader - * * @author Chris Beams + * @since 3.0 + * @see ConfigurationModel + * @see ConfigurationModelBeanDefinitionReader */ public class ConfigurationParser { diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ModelClass.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ModelClass.java index 929e44ac5ceb5a20562520d3e565718b0ec423a3..c872077ba4e1bb473250fa9e7ca6defe3fc33043 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ModelClass.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ModelClass.java @@ -15,23 +15,28 @@ */ package org.springframework.config.java.support; +import static org.springframework.util.ClassUtils.*; + import org.springframework.beans.BeanMetadataElement; -import org.springframework.config.java.Configuration; -import org.springframework.util.Assert; +import org.springframework.beans.factory.parsing.Location; +import org.springframework.core.io.ClassPathResource; import org.springframework.util.ClassUtils; /** - * Abstract representation of a class, free from java reflection. Base class used within the - * internal JavaConfig metamodel for representing {@link Configuration} classes. + * Representation of a class, free from java reflection, + * populated by {@link ConfigurationParser}. * * @author Chris Beams + * @see ConfigurationModel + * @see ConfigurationClass + * @see ConfigurationParser */ class ModelClass implements BeanMetadataElement { private String name; private boolean isInterface; - private String source; + private transient Object source; /** * Creates a new and empty ModelClass instance. @@ -98,8 +103,7 @@ 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() { + public Object getSource() { return source; } @@ -109,8 +113,11 @@ class ModelClass implements BeanMetadataElement { * @param source resource path to the .java file that declares this class. */ public void setSource(Object source) { - Assert.isInstanceOf(String.class, source); - this.source = (String) source; + this.source = source; + } + + public Location getLocation() { + return new Location(new ClassPathResource(convertClassNameToResourcePath(getName())), getSource()); } /** @@ -160,5 +167,3 @@ class ModelClass implements BeanMetadataElement { } } - -// TODO: Consider eliminating in favor of just ConfigurationClass diff --git a/org.springframework.config.java/src/test/java/org/springframework/config/java/support/AbstractCircularImportDetectionTests.java b/org.springframework.config.java/src/test/java/org/springframework/config/java/support/AbstractCircularImportDetectionTests.java index 45f9e31b63519e78fe82c90d75f17a849c161974..2f801063730b4973393ec348307d491e14841653 100644 --- a/org.springframework.config.java/src/test/java/org/springframework/config/java/support/AbstractCircularImportDetectionTests.java +++ b/org.springframework.config.java/src/test/java/org/springframework/config/java/support/AbstractCircularImportDetectionTests.java @@ -32,10 +32,11 @@ import test.beans.TestBean; * @author Chris Beams */ public abstract class AbstractCircularImportDetectionTests { + protected abstract ConfigurationParser newParser(); protected abstract String loadAsConfigurationSource(Class clazz) throws Exception; - + @Test public void simpleCircularImportIsDetected() throws Exception { boolean threw = false; 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 ed6729c0ad68571239d0f47a01d14dcc95b2afa7..d9bf1428006d38e0e339f37428f13addeafba56c 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 @@ -8,6 +8,7 @@ import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.config.java.Bean; import org.springframework.config.java.Configuration; @@ -49,6 +50,17 @@ public class BasicTests { return factory; } + @Test(expected=BeanDefinitionParsingException.class) + public void testFinalBeanMethod() { + initBeanFactory(ConfigWithFinalBean.class); + } + + @Configuration + static class ConfigWithFinalBean { + public final @Bean TestBean testBean() { + return new TestBean(); + } + } @Test public void simplestPossibleConfiguration() { @@ -61,8 +73,7 @@ public class BasicTests { @Configuration static class SimplestPossibleConfig { - public @Bean - String stringBean() { + public @Bean String stringBean() { return "foo"; } } @@ -82,15 +93,13 @@ public class BasicTests { @Configuration static class ConfigWithPrototypeBean { - public @Bean - TestBean foo() { + public @Bean TestBean foo() { TestBean foo = new TestBean("foo"); foo.setSpouse(bar()); return foo; } - public @Bean - TestBean bar() { + public @Bean TestBean bar() { TestBean bar = new TestBean("bar"); bar.setSpouse(baz()); return bar;