+ Source attribution is now consistent across all registered Problems
+ Various pruning of dead code and polish
This commit is contained in:
parent
8b4ad4575c
commit
72fae2ea19
|
|
@ -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<Annotation> annotations = new ArrayList<Annotation>();
|
||||
|
||||
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 int getLineNumber() {
|
||||
return lineNumber;
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,107 +36,51 @@ 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<BeanMethod> methods = new HashSet<BeanMethod>();
|
||||
|
||||
private HashSet<Annotation> pluginAnnotations = new HashSet<Annotation>();
|
||||
|
||||
private ConfigurationClass declaringClass;
|
||||
|
||||
public ConfigurationClass() {
|
||||
}
|
||||
|
||||
// TODO: get rid of constructors used only for testing. put in testing util.
|
||||
/**
|
||||
* Creates a new ConfigurationClass named <var>className.</var>
|
||||
*
|
||||
* @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<BeanMethod> getMethods() {
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public void setModifiers(int modifiers) {
|
||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
public Configuration getConfigurationAnnotation() {
|
||||
return this.configurationAnnotation;
|
||||
}
|
||||
|
||||
public void setConfigurationAnnotation(Configuration configAnno) {
|
||||
Assert.notNull(configAnno, "configuration annotation must be non-null");
|
||||
this.configurationAnnotation = configAnno;
|
||||
}
|
||||
|
||||
public Set<BeanMethod> getBeanMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public Annotation[] getPluginAnnotations() {
|
||||
return pluginAnnotations.toArray(new Annotation[pluginAnnotations.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link Extension @Plugin}-annotated annotation to this configuration class.
|
||||
*
|
||||
* @param pluginAnno type-level <code>Plugin</code> annotation
|
||||
*/
|
||||
public ConfigurationClass addPluginAnnotation(Annotation pluginAnno) {
|
||||
pluginAnnotations.add(pluginAnno);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigurationClass setDeclaringClass(ConfigurationClass configurationClass) {
|
||||
this.declaringClass = configurationClass;
|
||||
public ConfigurationClass addBeanMethod(BeanMethod method) {
|
||||
method.setDeclaringClass(this);
|
||||
methods.add(method);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -149,29 +88,13 @@ final class ConfigurationClass extends ModelClass {
|
|||
return declaringClass;
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public ConfigurationClass setModifiers(int modifiers) {
|
||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
|
||||
this.modifiers = modifiers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Configuration getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|||
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
||||
|
||||
private boolean processInnerClasses = true;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model,
|
||||
ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
|
|
@ -120,78 +118,24 @@ 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();
|
||||
if (!importStack.contains(configClass)) {
|
||||
importStack.push(configClass);
|
||||
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
|
||||
}
|
||||
|
||||
importStack.push(configClass);
|
||||
|
||||
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
|
||||
problemReporter.error(new CircularImportProblem(configClass, importStack));
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
// 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<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
|
||||
|
||||
if (annoType == null)
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
|
||||
Annotation pluginAnno = createMutableAnnotation(annoType);
|
||||
configClass.addPluginAnnotation(pluginAnno);
|
||||
return new MutableAnnotationVisitor(pluginAnno, classLoader);
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
return super.visitAnnotation(typeDesc, arg1);
|
||||
}
|
||||
|
||||
public boolean hasPluginAnnotation() {
|
||||
return hasPluginAnnotation;
|
||||
}
|
||||
|
||||
public Extension getPluginAnnotation() {
|
||||
return pluginAnnotation;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Delegates all {@link Configuration @Configuration} class method parsing to
|
||||
* {@link ConfigurationClassMethodVisitor}.
|
||||
|
|
@ -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<ConfigurationClass> currentImportStack) {
|
||||
CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> 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())
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ConfigurationClass> allConfigClasses = new ArrayList<ConfigurationClass>();
|
||||
//
|
||||
// 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 {
|
|||
* <var>errors</var>.
|
||||
*
|
||||
* @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")));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue