提交 72fae2ea 编写于 作者: C Chris Beams

+ Source attribution is now consistent across all registered Problems

+ Various pruning of dead code and polish
上级 8b4ad457
......@@ -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 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());
}
}
......
......@@ -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<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() {
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 <code>Plugin</code> 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<BeanMethod> 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());
}
}
......
......@@ -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,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<? 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);
}
/* 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<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")));
}
}
}
\ No newline at end of file
......@@ -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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册