Formatting pass, primarily to align with Spring's convention of hard tab indentation.

This commit is contained in:
Chris Beams 2009-02-28 06:18:46 +00:00
parent f8270428df
commit f9918f9b2e
42 changed files with 3002 additions and 2788 deletions

View File

@ -29,17 +29,21 @@ import org.springframework.stereotype.Component;
/** /**
* Annotation indicating that a class is a "Java Configuration" class, meaning that it exposes one * Annotation indicating that a class is a "Java Configuration" class, meaning that it
* or more {@link Bean} methods. Holds similar information to that held in the default values of a * exposes one or more {@link Bean} methods. Holds similar information to that held in the
* bean factory; can generally be thought of as the JavaConfig equivalent of XML's 'beans' root * default values of a bean factory; can generally be thought of as the JavaConfig
* element. * equivalent of XML's 'beans' root element.
* *
* <p>Note however that the information here is not used to populate the defaults of the owning bean * <p>
* factory, which would affect other configurations. In the style of the Java configuration * Note however that the information here is not used to populate the defaults of the owning
* mechanism generally, each Java configuration class is kept isolated.</p> * bean factory, which would affect other configurations. In the style of the Java
* configuration mechanism generally, each Java configuration class is kept isolated.
* </p>
* *
* <p>Configuration-annotated classes are also candidates for component scanning thanks to the fact * <p>
* that this annotation is meta-annotated with {@link Component @Component}.</p> * Configuration-annotated classes are also candidates for component scanning thanks to the
* fact that this annotation is meta-annotated with {@link Component @Component}.
* </p>
* *
* @author Rod Johnson * @author Rod Johnson
* @author Chris Beams * @author Chris Beams
@ -52,8 +56,9 @@ import org.springframework.stereotype.Component;
public @interface Configuration { public @interface Configuration {
/** /**
* Configuration name. Allow different variants, such as test, production * Configuration name. Allow different variants, such as test, production etc. Default
* etc. Default will always match. * will always match.
*
* @return * @return
*/ */
String[] names() default ""; String[] names() default "";
@ -90,8 +95,7 @@ public @interface Configuration {
boolean useFactoryAspects() default false; boolean useFactoryAspects() default false;
/** /**
* Do we check {@link Required @Required} methods to make sure they've been * Do we check {@link Required @Required} methods to make sure they've been called?
* called?
*/ */
boolean checkRequired() default false; boolean checkRequired() default false;

View File

@ -30,15 +30,18 @@ import sun.security.x509.Extension;
/** /**
* Abstract representation of a user-defined {@link Configuration @Configuration} class. Includes a * Abstract representation of a user-defined {@link Configuration @Configuration} class.
* set of Bean methods, AutoBean methods, ExternalBean methods, ExternalValue methods, etc. Includes * Includes a set of Bean methods, AutoBean methods, ExternalBean methods, ExternalValue
* all such methods defined in the ancestry of the class, in a 'flattened-out' manner. Note that * methods, etc. Includes all such methods defined in the ancestry of the class, in a
* each BeanMethod representation does still contain source information about where it was * 'flattened-out' manner. Note that each BeanMethod representation does still contain
* originally detected (for the purpose of tooling with Spring IDE). * source information about where it was originally detected (for the purpose of tooling
* with Spring IDE).
* *
* <p>Like the rest of the {@link org.springframework.config.java.model model} package, * <p>
* this class follows the fluent interface / builder pattern such that a model can be built up * Like the rest of the {@link org.springframework.config.java.model model} package, this
* easily by method chaining.</p> * class follows the fluent interface / builder pattern such that a model can be built up
* easily by method chaining.
* </p>
* *
* @author Chris Beams * @author Chris Beams
*/ */
@ -58,7 +61,8 @@ public final class ConfigurationClass extends ModelClass implements Validatable
private ConfigurationClass declaringClass; private ConfigurationClass declaringClass;
public ConfigurationClass() { } public ConfigurationClass() {
}
// TODO: get rid of constructors used only for testing. put in testing util. // TODO: get rid of constructors used only for testing. put in testing util.
/** /**
@ -81,7 +85,9 @@ public final class ConfigurationClass extends ModelClass implements Validatable
} }
private static Configuration defaultAnnotation() { private static Configuration defaultAnnotation() {
@Configuration class Prototype { } @Configuration
class Prototype {
}
return Prototype.class.getAnnotation(Configuration.class); return Prototype.class.getAnnotation(Configuration.class);
} }
@ -89,12 +95,12 @@ public final class ConfigurationClass extends ModelClass implements Validatable
* Creates a new ConfigurationClass object. * Creates a new ConfigurationClass object.
* *
* @param name Fully qualified name of the class being represented * @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 * @param id Bean name/id (if any) of this configuration class. used only in the case of
* of XML integration where {@link Configuration} beans may have a * XML integration where {@link Configuration} beans may have a user-specified
* user-specified id. * id.
* @param metadata Configuration annotation resident on this class. May be null indicating * @param metadata Configuration annotation resident on this class. May be null
* that the user specified this class to be processed but failed to properly * indicating that the user specified this class to be processed but failed to
* annotate it. * properly annotate it.
* @param modifiers Per {@link java.lang.reflect.Modifier} * @param modifiers Per {@link java.lang.reflect.Modifier}
*/ */
public ConfigurationClass(String name, String id, Configuration metadata, int modifiers) { public ConfigurationClass(String name, String id, Configuration metadata, int modifiers) {
@ -185,8 +191,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
@Override @Override
public String toString() { public String toString() {
return format("%s; modifiers=%d; methods=%s", return format("%s; modifiers=%d; methods=%s", super.toString(), modifiers, methods);
super.toString(), modifiers, methods);
} }
@Override @Override
@ -251,8 +256,8 @@ public final class ConfigurationClass extends ModelClass implements Validatable
@Override @Override
public String getDescription() { public String getDescription() {
return format("%s was provided as a Java Configuration class but was not annotated with @%s. " return format("%s was provided as a Java Configuration class but was not annotated with @%s. "
+ "Update the class definition to continue.", + "Update the class definition to continue.", getSimpleName(), Configuration.class
getSimpleName(), Configuration.class.getSimpleName()); .getSimpleName());
} }
} }
@ -287,8 +292,8 @@ public final class ConfigurationClass extends ModelClass implements Validatable
} }
/** /**
* Error raised when a Bean marked as 'allowOverriding=false' is attempted to be overridden by * Error raised when a Bean marked as 'allowOverriding=false' is attempted to be
* another bean definition. * overridden by another bean definition.
* *
* @see Bean#allowOverriding() * @see Bean#allowOverriding()
*/ */
@ -300,13 +305,12 @@ public final class ConfigurationClass extends ModelClass implements Validatable
* Creates a new IllegalBeanOverrideError object. * Creates a new IllegalBeanOverrideError object.
* *
* @param violatingClass class attempting an illegal override. null value signifies * @param violatingClass class attempting an illegal override. null value signifies
* that the violating class is unknown or that there is no * that the violating class is unknown or that there is no class to speak of
* class to speak of (in the case of an XML bean definition * (in the case of an XML bean definition doing the illegal overriding)
* doing the illegal overriding) * @param finalMethodInQuestion the method that has been marked
* @param finalMethodInQuestion the method that has been marked 'allowOverriding=false' * 'allowOverriding=false'
*/ */
public IllegalBeanOverrideError(ConfigurationClass violatingClass, public IllegalBeanOverrideError(ConfigurationClass violatingClass, ModelMethod finalMethodInQuestion) {
ModelMethod finalMethodInQuestion) {
super(violatingClass, -1); super(violatingClass, -1);
this.authoritativeClass = ConfigurationClass.this; this.authoritativeClass = ConfigurationClass.this;
this.finalMethodInQuestion = finalMethodInQuestion; this.finalMethodInQuestion = finalMethodInQuestion;

View File

@ -21,17 +21,19 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* An abstract representation of a set of user-provided "Configuration classes", usually but not * An abstract representation of a set of user-provided "Configuration classes", usually but
* necessarily annotated with {@link Configuration @Configuration}. The model is populated with a * not necessarily annotated with {@link Configuration @Configuration}. The model is
* {@link org.springframework.config.java.internal.parsing.ConfigurationParser} implementation which * populated with a
* may be reflection-based or ASM-based. Once a model has been populated, it can then be rendered * {@link org.springframework.config.java.internal.parsing.ConfigurationParser}
* out to a set of BeanDefinitions. The model provides an important layer of indirection between the * implementation which may be reflection-based or ASM-based. Once a model has been
* complexity of parsing a set of classes and the complexity of representing the contents of those * populated, it can then be rendered out to a set of BeanDefinitions. The model provides an
* classes as BeanDefinitions. * important layer of indirection between the complexity of parsing a set of classes and the
* complexity of representing the contents of those classes as BeanDefinitions.
* *
* <p>Interface follows the builder pattern for method chaining.</p> * <p>
* Interface follows the builder pattern for method chaining.
* </p>
* *
* @author Chris Beams * @author Chris Beams
* @see org.springframework.config.java.internal.parsing.ConfigurationParser * @see org.springframework.config.java.internal.parsing.ConfigurationParser
@ -43,9 +45,9 @@ public final class ConfigurationModel implements Validatable {
private final ArrayList<Validator> validators = new ArrayList<Validator>(); private final ArrayList<Validator> validators = new ArrayList<Validator>();
/** /**
* Add a {@link Configuration @Configuration} class to the model. Classes may be added at will * Add a {@link Configuration @Configuration} class to the model. Classes may be added
* and without any particular validation. Malformed classes will be caught and errors processed * at will and without any particular validation. Malformed classes will be caught and
* during {@link #validate() validation} * errors processed during {@link #validate() validation}
* *
* @param configurationClass user-supplied Configuration class * @param configurationClass user-supplied Configuration class
*/ */
@ -68,7 +70,8 @@ public final class ConfigurationModel implements Validatable {
} }
// /** // /**
// * Return all configuration classes, including all imported configuration classes. This method // * Return all configuration classes, including all imported configuration classes.
// This method
// * should be generally preferred over {@link #getConfigurationClasses()} // * should be generally preferred over {@link #getConfigurationClasses()}
// * // *
// * @see #getConfigurationClasses() // * @see #getConfigurationClasses()
@ -87,7 +90,8 @@ public final class ConfigurationModel implements Validatable {
} }
/** /**
* Recurses through the model validating each object along the way and aggregating any <var>errors</var>. * Recurses through the model validating each object along the way and aggregating any
* <var>errors</var>.
* *
* @see ConfigurationClass#validate(java.util.List) * @see ConfigurationClass#validate(java.util.List)
* @see ModelMethod#validate(java.util.List) * @see ModelMethod#validate(java.util.List)
@ -129,8 +133,7 @@ public final class ConfigurationModel implements Validatable {
@Override @Override
public String toString() { public String toString() {
return format("%s: configurationClasses=%s", return format("%s: configurationClasses=%s", getClass().getSimpleName(), configurationClasses);
getClass().getSimpleName(), configurationClasses);
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import java.lang.annotation.Target;
import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.NoOp; import net.sf.cglib.proxy.NoOp;
/** /**
* Meta-annotation used to identify annotations as producers of beans and/or values. * Meta-annotation used to identify annotations as producers of beans and/or values.
* *
@ -35,21 +36,20 @@ import net.sf.cglib.proxy.NoOp;
public @interface Factory { public @interface Factory {
/** /**
* Specifies which registrar (if any) should be used to register * Specifies which registrar (if any) should be used to register bean definitions for
* bean definitions for this {@link Factory} method. * this {@link Factory} method.
*/ */
Class<? extends BeanDefinitionRegistrar> registrarType(); Class<? extends BeanDefinitionRegistrar> registrarType();
/** /**
* Specifies what (if any) callback should be used when processing this {@link Factory} method. * Specifies what (if any) callback should be used when processing this {@link Factory}
* Defaults to CGLIB's {@link NoOp}, which does nothing. * method. Defaults to CGLIB's {@link NoOp}, which does nothing. TODO: rename
* TODO: rename (interceptorType)? to keep with the -or|-ar nomenclature * (interceptorType)? to keep with the -or|-ar nomenclature
*/ */
Class<? extends Callback> callbackType() default NoOp.class; Class<? extends Callback> callbackType() default NoOp.class;
/** /**
* TODO: document * TODO: document TODO: rename
* TODO: rename
*/ */
Class<? extends Validator>[] validatorTypes() default {}; Class<? extends Validator>[] validatorTypes() default {};
} }

View File

@ -21,9 +21,8 @@ import org.springframework.util.ClassUtils;
/** /**
* Abstract representation of a class, free from java reflection. * Abstract representation of a class, free from java reflection. Base class used within the
* Base class used within the internal JavaConfig metamodel for * internal JavaConfig metamodel for representing {@link Configuration} classes.
* representing {@link Configuration} classes.
* *
* @author Chris Beams * @author Chris Beams
*/ */
@ -37,7 +36,8 @@ public class ModelClass implements BeanMetadataElement {
/** /**
* Creates a new and empty ModelClass instance. * Creates a new and empty ModelClass instance.
*/ */
public ModelClass() { } public ModelClass() {
}
/** /**
* Creates a new ModelClass instance * Creates a new ModelClass instance
@ -95,8 +95,8 @@ public class ModelClass implements BeanMetadataElement {
} }
/** /**
* Returns a resource path-formatted representation of the .java * Returns a resource path-formatted representation of the .java file that declares this
* file that declares this class * class
*/ */
public String getSource() { public String getSource() {
return source; return source;
@ -113,7 +113,9 @@ public class ModelClass implements BeanMetadataElement {
} }
/** /**
* Given a ModelClass instance representing a class com.acme.Foo, this method will return * Given a ModelClass instance representing a class com.acme.Foo, this method will
* return
*
* <pre> * <pre>
* ModelClass: name=Foo * ModelClass: name=Foo
* </pre> * </pre>

View File

@ -101,8 +101,8 @@ public final class ModelMethod implements Validatable {
T anno = getAnnotation(annoType); T anno = getAnnotation(annoType);
if (anno == null) if (anno == null)
throw new IllegalStateException( throw new IllegalStateException(format("annotation %s not found on %s", annoType.getSimpleName(),
format("annotation %s not found on %s", annoType.getSimpleName(), this)); this));
return anno; return anno;
} }
@ -168,8 +168,8 @@ public final class ModelMethod implements Validatable {
@Override @Override
public String toString() { public String toString() {
String returnTypeName = returnType == null ? "<unknown>" : returnType.getSimpleName(); String returnTypeName = returnType == null ? "<unknown>" : returnType.getSimpleName();
return String.format("%s: name=%s; returnType=%s; modifiers=%d", return String.format("%s: name=%s; returnType=%s; modifiers=%d", getClass().getSimpleName(), name,
getClass().getSimpleName(), name, returnTypeName, modifiers); returnTypeName, modifiers);
} }
@Override @Override
@ -232,8 +232,7 @@ public final class ModelMethod implements Validatable {
@Override @Override
public String getDescription() { public String getDescription() {
return format("method '%s' may not be final - remove the final modifier to continue", return format("method '%s' may not be final - remove the final modifier to continue", getName());
getName());
} }
} }

View File

@ -19,23 +19,24 @@ package org.springframework.config.java;
/** /**
* Enumerates the names of the scopes supported out of the box in Spring. * Enumerates the names of the scopes supported out of the box in Spring.
* <p> * <p>
* Not modeled as an actual java enum because annotations that accept a scope * Not modeled as an actual java enum because annotations that accept a scope attribute must
* attribute must allow for user-defined scope names. Given that java * allow for user-defined scope names. Given that java enums are not extensible, these must
* enums are not extensible, these must remain simple string constants. * remain simple string constants.
* *
* @author Chris Beams * @author Chris Beams
* @since 3.0 * @since 3.0
*/ */
public class Scopes { public class Scopes {
private Scopes() { } private Scopes() {
}
public static final String SINGLETON = "singleton"; // see BeanDefinition.SCOPE_SINGLETON; public static final String SINGLETON = "singleton";
public static final String PROTOTYPE = "prototype"; // see BeanDefinition.SCOPE_PROTOTYPE; public static final String PROTOTYPE = "prototype";
public static final String REQUEST = "request"; // see WebApplicationContext.SCOPE_REQUEST; public static final String REQUEST = "request";
public static final String SESSION = "session"; // see WebApplicationContext.SCOPE_SESSION; public static final String SESSION = "session";
} }

View File

@ -16,12 +16,11 @@
package org.springframework.config.java; package org.springframework.config.java;
/** /**
* Represents an invalid usage of JavaConfig constructs, e.g. a {@link Configuration} that declares * Represents an invalid usage of JavaConfig constructs, e.g. a {@link Configuration} that
* no {@link Bean @Bean} methods, or declaring both {@link Bean @Bean} and * declares no {@link Bean @Bean} methods, or declaring both {@link Bean @Bean} and
* {@link ExternalBean @ExternalBean} on a single method. Explore the type hierarchy to discover all * {@link ExternalBean @ExternalBean} on a single method. Explore the type hierarchy to
* possible usage errors. * discover all possible usage errors.
* *
* @author Chris Beams * @author Chris Beams
* @see MalformedConfigurationException * @see MalformedConfigurationException
@ -34,9 +33,10 @@ public abstract class UsageError {
/** /**
* Create a new usage error, providing information about where the error was detected. * Create a new usage error, providing information about where the error was detected.
* *
* @param modelClass class in which this error was detected. Null value indicates that the * @param modelClass class in which this error was detected. Null value indicates that
* error was not local to a single class. * the error was not local to a single class.
* @param lineNumber line number on which this error was detected (useful for tooling integration) * @param lineNumber line number on which this error was detected (useful for tooling
* integration)
* *
* @see ModelClass#getSource() * @see ModelClass#getSource()
*/ */
@ -51,8 +51,9 @@ public abstract class UsageError {
public abstract String getDescription(); public abstract String getDescription();
/** /**
* Same as {@link #getDescription()} but attributed with class and line number information. If * Same as {@link #getDescription()} but attributed with class and line number
* modelClass constructor parameter was null, class and line number information will be omitted. * information. If modelClass constructor parameter was null, class and line number
* information will be omitted.
*/ */
public final String getAttributedDescription() { public final String getAttributedDescription() {
if (clazz == null) if (clazz == null)

View File

@ -14,6 +14,7 @@ import org.springframework.util.ReflectionUtils;
import sun.security.x509.Extension; import sun.security.x509.Extension;
/** /**
* Misc utils * Misc utils
* *
@ -27,11 +28,11 @@ public class Util {
private static final Log log = LogFactory.getLog(Util.class); private static final Log log = LogFactory.getLog(Util.class);
private Util() { } private Util() {
}
/** /**
* Returns instance of type T by invoking its default or no-arg * Returns instance of type T by invoking its default or no-arg constructor.
* constructor.
* <p> * <p>
* Any reflection-related issues are re-thrown as unchecked. * Any reflection-related issues are re-thrown as unchecked.
*/ */
@ -42,16 +43,16 @@ public class Util {
return noArgCtor.newInstance(); return noArgCtor.newInstance();
} catch (Exception ex) { } catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex); ReflectionUtils.handleReflectionException(ex);
throw new IllegalStateException( throw new IllegalStateException(format("Unexpected reflection exception - %s: %s", ex.getClass()
format("Unexpected reflection exception - %s: %s", .getName(), ex.getMessage()));
ex.getClass().getName(), ex.getMessage()));
} }
} }
/** /**
* Loads the specified class using the default class loader, gracefully handling any * Loads the specified class using the default class loader, gracefully handling any
* {@link ClassNotFoundException} that may be thrown. This functionality is specifically * {@link ClassNotFoundException} that may be thrown. This functionality is specifically
* implemented to accomodate tooling (Spring IDE) concerns, where user-defined types will not be * implemented to accomodate tooling (Spring IDE) concerns, where user-defined types
* will not be
* *
* @param <T> type of class to be returned * @param <T> type of class to be returned
* @param fqClassName fully-qualified class name * @param fqClassName fully-qualified class name
@ -91,32 +92,34 @@ public class Util {
try { try {
return (Class<? extends T>) getDefaultClassLoader().loadClass(fqClassName); return (Class<? extends T>) getDefaultClassLoader().loadClass(fqClassName);
} catch (ClassNotFoundException ex) { } catch (ClassNotFoundException ex) {
throw new IllegalArgumentException( throw new IllegalArgumentException(format(
format("Class [%s] could not be loaded, check your CLASSPATH.", fqClassName), ex); "Class [%s] could not be loaded, check your CLASSPATH.", fqClassName), ex);
} }
} }
/** /**
* Loads the specified class using the default class loader, gracefully handling any * Loads the specified class using the default class loader, gracefully handling any
* {@link ClassNotFoundException} that may be thrown by issuing a WARN level logging statement * {@link ClassNotFoundException} that may be thrown by issuing a WARN level logging
* and return null. This functionality is specifically implemented to accomodate tooling * statement and return null. This functionality is specifically implemented to
* (Spring IDE) concerns, where user-defined types will not be available to the tooling. * accomodate tooling (Spring IDE) concerns, where user-defined types will not be
* available to the tooling.
* <p> * <p>
* ASM class reading is used throughout JavaConfig, but there are certain cases where * ASM class reading is used throughout JavaConfig, but there are certain cases where
* classloading cannot be avoided - specifically in cases where users define their own * classloading cannot be avoided - specifically in cases where users define their own
* {@link Extension} or {@link Factory} annotations. This method should therefore be used sparingly * {@link Extension} or {@link Factory} annotations. This method should therefore be
* but consistently where required. * used sparingly but consistently where required.
* <p> * <p>
* Because {@link ClassNotFoundException} is compensated for by returning null, callers must * Because {@link ClassNotFoundException} is compensated for by returning null, callers
* take care to handle the null case appropriately. * must take care to handle the null case appropriately.
* <p> * <p>
* In cases where the WARN logging statement is not desired, use the {@link #loadClass(String)} * In cases where the WARN logging statement is not desired, use the
* method, which returns null but issues no logging statements. * {@link #loadClass(String)} method, which returns null but issues no logging
* statements.
* <p> * <p>
* This method should only ever return null in the case of a user-defined type be processed at * This method should only ever return null in the case of a user-defined type be
* tooling time. Therefore, tooling may not be able to represent any custom annotation * processed at tooling time. Therefore, tooling may not be able to represent any custom
* semantics, but JavaConfig itself will not have any problem loading and respecting them at * annotation semantics, but JavaConfig itself will not have any problem loading and
* actual runtime. * respecting them at actual runtime.
* *
* @param <T> type of class to be returned * @param <T> type of class to be returned
* @param fqClassName fully-qualified class name * @param fqClassName fully-qualified class name
@ -139,11 +142,11 @@ public class Util {
} }
/** /**
* Uses the default ClassLoader to load <var>pathToClass</var>. Appends '.class' * Uses the default ClassLoader to load <var>pathToClass</var>. Appends '.class' to
* to pathToClass before attempting to load. * pathToClass before attempting to load.
* *
* @param pathToClass resource path to class, not including .class suffix. * @param pathToClass resource path to class, not including .class suffix. e.g.:
* e.g.: com/acme/MyClass * com/acme/MyClass
* *
* @return inputStream for <var>pathToClass</var> * @return inputStream for <var>pathToClass</var>
* *
@ -155,8 +158,8 @@ public class Util {
InputStream is = ClassUtils.getDefaultClassLoader().getResourceAsStream(classFileName); InputStream is = ClassUtils.getDefaultClassLoader().getResourceAsStream(classFileName);
if (is == null) if (is == null)
throw new RuntimeException( throw new RuntimeException(new FileNotFoundException("Class file [" + classFileName
new FileNotFoundException("Class file [" + classFileName + "] not found")); + "] not found"));
return is; return is;
} }

View File

@ -2,6 +2,7 @@ package org.springframework.config.java;
import java.util.List; import java.util.List;
/** /**
* Indicates a type is able to be validated for errors. * Indicates a type is able to be validated for errors.
* *

View File

@ -37,23 +37,29 @@ import org.springframework.config.java.Validator;
/** /**
* Annotation to be applied to methods that create beans in a Spring context. The name of the bean * Annotation to be applied to methods that create beans in a Spring context. The name of
* is the method name. (It is also possible to specify aliases using the aliases array on this * the bean is the method name. (It is also possible to specify aliases using the aliases
* annotation.) * array on this annotation.)
* *
* <p>Contains information similar to that held in Spring's internal BeanDefinition metadata.</p> * <p>
* Contains information similar to that held in Spring's internal BeanDefinition metadata.
* </p>
* *
* <p>Bean creation methods must be non-private (default, public or protected). Bean creation * <p>
* methods may throw any exception, which will be caught and handled by the Spring container on * Bean creation methods must be non-private (default, public or protected). Bean creation
* processing of the configuration class.<br> * methods may throw any exception, which will be caught and handled by the Spring container
* Bean creation methods must return an object type. The decision to return a class or an interface * on processing of the configuration class.<br>
* will be significant in the event of proxying. Bean methods that return interfaces will be proxied * Bean creation methods must return an object type. The decision to return a class or an
* using dynamic proxies; those that return a class will require CGLIB or other subclass-based * interface will be significant in the event of proxying. Bean methods that return
* proxying. It is recommended to return an interface where possible, as this is also consistent * interfaces will be proxied using dynamic proxies; those that return a class will require
* with best practice around loose coupling.</p> * CGLIB or other subclass-based proxying. It is recommended to return an interface where
* possible, as this is also consistent with best practice around loose coupling.
* </p>
* *
* <p>Bean creation methods may reference other bean creation methods by calling them directly, as * <p>
* follows. This ensures that references between beans are strongly typed:</p> * Bean creation methods may reference other bean creation methods by calling them directly,
* as follows. This ensures that references between beans are strongly typed:
* </p>
* *
* @see Configuration * @see Configuration
* @see BeanNamingStrategy * @see BeanNamingStrategy
@ -66,9 +72,8 @@ import org.springframework.config.java.Validator;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Inherited @Inherited
@Documented @Documented
@Factory(registrarType=BeanRegistrar.class, @Factory(registrarType = BeanRegistrar.class, callbackType = BeanMethodInterceptor.class, validatorTypes = {
callbackType=BeanMethodInterceptor.class, BeanValidator.class, IllegalBeanOverrideValidator.class })
validatorTypes={BeanValidator.class, IllegalBeanOverrideValidator.class})
public @interface Bean { public @interface Bean {
/** /**
@ -78,7 +83,8 @@ public @interface Bean {
* @see BeanDefinition#ROLE_INFRASTRUCTURE * @see BeanDefinition#ROLE_INFRASTRUCTURE
* @see BeanDefinition#ROLE_SUPPORT * @see BeanDefinition#ROLE_SUPPORT
* *
* @see AbstractBeanDefinition the 'role' field is assigned by default to ROLE_APPLICATION * @see AbstractBeanDefinition the 'role' field is assigned by default to
* ROLE_APPLICATION
*/ */
int role() default BeanDefinition.ROLE_APPLICATION; int role() default BeanDefinition.ROLE_APPLICATION;
@ -88,8 +94,8 @@ public @interface Bean {
String[] aliases() default {}; String[] aliases() default {};
/** /**
* Scope: whether the bean is a singleton, prototype or custom scope. * Scope: whether the bean is a singleton, prototype or custom scope. Default is
* Default is singleton. * singleton.
*/ */
String scope() default Scopes.SINGLETON; String scope() default Scopes.SINGLETON;
@ -107,13 +113,14 @@ public @interface Bean {
// * A bean may be marked as primary, useful for disambiguation when looking // * A bean may be marked as primary, useful for disambiguation when looking
// * up beans by type. // * up beans by type.
// * // *
// * @see org.springframework.config.java.context.JavaConfigApplicationContext#getBean(Class); // * @see
// org.springframework.config.java.context.JavaConfigApplicationContext#getBean(Class);
// */ // */
// Primary primary() default Primary.UNSPECIFIED; // Primary primary() default Primary.UNSPECIFIED;
/** /**
* Bean init method name. Normally this is not needed, as the initialization * Bean init method name. Normally this is not needed, as the initialization (with
* (with parameterization) can be done directly through java code. * parameterization) can be done directly through java code.
*/ */
String initMethodName() default ""; String initMethodName() default "";
@ -138,10 +145,9 @@ public @interface Bean {
// Meta[] meta() default { }; // Meta[] meta() default { };
/** /**
* Allow the bean to be overridden in another JavaConfig, XML or other * Allow the bean to be overridden in another JavaConfig, XML or other non-Java
* non-Java configuration. This is consistent with * configuration. This is consistent with DefaultListableBeanFactory's
* DefaultListableBeanFactory's allowBeanDefinitionOverriding property, * allowBeanDefinitionOverriding property, which defaults to true.
* which defaults to true.
* *
* @return whether overriding of this bean is allowed * @return whether overriding of this bean is allowed
*/ */
@ -212,6 +218,3 @@ class IllegalBeanOverrideValidator implements Validator {
} }
} }

View File

@ -23,6 +23,7 @@ import net.sf.cglib.proxy.MethodProxy;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
/** /**
* Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper * Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper
* handling of bean semantics such as scoping and AOP proxying. * handling of bean semantics such as scoping and AOP proxying.
@ -35,16 +36,18 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
class BeanMethodInterceptor extends AbstractMethodInterceptor { class BeanMethodInterceptor extends AbstractMethodInterceptor {
/** /**
* Enhances a {@link Bean @Bean} method to check the supplied BeanFactory for the existence * Enhances a {@link Bean @Bean} method to check the supplied BeanFactory for the
* of this bean object. * existence of this bean object.
*/ */
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String beanName = getBeanName(method); String beanName = getBeanName(method);
// TODO: re-enable for @ScopedProxy support // TODO: re-enable for @ScopedProxy support
// boolean isScopedProxy = (AnnotationUtils.findAnnotation(method, ScopedProxy.class) != null); // boolean isScopedProxy = (AnnotationUtils.findAnnotation(method,
// ScopedProxy.class) != null);
// //
// String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName); // String scopedBeanName =
// ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
// if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName)) // if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
// beanName = scopedBeanName; // beanName = scopedBeanName;
@ -52,8 +55,8 @@ class BeanMethodInterceptor extends AbstractMethodInterceptor {
// we have an already existing cached instance of this bean -> retrieve it // we have an already existing cached instance of this bean -> retrieve it
Object cachedBean = beanFactory.getBean(beanName); Object cachedBean = beanFactory.getBean(beanName);
if (log.isInfoEnabled()) if (log.isInfoEnabled())
log.info(format("Returning cached singleton object [%s] for @Bean method %s.%s", log.info(format("Returning cached singleton object [%s] for @Bean method %s.%s", cachedBean,
cachedBean, method.getDeclaringClass().getSimpleName(), beanName)); method.getDeclaringClass().getSimpleName(), beanName));
return cachedBean; return cachedBean;
} }
@ -62,22 +65,23 @@ class BeanMethodInterceptor extends AbstractMethodInterceptor {
} }
/** /**
* Check the beanFactory to see whether the bean named <var>beanName</var> already exists. * Check the beanFactory to see whether the bean named <var>beanName</var> already
* Accounts for the fact that the requested bean may be "in creation", i.e.: we're in the * exists. Accounts for the fact that the requested bean may be "in creation", i.e.:
* middle of servicing the initial request for this bean. From JavaConfig's perspective, * we're in the middle of servicing the initial request for this bean. From JavaConfig's
* this means that the bean does not actually yet exist, and that it is now our job to * perspective, this means that the bean does not actually yet exist, and that it is now
* create it for the first time by executing the logic in the corresponding Bean method. * our job to create it for the first time by executing the logic in the corresponding
* Bean method.
* <p> * <p>
* Said another way, this check repurposes {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} * Said another way, this check repurposes
* to determine whether the container is calling this method or the user is calling this method. * {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
* the container is calling this method or the user is calling this method.
* *
* @param beanName name of bean to check for * @param beanName name of bean to check for
* *
* @return true if <var>beanName</var> already exists in beanFactory * @return true if <var>beanName</var> already exists in beanFactory
*/ */
private boolean factoryContainsBean(String beanName) { private boolean factoryContainsBean(String beanName) {
return beanFactory.containsBean(beanName) return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName);
&& !beanFactory.isCurrentlyInCreation(beanName);
} }

View File

@ -39,7 +39,7 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
// TODO: SJC-242 method too long // TODO: SJC-242 method too long
public void register(ModelMethod method, BeanDefinitionRegistry registry) { public void register(ModelMethod method, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDef = new JavaConfigBeanDefinition(); RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
ConfigurationClass configClass = method.getDeclaringClass(); ConfigurationClass configClass = method.getDeclaringClass();
@ -56,7 +56,8 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
// consider autowiring // consider autowiring
if (bean.autowire() != AnnotationUtils.getDefaultValue(Bean.class, "autowire")) if (bean.autowire() != AnnotationUtils.getDefaultValue(Bean.class, "autowire"))
beanDef.setAutowireMode(bean.autowire().value()); beanDef.setAutowireMode(bean.autowire().value());
else if (defaults.defaultAutowire() != AnnotationUtils.getDefaultValue(Configuration.class, "defaultAutowire")) else if (defaults.defaultAutowire() != AnnotationUtils.getDefaultValue(Configuration.class,
"defaultAutowire"))
beanDef.setAutowireMode(defaults.defaultAutowire().value()); beanDef.setAutowireMode(defaults.defaultAutowire().value());
String beanName = method.getName(); String beanName = method.getName();
@ -66,7 +67,7 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry); BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry);
// is the existing bean definition one that was created by JavaConfig? // is the existing bean definition one that was created by JavaConfig?
if (!(existingBeanDef instanceof JavaConfigBeanDefinition)) { if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
// no -> then it's an external override, probably XML // no -> then it's an external override, probably XML
// ensure that overriding is ok // ensure that overriding is ok
@ -76,9 +77,9 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
} }
// overriding is legal, return immediately // overriding is legal, return immediately
logger.info(format("Skipping loading bean definition for %s: a definition for bean '%s' already exists. " logger.info(format(
+ "This is likely due to an override in XML.", "Skipping loading bean definition for %s: a definition for bean '%s' already exists. "
method, beanName)); + "This is likely due to an override in XML.", method, beanName));
return; return;
} }
} }
@ -118,16 +119,21 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
// //
// // Create a scoped proxy definition for the original bean name, // // Create a scoped proxy definition for the original bean name,
// // "hiding" the target bean in an internal target definition. // // "hiding" the target bean in an internal target definition.
// String targetBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName); // String targetBeanName =
// RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class); // ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
// scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName); // RootBeanDefinition scopedProxyDefinition = new
// RootBeanDefinition(ScopedProxyFactoryBean.class);
// scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName",
// targetBeanName);
// //
// if (scopedProxy.proxyTargetClass()) // if (scopedProxy.proxyTargetClass())
// targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); // targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE,
// Boolean.TRUE);
// // ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we // // ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
// // don't need to set it explicitly here. // // don't need to set it explicitly here.
// else // else
// scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE); // scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass",
// Boolean.FALSE);
// //
// // The target bean should be ignored in favor of the scoped proxy. // // The target bean should be ignored in favor of the scoped proxy.
// targetDef.setAutowireCandidate(false); // targetDef.setAutowireCandidate(false);
@ -142,13 +148,14 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
// TODO: re-enable for @Meta support // TODO: re-enable for @Meta support
// does this bean method have any @Meta annotations? // does this bean method have any @Meta annotations?
// for (Meta meta : bean.meta()) // for (Meta meta : bean.meta())
// beanDef.addMetadataAttribute(new BeanMetadataAttribute(meta.key(), meta.value())); // beanDef.addMetadataAttribute(new BeanMetadataAttribute(meta.key(),
// meta.value()));
if (bean.dependsOn().length > 0) if (bean.dependsOn().length > 0)
beanDef.setDependsOn(bean.dependsOn()); beanDef.setDependsOn(bean.dependsOn());
logger.info(format("Registering bean definition for @Bean method %s.%s()", logger.info(format("Registering bean definition for @Bean method %s.%s()", configClass.getName(),
configClass.getName(), beanName)); beanName));
registry.registerBeanDefinition(beanName, beanDef); registry.registerBeanDefinition(beanName, beanDef);
@ -184,20 +191,18 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
} }
} while (clbf != null); } while (clbf != null);
throw new NoSuchBeanDefinitionException( throw new NoSuchBeanDefinitionException(format("No bean definition matching name '%s' "
format("No bean definition matching name '%s' "
+ "could be found in %s or its ancestry", beanName, registry)); + "could be found in %s or its ancestry", beanName, registry));
} }
} }
/** /**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition * {@link RootBeanDefinition} marker subclass used to signify that a bean definition created
* created by JavaConfig as opposed to any other configuration source. Used in bean * by JavaConfig as opposed to any other configuration source. Used in bean overriding cases
* overriding cases where it's necessary to determine whether the bean definition was created * where it's necessary to determine whether the bean definition was created externally
* externally (e.g. via XML). * (e.g. via XML).
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
// TODO: SJC-242 what to do about JavaConfigBeanDefinition? class ConfigurationClassBeanDefinition extends RootBeanDefinition {
class JavaConfigBeanDefinition extends RootBeanDefinition {
} }

View File

@ -24,13 +24,11 @@ import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
/** /**
* Transforms a class by adding bytecode for a class-level annotation. * Transforms a class by adding bytecode for a class-level annotation. Checks to ensure that
* Checks to ensure that the desired annotation is not already present * the desired annotation is not already present before adding. Used by
* before adding. Used by {@link ConfigurationEnhancer} to dynamically add * {@link ConfigurationEnhancer} to dynamically add an {@link org.aspectj.lang.Aspect}
* an {@link org.aspectj.lang.Aspect} annotation to an enhanced Configuration * annotation to an enhanced Configuration subclass.
* subclass.
* <p/> * <p/>
* This class was originally adapted from examples the ASM 3.0 documentation. * This class was originally adapted from examples the ASM 3.0 documentation.
* *
@ -44,8 +42,7 @@ class AddAnnotationAdapter extends ClassAdapter {
* Creates a new AddAnnotationAdapter instance. * Creates a new AddAnnotationAdapter instance.
* *
* @param cv the ClassVisitor delegate * @param cv the ClassVisitor delegate
* @param annotationDesc name of the annotation to be added * @param annotationDesc name of the annotation to be added (in type descriptor format)
* (in type descriptor format)
*/ */
public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) { public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) {
super(cv); super(cv);
@ -56,7 +53,8 @@ class AddAnnotationAdapter extends ClassAdapter {
* Ensures that the version of the resulting class is Java 5 or better. * Ensures that the version of the resulting class is Java 5 or better.
*/ */
@Override @Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version; int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version;
cv.visit(v, access, name, signature, superName, interfaces); cv.visit(v, access, name, signature, superName, interfaces);
} }
@ -85,7 +83,8 @@ class AddAnnotationAdapter extends ClassAdapter {
} }
@Override @Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
addAnnotation(); addAnnotation();
return cv.visitMethod(access, name, desc, signature, exceptions); return cv.visitMethod(access, name, desc, signature, exceptions);
} }

View File

@ -46,7 +46,6 @@ import org.springframework.config.java.ConfigurationModel;
import org.springframework.config.java.ModelMethod; import org.springframework.config.java.ModelMethod;
/** /**
* Enhances {@link Configuration} classes by generating a CGLIB subclass capable of * Enhances {@link Configuration} classes by generating a CGLIB subclass capable of
* interacting with the Spring container to respect bean semantics. * interacting with the Spring container to respect bean semantics.
@ -59,25 +58,21 @@ public class ConfigurationEnhancer {
private static final Log log = LogFactory.getLog(ConfigurationEnhancer.class); private static final Log log = LogFactory.getLog(ConfigurationEnhancer.class);
private final ArrayList<Class<? extends Callback>> callbackTypes = private final ArrayList<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
new ArrayList<Class<? extends Callback>>();
private final LinkedHashSet<BeanDefinitionRegistrar> registrars = private final LinkedHashSet<BeanDefinitionRegistrar> registrars = new LinkedHashSet<BeanDefinitionRegistrar>();
new LinkedHashSet<BeanDefinitionRegistrar>();
private final ArrayList<Callback> callbackInstances = private final ArrayList<Callback> callbackInstances = new ArrayList<Callback>();
new ArrayList<Callback>();
private final CallbackFilter callbackFilter = private final CallbackFilter callbackFilter = new CallbackFilter() {
new CallbackFilter() {
public int accept(Method candidateMethod) { public int accept(Method candidateMethod) {
Iterator<BeanDefinitionRegistrar> iter = registrars.iterator(); Iterator<BeanDefinitionRegistrar> iter = registrars.iterator();
for (int i = 0; iter.hasNext(); i++) for (int i = 0; iter.hasNext(); i++)
if (iter.next().accepts(candidateMethod)) if (iter.next().accepts(candidateMethod))
return i; return i;
throw new IllegalStateException(format("No registrar is capable of " + throw new IllegalStateException(format("No registrar is capable of "
"handling method [%s]. Perhaps you forgot to add a catch-all registrar?", + "handling method [%s]. Perhaps you forgot to add a catch-all registrar?",
candidateMethod.getName())); candidateMethod.getName()));
} }
}; };
@ -100,7 +95,8 @@ public class ConfigurationEnhancer {
* *
* @see #callbackFilter * @see #callbackFilter
*/ */
private void populateRegistrarsAndCallbacks(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { private void populateRegistrarsAndCallbacks(DefaultListableBeanFactory beanFactory,
ConfigurationModel model) {
for (ConfigurationClass configClass : model.getAllConfigurationClasses()) { for (ConfigurationClass configClass : model.getAllConfigurationClasses()) {
for (ModelMethod method : configClass.getMethods()) { for (ModelMethod method : configClass.getMethods()) {
@ -134,8 +130,8 @@ public class ConfigurationEnhancer {
/** /**
* Loads the specified class and generates a CGLIB subclass of it equipped with container-aware * Loads the specified class and generates a CGLIB subclass of it equipped with
* callbacks capable of respecting scoping and other bean semantics. * container-aware callbacks capable of respecting scoping and other bean semantics.
* *
* @return fully-qualified name of the enhanced subclass * @return fully-qualified name of the enhanced subclass
*/ */
@ -150,8 +146,8 @@ public class ConfigurationEnhancer {
subclass = nestOneClassDeeperIfAspect(superclass, subclass); subclass = nestOneClassDeeperIfAspect(superclass, subclass);
if (log.isInfoEnabled()) if (log.isInfoEnabled())
log.info(format("Successfully enhanced %s; enhanced class name is: %s", log.info(format("Successfully enhanced %s; enhanced class name is: %s", configClassName, subclass
configClassName, subclass.getName())); .getName()));
return subclass.getName(); return subclass.getName();
} }
@ -188,11 +184,11 @@ public class ConfigurationEnhancer {
} }
/** /**
* Works around a constraint imposed by the AspectJ 5 annotation-style programming model. See * Works around a constraint imposed by the AspectJ 5 annotation-style programming
* comments inline for detail. * model. See comments inline for detail.
* *
* @return original subclass instance unless superclass is annnotated with @Aspect, in which * @return original subclass instance unless superclass is annnotated with @Aspect, in
* case a subclass of the subclass is returned * which case a subclass of the subclass is returned
*/ */
private Class<?> nestOneClassDeeperIfAspect(Class<?> superclass, Class<?> origSubclass) { private Class<?> nestOneClassDeeperIfAspect(Class<?> superclass, Class<?> origSubclass) {
boolean superclassIsAnAspect = false; boolean superclassIsAnAspect = false;
@ -215,8 +211,8 @@ public class ConfigurationEnhancer {
@Override @Override
protected byte[] transform(byte[] b) throws Exception { protected byte[] transform(byte[] b) throws Exception {
ClassWriter writer = new ClassWriter(false); ClassWriter writer = new ClassWriter(false);
ClassAdapter adapter = ClassAdapter adapter = new AddAnnotationAdapter(writer,
new AddAnnotationAdapter(writer, "Lorg/aspectj/lang/annotation/Aspect;"); "Lorg/aspectj/lang/annotation/Aspect;");
ClassReader reader = new ClassReader(b); ClassReader reader = new ClassReader(b);
reader.accept(adapter, false); reader.accept(adapter, false);
return writer.toByteArray(); return writer.toByteArray();

View File

@ -19,12 +19,11 @@ import org.objectweb.asm.AnnotationVisitor;
/** /**
* An empty AnnotationVisitor that delegates to another AnnotationVisitor. * An empty AnnotationVisitor that delegates to another AnnotationVisitor. This class can be
* This class can be used as a super class to quickly implement * used as a super class to quickly implement useful annotation adapter classes, just by
* useful annotation adapter classes, just by overriding the necessary * overriding the necessary methods. Note that for some reason, ASM doesn't provide this
* methods. Note that for some reason, ASM doesn't provide this class * class (it does provide MethodAdapter and ClassAdapter), thus we're following the general
* (it does provide MethodAdapter and ClassAdapter), thus we're following * pattern and adding our own here.
* the general pattern and adding our own here.
* *
* @author Chris Beams * @author Chris Beams
*/ */
@ -33,8 +32,8 @@ class AnnotationAdapter implements AnnotationVisitor {
private AnnotationVisitor delegate; private AnnotationVisitor delegate;
/** /**
* Creates a new AnnotationAdapter instance that will delegate * Creates a new AnnotationAdapter instance that will delegate all its calls to
* all its calls to <var>delegate</var>. * <var>delegate</var>.
* *
* @param delegate In most cases, the delegate will simply be * @param delegate In most cases, the delegate will simply be
* {@link AsmUtils#EMPTY_VISITOR} * {@link AsmUtils#EMPTY_VISITOR}

View File

@ -36,23 +36,26 @@ class AsmUtils {
/** /**
* @param className a standard, dot-delimeted, fully-qualified Java class name * @param className a standard, dot-delimeted, fully-qualified Java class name
* @return internal version of className, as per ASM guide section 2.1.2 "Internal Names" * @return internal version of className, as per ASM guide section 2.1.2
* "Internal Names"
*/ */
public static String convertClassNameToInternalName(String className) { public static String convertClassNameToInternalName(String className) {
return className.replace('.', '/'); return className.replace('.', '/');
} }
/** /**
* Convert a type descriptor to a classname suitable for classloading * Convert a type descriptor to a classname suitable for classloading with
* with Class.forName(). * Class.forName().
* *
* @param typeDescriptor see ASM guide section 2.1.3 * @param typeDescriptor see ASM guide section 2.1.3
*/ */
public static String convertTypeDescriptorToClassName(String typeDescriptor) { public static String convertTypeDescriptorToClassName(String typeDescriptor) {
final String internalName; // See ASM guide section 2.1.2 final String internalName; // See ASM guide section 2.1.2
// TODO: SJC-242 should catch all possible cases. use case statement and switch on char // TODO: SJC-242 should catch all possible cases. use case statement and switch on
// TODO: SJC-242 converting from primitive to object here won't be intuitive to users // char
// TODO: SJC-242 converting from primitive to object here won't be intuitive to
// users
if ("V".equals(typeDescriptor)) if ("V".equals(typeDescriptor))
return Void.class.getName(); return Void.class.getName();
if ("I".equals(typeDescriptor)) if ("I".equals(typeDescriptor))
@ -87,10 +90,11 @@ class AsmUtils {
} }
/** /**
* Creates a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class' * Creates a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class' to
* to pathToClass before attempting to load. * pathToClass before attempting to load.
* *
* @throws RuntimeException if <var>pathToClass</var>+.class cannot be found on the classpath * @throws RuntimeException if <var>pathToClass</var>+.class cannot be found on the
* classpath
* @throws RuntimeException if an IOException occurs when creating the new ClassReader * @throws RuntimeException if an IOException occurs when creating the new ClassReader
*/ */
public static ClassReader newClassReader(String pathToClass) { public static ClassReader newClassReader(String pathToClass) {
@ -99,12 +103,13 @@ class AsmUtils {
} }
/** /**
* Convenience method that simply returns a new ASM {@link ClassReader} instance based on * Convenience method that simply returns a new ASM {@link ClassReader} instance based
* the supplied <var>bytes</var> byte array. This method is exactly equivalent to calling * on the supplied <var>bytes</var> byte array. This method is exactly equivalent to
* new ClassReader(byte[]), and is mainly provided for symmetry with usage of * calling new ClassReader(byte[]), and is mainly provided for symmetry with usage of
* {@link #newClassReader(InputStream)}. * {@link #newClassReader(InputStream)}.
* *
* @param bytes byte array that will be provided as input to the new ClassReader instance. * @param bytes byte array that will be provided as input to the new ClassReader
* instance.
* *
* @return * @return
*/ */
@ -113,10 +118,11 @@ class AsmUtils {
} }
/** /**
* Convenience method that creates and returns a new ASM {@link ClassReader} for the given * Convenience method that creates and returns a new ASM {@link ClassReader} for the
* InputStream <var>is</var>, closing the InputStream after creating the ClassReader and rethrowing * given InputStream <var>is</var>, closing the InputStream after creating the
* any IOException thrown during ClassReader instantiation as an unchecked exception. Logs and ignores * ClassReader and rethrowing any IOException thrown during ClassReader instantiation as
* any IOException thrown when closing the InputStream. * an unchecked exception. Logs and ignores any IOException thrown when closing the
* InputStream.
* *
* @param is InputStream that will be provided to the new ClassReader instance. * @param is InputStream that will be provided to the new ClassReader instance.
*/ */
@ -124,7 +130,8 @@ class AsmUtils {
try { try {
return new ClassReader(is); return new ClassReader(is);
} catch (IOException ex) { } catch (IOException ex) {
throw new RuntimeException("An unexpected exception occurred while creating ASM ClassReader: " + ex); throw new RuntimeException("An unexpected exception occurred while creating ASM ClassReader: "
+ ex);
} finally { } finally {
try { try {
is.close(); is.close();

View File

@ -34,9 +34,11 @@ import org.springframework.config.java.Factory;
import org.springframework.config.java.ModelClass; import org.springframework.config.java.ModelClass;
import org.springframework.config.java.ModelMethod; import org.springframework.config.java.ModelMethod;
/** /**
* Visits a single method declared in a given {@link Configuration} class. Determines whether the * Visits a single method declared in a given {@link Configuration} class. Determines
* method is a {@link Factory} method and if so, adds it to the {@link ConfigurationClass}. * whether the method is a {@link Factory} method and if so, adds it to the
* {@link ConfigurationClass}.
* *
* @author Chris Beams * @author Chris Beams
*/ */
@ -70,8 +72,8 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
} }
/** /**
* Visits a single annotation on this method. Will be called once for each * Visits a single annotation on this method. Will be called once for each annotation
* annotation present (regardless of its RetentionPolicy). * present (regardless of its RetentionPolicy).
*/ */
@Override @Override
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) { public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
@ -90,12 +92,11 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
} }
/** /**
* Provides the line number of this method within its declaring class. In reality, * Provides the line number of this method within its declaring class. In reality, this
* this number is always inaccurate - <var>lineNo</var> represents the line number * number is always inaccurate - <var>lineNo</var> represents the line number of the
* of the first instruction in this method. Method declaration line numbers are * first instruction in this method. Method declaration line numbers are not in any way
* not in any way tracked in the bytecode. Any tooling or output that reads this * tracked in the bytecode. Any tooling or output that reads this value will have to
* value will have to compensate and estimate where the actual method declaration * compensate and estimate where the actual method declaration is.
* is.
*/ */
@Override @Override
public void visitLineNumber(int lineNo, Label start) { public void visitLineNumber(int lineNo, Label start) {
@ -104,8 +105,8 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
/** /**
* Parses through all {@link #annotations} on this method in order to determine whether * Parses through all {@link #annotations} on this method in order to determine whether
* it is a {@link Factory} method or not and if so adds it to the * it is a {@link Factory} method or not and if so adds it to the enclosing
* enclosing {@link #configClass}. * {@link #configClass}.
*/ */
@Override @Override
public void visitEnd() { public void visitEnd() {

View File

@ -37,8 +37,8 @@ import org.springframework.util.ClassUtils;
/** /**
* Visits a {@link Configuration} class, populating a {@link ConfigurationClass} instance with * Visits a {@link Configuration} class, populating a {@link ConfigurationClass} instance
* information gleaned along the way. * with information gleaned along the way.
* *
* @author Chris Beams * @author Chris Beams
*/ */
@ -65,10 +65,8 @@ class ConfigurationClassVisitor extends ClassAdapter {
@Override @Override
public void visitSource(String sourceFile, String debug) { public void visitSource(String sourceFile, String debug) {
String resourcePath = String resourcePath = convertClassNameToResourcePath(configClass.getName()).substring(0,
convertClassNameToResourcePath(configClass.getName()) configClass.getName().lastIndexOf('.') + 1).concat(sourceFile);
.substring(0, configClass.getName().lastIndexOf('.')+1)
.concat(sourceFile);
configClass.setSource(resourcePath); configClass.setSource(resourcePath);
} }
@ -184,18 +182,18 @@ class ConfigurationClassVisitor extends ClassAdapter {
* {@link ConfigurationClassMethodVisitor}. * {@link ConfigurationClassMethodVisitor}.
*/ */
@Override @Override
public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, String arg3,
String arg3, String[] arg4) { String[] arg4) {
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers); return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers);
} }
/** /**
* Implementation deals with inner classes here even though it would have * Implementation deals with inner classes here even though it would have been more
* been more intuitive to deal with outer classes. Due to limitations in ASM * intuitive to deal with outer classes. Due to limitations in ASM (resulting from
* (resulting from limitations in the VM spec) we cannot directly look for outer classes * limitations in the VM spec) we cannot directly look for outer classes in all cases,
* in all cases, so instead build up a model of {@link #innerClasses} and process * so instead build up a model of {@link #innerClasses} and process declaring class
* declaring class logic in a kind of inverted manner. * logic in a kind of inverted manner.
*/ */
@Override @Override
public void visitInnerClass(String name, String outerName, String innerName, int access) { public void visitInnerClass(String name, String outerName, String innerName, int access) {
@ -217,8 +215,8 @@ class ConfigurationClassVisitor extends ClassAdapter {
ConfigurationClass innerConfigClass = new ConfigurationClass(); ConfigurationClass innerConfigClass = new ConfigurationClass();
ConfigurationClassVisitor ccVisitor = ConfigurationClassVisitor ccVisitor = new ConfigurationClassVisitor(innerConfigClass,
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel()); new ConfigurationModel());
ccVisitor.setProcessInnerClasses(false); ccVisitor.setProcessInnerClasses(false);
ClassReader reader = AsmUtils.newClassReader(name); ClassReader reader = AsmUtils.newClassReader(name);

View File

@ -16,16 +16,15 @@
package org.springframework.config.java.internal.parsing; package org.springframework.config.java.internal.parsing;
/** /**
* Note: the visibility of this interface would be reduced to package-private * Note: the visibility of this interface would be reduced to package-private save for an
* save for an obscure restriction of JDK dynamic proxies. * obscure restriction of JDK dynamic proxies.
* {@link MutableAnnotationUtils#createMutableAnnotation(Class)} creates a proxy * {@link MutableAnnotationUtils#createMutableAnnotation(Class)} creates a proxy based on
* based on two interfaces: this one, and whatever annotation is currently being * two interfaces: this one, and whatever annotation is currently being parsed. The
* parsed. The restriction is that both interfaces may not be package-private if * restriction is that both interfaces may not be package-private if they are in separate
* they are in separate packages. In order to avoid unnecessarily restricting * packages. In order to avoid unnecessarily restricting the visibility options for
* the visibility options for user-defined annotations, this interface becomes * user-defined annotations, this interface becomes public. Because it is in the internal.*
* public. Because it is in the internal.* package, it won't pollute the public * package, it won't pollute the public API, but developers should take caution not to use
* API, but developers should take caution not to use this annotation outside * this annotation outside the internal.parsing package.
* the internal.parsing package.
* *
* @author Chris Beams * @author Chris Beams
*/ */

View File

@ -102,8 +102,10 @@ final class MutableAnnotationInvocationHandler implements InvocationHandler {
final int attribValueHashCode; final int attribValueHashCode;
if (attribValue == null) if (attribValue == null)
// memberValue may be null when a mutable annotation is being added to a collection // memberValue may be null when a mutable annotation is being added to a
// and before it has actually been visited (and populated) by MutableAnnotationVisitor // collection
// and before it has actually been visited (and populated) by
// MutableAnnotationVisitor
attribValueHashCode = 0; attribValueHashCode = 0;
else if (attribValue.getClass().isArray()) else if (attribValue.getClass().isArray())
attribValueHashCode = Arrays.hashCode((Object[]) attribValue); attribValueHashCode = Arrays.hashCode((Object[]) attribValue);
@ -117,15 +119,17 @@ final class MutableAnnotationInvocationHandler implements InvocationHandler {
} }
/** /**
* Compares <var>proxy</var> object and <var>other</var> object by comparing the return values * Compares <var>proxy</var> object and <var>other</var> object by comparing the return
* of the methods specified by their common {@link Annotation} ancestry. * values of the methods specified by their common {@link Annotation} ancestry.
* <p/> * <p/>
* <var>other</var> must be the same type as or a subtype of <var>proxy</var>. * <var>other</var> must be the same type as or a subtype of <var>proxy</var>. Will
* Will return false otherwise. * return false otherwise.
* <p/> * <p/>
* Eagerly returns true if {@code proxy} == <var>other</var></p> * Eagerly returns true if {@code proxy} == <var>other</var>
* </p>
* <p/> * <p/>
* Conforms strictly to the equals() specification for Annotation</p> * Conforms strictly to the equals() specification for Annotation
* </p>
* *
* @see Annotation#equals(Object) * @see Annotation#equals(Object)
*/ */
@ -191,8 +195,7 @@ final class MutableAnnotationInvocationHandler implements InvocationHandler {
try { try {
method = annotationType.getDeclaredMethod(attributeName); method = annotationType.getDeclaredMethod(attributeName);
} } catch (Exception ex) {
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex); ReflectionUtils.handleReflectionException(ex);
} }

View File

@ -23,9 +23,9 @@ import java.lang.reflect.Proxy;
class MutableAnnotationUtils { class MutableAnnotationUtils {
/** /**
* Creates a {@link MutableAnnotation} for {@code annoType}. * Creates a {@link MutableAnnotation} for {@code annoType}. JDK dynamic proxies are
* JDK dynamic proxies are used, and the returned proxy implements * used, and the returned proxy implements both {@link MutableAnnotation} and annotation
* both {@link MutableAnnotation} and annotation type {@code A} * type {@code A}
* *
* @param <A> annotation type that must be supplied and returned * @param <A> annotation type that must be supplied and returned
* @param annoType type of annotation to create * @param annoType type of annotation to create

View File

@ -38,9 +38,9 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
protected final MutableAnnotation mutableAnno; protected final MutableAnnotation mutableAnno;
/** /**
* Creates a new {@link MutableAnnotationVisitor} instance that will populate * Creates a new {@link MutableAnnotationVisitor} instance that will populate the the
* the the attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation} * attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation} instead of
* instead of {@link MutableAnnotation} to avoid the need for callers to typecast. * {@link MutableAnnotation} to avoid the need for callers to typecast.
* *
* @param mutableAnno {@link MutableAnnotation} instance to visit and populate * @param mutableAnno {@link MutableAnnotation} instance to visit and populate
* *
@ -110,6 +110,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
return new MutableAnnotationVisitor(anno); return new MutableAnnotationVisitor(anno);
} }
public void visitEnd() { } public void visitEnd() {
}
} }

View File

@ -38,9 +38,9 @@ public @interface Extension {
Class<? extends ExtensionAnnotationBeanDefinitionRegistrar<?>> handler(); Class<? extends ExtensionAnnotationBeanDefinitionRegistrar<?>> handler();
/** /**
* The order in which this plugin will be processed * The order in which this plugin will be processed relative to others. Per the
* relative to others. Per the semantics of {@link Ordered}, * semantics of {@link Ordered}, lower integer values will be treated as higher
* lower integer values will be treated as higher priority. * priority.
* *
* @see Ordered * @see Ordered
*/ */

View File

@ -15,7 +15,6 @@
*/ */
package org.springframework.config.java.support; package org.springframework.config.java.support;
import static java.lang.String.*; import static java.lang.String.*;
import static org.springframework.config.java.Util.*; import static org.springframework.config.java.Util.*;
@ -44,10 +43,9 @@ import org.springframework.core.io.Resource;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Reads a given fully-populated {@link ConfigurationModel}, registering bean definitions with * Reads a given fully-populated {@link ConfigurationModel}, registering bean definitions
* the given {@link BeanDefinitionRegistry} based on its contents. * with the given {@link BeanDefinitionRegistry} based on its contents.
* <p> * <p>
* This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does not * This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does not
* implement/extend any of its artifacts as {@link ConfigurationModel} is not a * implement/extend any of its artifacts as {@link ConfigurationModel} is not a
@ -70,8 +68,8 @@ class ConfigurationModelBeanDefinitionReader {
} }
/** /**
* Reads {@code model}, registering bean definitions with {@link #beanFactory} * Reads {@code model}, registering bean definitions with {@link #beanFactory} based on
* based on its contents. * its contents.
* *
* @return number of bean definitions generated * @return number of bean definitions generated
*/ */
@ -85,8 +83,8 @@ class ConfigurationModelBeanDefinitionReader {
} }
/** /**
* Reads a particular {@link ConfigurationClass}, registering bean definitions * Reads a particular {@link ConfigurationClass}, registering bean definitions for the
* for the class itself, all its {@link Factory} methods and all its {@link Extension} * class itself, all its {@link Factory} methods and all its {@link Extension}
* annotations. * annotations.
*/ */
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
@ -112,7 +110,8 @@ class ConfigurationModelBeanDefinitionReader {
Class<?> beanClass = RequiredAnnotationBeanPostProcessor.class; Class<?> beanClass = RequiredAnnotationBeanPostProcessor.class;
String beanName = beanClass.getName() + "#0"; String beanName = beanClass.getName() + "#0";
requiredAnnotationPostProcessor.setBeanClass(beanClass); requiredAnnotationPostProcessor.setBeanClass(beanClass);
requiredAnnotationPostProcessor.setResourceDescription("ensures @Required methods have been invoked"); requiredAnnotationPostProcessor
.setResourceDescription("ensures @Required methods have been invoked");
beanFactory.registerBeanDefinition(beanName, requiredAnnotationPostProcessor); beanFactory.registerBeanDefinition(beanName, requiredAnnotationPostProcessor);
} }
@ -125,9 +124,11 @@ class ConfigurationModelBeanDefinitionReader {
// and potentially has PropertyValues and ConstructorArgs) // and potentially has PropertyValues and ConstructorArgs)
if (beanFactory.containsBeanDefinition(configBeanName)) { if (beanFactory.containsBeanDefinition(configBeanName)) {
if (log.isInfoEnabled()) if (log.isInfoEnabled())
log.info(format("Copying property and constructor arg values from existing bean definition for " log.info(format(
"Copying property and constructor arg values from existing bean definition for "
+ "@Configuration class %s to new bean definition", configBeanName)); + "@Configuration class %s to new bean definition", configBeanName));
AbstractBeanDefinition existing = (AbstractBeanDefinition)beanFactory.getBeanDefinition(configBeanName); AbstractBeanDefinition existing = (AbstractBeanDefinition) beanFactory
.getBeanDefinition(configBeanName);
configBeanDef.setPropertyValues(existing.getPropertyValues()); configBeanDef.setPropertyValues(existing.getPropertyValues());
configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues()); configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues());
configBeanDef.setResource(existing.getResource()); configBeanDef.setResource(existing.getResource());
@ -141,8 +142,8 @@ class ConfigurationModelBeanDefinitionReader {
/** /**
* Reads a particular {@link ModelMethod}, registering bean definitions * Reads a particular {@link ModelMethod}, registering bean definitions with
* with {@link #beanFactory} based on its contents. * {@link #beanFactory} based on its contents.
* *
* @see Factory * @see Factory
*/ */
@ -158,9 +159,8 @@ class ConfigurationModelBeanDefinitionReader {
// assert this as an invariant now // assert this as an invariant now
Class<?> annoClass = anno.getClass(); Class<?> annoClass = anno.getClass();
Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class); Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class);
Assert.isTrue(extensionAnno != null, Assert.isTrue(extensionAnno != null, format("%s annotation is not annotated as a @%s", annoClass,
format("%s annotation is not annotated as a @%s", Extension.class.getSimpleName()));
annoClass, Extension.class.getSimpleName()));
Class<? extends ExtensionAnnotationBeanDefinitionRegistrar> extHandlerClass = extensionAnno.handler(); Class<? extends ExtensionAnnotationBeanDefinitionRegistrar> extHandlerClass = extensionAnno.handler();
@ -178,8 +178,8 @@ class ConfigurationModelBeanDefinitionReader {
private Integer getOrder(Annotation a) { private Integer getOrder(Annotation a) {
Extension plugin = a.annotationType().getAnnotation(Extension.class); Extension plugin = a.annotationType().getAnnotation(Extension.class);
if (plugin == null) if (plugin == null)
throw new IllegalArgumentException( throw new IllegalArgumentException("annotation was not annotated with @Plugin: "
"annotation was not annotated with @Plugin: " + a.annotationType()); + a.annotationType());
return plugin.order(); return plugin.order();
} }
} }

View File

@ -40,8 +40,8 @@ import org.springframework.util.ClassUtils;
/** /**
* {@link BeanFactoryPostProcessor} used for bootstrapping {@link Configuration @Configuration} * {@link BeanFactoryPostProcessor} used for bootstrapping {@link Configuration
* beans from Spring XML files. * @Configuration} beans from Spring XML files.
*/ */
public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProcessor { public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProcessor {
@ -49,8 +49,8 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
/** /**
* Returns the order in which this {@link BeanPostProcessor} will be executed. * Returns the order in which this {@link BeanPostProcessor} will be executed. Returns
* Returns {@link Ordered#HIGHEST_PRECEDENCE}. * {@link Ordered#HIGHEST_PRECEDENCE}.
*/ */
public int getOrder() { public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; return Ordered.HIGHEST_PRECEDENCE;
@ -58,8 +58,8 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
/** /**
* Searches <var>beanFactory</var> for any {@link Configuration} classes in order * Searches <var>beanFactory</var> for any {@link Configuration} classes in order to
* to parse and enhance them. Also registers any {@link BeanPostProcessor} objects * parse and enhance them. Also registers any {@link BeanPostProcessor} objects
* necessary to fulfill JavaConfig requirements. * necessary to fulfill JavaConfig requirements.
*/ */
public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException { public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
@ -78,7 +78,8 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
private void parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { private void parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
// linked map is important for maintaining predictable ordering of configuration classes. // linked map is important for maintaining predictable ordering of configuration
// classes.
// this is important in bean / value override situations. // this is important in bean / value override situations.
LinkedHashMap<String, ClassPathResource> configClassResources = new LinkedHashMap<String, ClassPathResource>(); LinkedHashMap<String, ClassPathResource> configClassResources = new LinkedHashMap<String, ClassPathResource>();
@ -93,7 +94,8 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
} }
} }
ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(beanFactory); ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(
beanFactory);
ConfigurationParser parser = new ConfigurationParser(model); ConfigurationParser parser = new ConfigurationParser(model);
for (String id : configClassResources.keySet()) for (String id : configClassResources.keySet())
@ -108,15 +110,16 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
} }
/** /**
* Post-processes a BeanFactory in search of Configuration class BeanDefinitions; any candidates * Post-processes a BeanFactory in search of Configuration class BeanDefinitions; any
* are then enhanced by a {@link ConfigurationEnhancer}. Candidate status is determined by * candidates are then enhanced by a {@link ConfigurationEnhancer}. Candidate status is
* BeanDefinition attribute metadata. * determined by BeanDefinition attribute metadata.
* *
* @author Chris Beams * @author Chris Beams
* @see ConfigurationEnhancer * @see ConfigurationEnhancer
* @see BeanFactoryPostProcessor * @see BeanFactoryPostProcessor
*/ */
private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory,
ConfigurationModel model) {
ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model); ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model);
@ -133,7 +136,10 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
String enhancedClassName = enhancer.enhance(configClassName); String enhancedClassName = enhancer.enhance(configClassName);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'", logger
.debug(String
.format(
"Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'",
beanName, configClassName, enhancedClassName)); beanName, configClassName, enhancedClassName));
beanDef.setBeanClassName(enhancedClassName); beanDef.setBeanClassName(enhancedClassName);
@ -146,22 +152,21 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
} }
/** /**
* Determines whether the class for <var>beanDef</var> is a {@link Configuration}-annotated * Determines whether the class for <var>beanDef</var> is a {@link Configuration}
* class. Returns false if <var>beanDef</var> has no class specified. * -annotated class. Returns false if <var>beanDef</var> has no class specified.
* <p> * <p>
* Note: the classloading used within should not be problematic or interfere with tooling in any * Note: the classloading used within should not be problematic or interfere with
* way. BeanFactoryPostProcessing happens only during actual runtime processing via * tooling in any way. BeanFactoryPostProcessing happens only during actual runtime
* {@link JavaConfigApplicationContext} or via XML using {@link ConfigurationPostProcessor}. In * processing via {@link JavaConfigApplicationContext} or via XML using
* any case, tooling (Spring IDE) will hook in at a lower level than this class and * {@link ConfigurationPostProcessor}. In any case, tooling (Spring IDE) will hook in at
* thus never encounter this classloading. Should this become problematic, it would not be * a lower level than this class and thus never encounter this classloading. Should this
* too difficult to replace the following with ASM logic that traverses the class hierarchy in * become problematic, it would not be too difficult to replace the following with ASM
* order to find whether the class is directly or indirectly annotated with * logic that traverses the class hierarchy in order to find whether the class is
* {@link Configuration}. * directly or indirectly annotated with {@link Configuration}.
*/ */
private static boolean isConfigClass(BeanDefinition beanDef) { private static boolean isConfigClass(BeanDefinition beanDef) {
String className = beanDef.getBeanClassName(); String className = beanDef.getBeanClassName();
return className != null return className != null && loadRequiredClass(className).isAnnotationPresent(Configuration.class);
&& loadRequiredClass(className).isAnnotationPresent(Configuration.class);
} }
} }

View File

@ -2,6 +2,7 @@ package test.basic;
import org.junit.Before; import org.junit.Before;
public abstract class AbstractJavaConfigTests { public abstract class AbstractJavaConfigTests {
@Before @Before

View File

@ -13,11 +13,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.beans.Colour; import test.beans.Colour;
import test.beans.TestBean; import test.beans.TestBean;
public class AutowiredConfigurationTests { public class AutowiredConfigurationTests {
public @Test void test() { public @Test
void test() {
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext( ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext(
AutowiredConfigurationTests.class.getSimpleName() + ".xml", AutowiredConfigurationTests.class.getSimpleName() + ".xml", AutowiredConfigurationTests.class);
AutowiredConfigurationTests.class);
assertThat(factory.getBean("colour", Colour.class), equalTo(Colour.RED)); assertThat(factory.getBean("colour", Colour.class), equalTo(Colour.RED));
assertThat(factory.getBean("testBean", TestBean.class).getName(), equalTo(Colour.RED.toString())); assertThat(factory.getBean("testBean", TestBean.class).getName(), equalTo(Colour.RED.toString()));
@ -25,21 +26,26 @@ public class AutowiredConfigurationTests {
@Configuration @Configuration
static class AutowiredConfig { static class AutowiredConfig {
private @Autowired Colour colour; private @Autowired
Colour colour;
public @Bean TestBean testBean() { public @Bean
TestBean testBean() {
return new TestBean(colour.toString()); return new TestBean(colour.toString());
} }
} }
@Configuration @Configuration
static class ColorConfig { static class ColorConfig {
public @Bean Colour colour() { return Colour.RED; } public @Bean
Colour colour() {
return Colour.RED;
}
} }
public @Test
public @Test void testValueInjection() { void testValueInjection() {
System.setProperty("myProp", "foo"); System.setProperty("myProp", "foo");
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext( ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext(
@ -55,7 +61,8 @@ public class AutowiredConfigurationTests {
@Value("#{systemProperties.myProp}") @Value("#{systemProperties.myProp}")
private String name = "default"; private String name = "default";
public @Bean TestBean testBean() { public @Bean
TestBean testBean() {
return new TestBean(name); return new TestBean(name);
} }
} }

View File

@ -17,16 +17,18 @@ import org.springframework.config.java.support.ConfigurationPostProcessor;
import test.beans.ITestBean; import test.beans.ITestBean;
import test.beans.TestBean; import test.beans.TestBean;
public class BasicTests { public class BasicTests {
/** /**
* Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition} for * Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition} for
* each of the given {@link Configuration} <var>configClasses</var>, and then post-processes * each of the given {@link Configuration} <var>configClasses</var>, and then
* the factory using JavaConfig's {@link ConfigurationPostProcessor}. When complete, * post-processes the factory using JavaConfig's {@link ConfigurationPostProcessor}.
* the factory is ready to service requests for any {@link Bean} methods declared by * When complete, the factory is ready to service requests for any {@link Bean} methods
* <var>configClasses</var>. * declared by <var>configClasses</var>.
* *
* @param configClasses the {@link Configuration} classes under test. may be an empty list. * @param configClasses the {@link Configuration} classes under test. may be an empty
* list.
* *
* @return fully initialized and post-processed {@link BeanFactory} * @return fully initialized and post-processed {@link BeanFactory}
*/ */
@ -35,7 +37,8 @@ public class BasicTests {
for (Class<?> configClass : configClasses) { for (Class<?> configClass : configClasses) {
String configBeanName = configClass.getName(); String configBeanName = configClass.getName();
factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass).getBeanDefinition()); factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass)
.getBeanDefinition());
} }
new ConfigurationPostProcessor().postProcessBeanFactory(factory); new ConfigurationPostProcessor().postProcessBeanFactory(factory);
@ -57,7 +60,8 @@ public class BasicTests {
@Configuration @Configuration
static class SimplestPossibleConfig { static class SimplestPossibleConfig {
public @Bean String stringBean() { public @Bean
String stringBean() {
return "foo"; return "foo";
} }
} }
@ -77,13 +81,15 @@ public class BasicTests {
@Configuration @Configuration
static class ConfigWithPrototypeBean { static class ConfigWithPrototypeBean {
public @Bean TestBean foo() { public @Bean
TestBean foo() {
TestBean foo = new TestBean("foo"); TestBean foo = new TestBean("foo");
foo.setSpouse(bar()); foo.setSpouse(bar());
return foo; return foo;
} }
public @Bean TestBean bar() { public @Bean
TestBean bar() {
TestBean bar = new TestBean("bar"); TestBean bar = new TestBean("bar");
bar.setSpouse(baz()); bar.setSpouse(baz());
return bar; return bar;
@ -95,4 +101,3 @@ public class BasicTests {
} }
} }
} }

View File

@ -34,5 +34,7 @@ public class Colour extends ShortCodedLabeledEnum {
public static final Colour PURPLE = new Colour(3, "PURPLE"); public static final Colour PURPLE = new Colour(3, "PURPLE");
private Colour(int code, String label) { super(code, label); } private Colour(int code, String label) {
super(code, label);
}
} }

View File

@ -22,10 +22,16 @@ public class DependsOnTestBean {
private int state; private int state;
public void setTestBean(TestBean tb) { this.tb = tb; } public void setTestBean(TestBean tb) {
this.tb = tb;
}
public int getState() { return state; } public int getState() {
return state;
}
public TestBean getTestBean() { return tb; } public TestBean getTestBean() {
return tb;
}
} }

View File

@ -19,8 +19,8 @@ import java.io.IOException;
/** /**
* Interface used for test beans. Two methods are the same as on Person, but if * Interface used for test beans. Two methods are the same as on Person, but if this extends
* this extends person it breaks quite a few tests * person it breaks quite a few tests
* *
* @author Rod Johnson * @author Rod Johnson
*/ */

View File

@ -48,7 +48,9 @@ public class IndexedTestBean {
private SortedMap<String, TestBean> sortedMap; private SortedMap<String, TestBean> sortedMap;
public IndexedTestBean() { this(true); } public IndexedTestBean() {
this(true);
}
public IndexedTestBean(boolean populate) { public IndexedTestBean(boolean populate) {
if (populate) { if (populate) {
@ -84,32 +86,60 @@ public class IndexedTestBean {
this.map.put("key4", list); this.map.put("key4", list);
} }
public TestBean[] getArray() { return array; } public TestBean[] getArray() {
return array;
}
public void setArray(TestBean[] array) { this.array = array; } public void setArray(TestBean[] array) {
this.array = array;
}
public Collection<?> getCollection() { return collection; } public Collection<?> getCollection() {
return collection;
}
public void setCollection(Collection<TestBean> collection) { this.collection = collection; } public void setCollection(Collection<TestBean> collection) {
this.collection = collection;
}
public List<TestBean> getList() { return list; } public List<TestBean> getList() {
return list;
}
public void setList(List<TestBean> list) { this.list = list; } public void setList(List<TestBean> list) {
this.list = list;
}
public Set<TestBean> getSet() { return set; } public Set<TestBean> getSet() {
return set;
}
public void setSet(Set<TestBean> set) { this.set = set; } public void setSet(Set<TestBean> set) {
this.set = set;
}
public SortedSet<TestBean> getSortedSet() { return sortedSet; } public SortedSet<TestBean> getSortedSet() {
return sortedSet;
}
public void setSortedSet(SortedSet<TestBean> sortedSet) { this.sortedSet = sortedSet; } public void setSortedSet(SortedSet<TestBean> sortedSet) {
this.sortedSet = sortedSet;
}
public Map<String, Object> getMap() { return map; } public Map<String, Object> getMap() {
return map;
}
public void setMap(Map<String, Object> map) { this.map = map; } public void setMap(Map<String, Object> map) {
this.map = map;
}
public SortedMap<String, TestBean> getSortedMap() { return sortedMap; } public SortedMap<String, TestBean> getSortedMap() {
return sortedMap;
}
public void setSortedMap(SortedMap<String, TestBean> sortedMap) { this.sortedMap = sortedMap; } public void setSortedMap(SortedMap<String, TestBean> sortedMap) {
this.sortedMap = sortedMap;
}
} }

View File

@ -25,7 +25,8 @@ public class NestedTestBean implements INestedTestBean {
private String company = ""; private String company = "";
public NestedTestBean() { } public NestedTestBean() {
}
public NestedTestBean(String company) { public NestedTestBean(String company) {
setCompany(company); setCompany(company);

View File

@ -99,11 +99,16 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
private List<?> pets; private List<?> pets;
public TestBean() { } public TestBean() {
}
public TestBean(String name) { this.name = name; } public TestBean(String name) {
this.name = name;
}
public TestBean(ITestBean spouse) { this.spouse = spouse; } public TestBean(ITestBean spouse) {
this.spouse = spouse;
}
public TestBean(String name, int age) { public TestBean(String name, int age) {
this.name = name; this.name = name;
@ -115,39 +120,73 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
this.someProperties = someProperties; this.someProperties = someProperties;
} }
public void setBeanName(String beanName) { this.beanName = beanName; } public void setBeanName(String beanName) {
this.beanName = beanName;
}
public String getBeanName() { return beanName; } public String getBeanName() {
return beanName;
}
public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public BeanFactory getBeanFactory() { return beanFactory; } public BeanFactory getBeanFactory() {
return beanFactory;
}
public void setPostProcessed(boolean postProcessed) { this.postProcessed = postProcessed; } public void setPostProcessed(boolean postProcessed) {
this.postProcessed = postProcessed;
}
public boolean isPostProcessed() { return postProcessed; } public boolean isPostProcessed() {
return postProcessed;
}
public String getName() { return name; } public String getName() {
return name;
}
public void setName(String name) { this.name = name; } public void setName(String name) {
this.name = name;
}
public String getSex() { return sex; } public String getSex() {
return sex;
}
public void setSex(String sex) { this.sex = sex; } public void setSex(String sex) {
this.sex = sex;
}
public int getAge() { return age; } public int getAge() {
return age;
}
public void setAge(int age) { this.age = age; } public void setAge(int age) {
this.age = age;
}
public boolean isJedi() { return jedi; } public boolean isJedi() {
return jedi;
}
public void setJedi(boolean jedi) { this.jedi = jedi; } public void setJedi(boolean jedi) {
this.jedi = jedi;
}
public ITestBean getSpouse() { return spouse; } public ITestBean getSpouse() {
return spouse;
}
public void setSpouse(ITestBean spouse) { this.spouse = spouse; } public void setSpouse(ITestBean spouse) {
this.spouse = spouse;
}
public String getTouchy() { return touchy; } public String getTouchy() {
return touchy;
}
public void setTouchy(String touchy) throws Exception { public void setTouchy(String touchy) throws Exception {
if (touchy.indexOf('.') != -1) { if (touchy.indexOf('.') != -1) {
@ -161,77 +200,149 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
this.touchy = touchy; this.touchy = touchy;
} }
public String getCountry() { return country; } public String getCountry() {
return country;
}
public void setCountry(String country) { this.country = country; } public void setCountry(String country) {
this.country = country;
}
public String[] getStringArray() { return stringArray; } public String[] getStringArray() {
return stringArray;
}
public void setStringArray(String[] stringArray) { this.stringArray = stringArray; } public void setStringArray(String[] stringArray) {
this.stringArray = stringArray;
}
public Integer[] getSomeIntegerArray() { return someIntegerArray; } public Integer[] getSomeIntegerArray() {
return someIntegerArray;
}
public void setSomeIntegerArray(Integer[] someIntegerArray) { this.someIntegerArray = someIntegerArray; } public void setSomeIntegerArray(Integer[] someIntegerArray) {
this.someIntegerArray = someIntegerArray;
}
public Date getDate() { return date; } public Date getDate() {
return date;
}
public void setDate(Date date) { this.date = date; } public void setDate(Date date) {
this.date = date;
}
public Float getMyFloat() { return myFloat; } public Float getMyFloat() {
return myFloat;
}
public void setMyFloat(Float myFloat) { this.myFloat = myFloat; } public void setMyFloat(Float myFloat) {
this.myFloat = myFloat;
}
public Collection<?> getFriends() { return friends; } public Collection<?> getFriends() {
return friends;
}
public void setFriends(Collection<?> friends) { this.friends = friends; } public void setFriends(Collection<?> friends) {
this.friends = friends;
}
public Set<?> getSomeSet() { return someSet; } public Set<?> getSomeSet() {
return someSet;
}
public void setSomeSet(Set<?> someSet) { this.someSet = someSet; } public void setSomeSet(Set<?> someSet) {
this.someSet = someSet;
}
public Map<?, ?> getSomeMap() { return someMap; } public Map<?, ?> getSomeMap() {
return someMap;
}
public void setSomeMap(Map<?, ?> someMap) { this.someMap = someMap; } public void setSomeMap(Map<?, ?> someMap) {
this.someMap = someMap;
}
public List<?> getSomeList() { return someList; } public List<?> getSomeList() {
return someList;
}
public void setSomeList(List<?> someList) { this.someList = someList; } public void setSomeList(List<?> someList) {
this.someList = someList;
}
public Properties getSomeProperties() { return someProperties; } public Properties getSomeProperties() {
return someProperties;
}
public void setSomeProperties(Properties someProperties) { this.someProperties = someProperties; } public void setSomeProperties(Properties someProperties) {
this.someProperties = someProperties;
}
public INestedTestBean getDoctor() { return doctor; } public INestedTestBean getDoctor() {
return doctor;
}
public INestedTestBean getLawyer() { return lawyer; } public INestedTestBean getLawyer() {
return lawyer;
}
public void setDoctor(INestedTestBean bean) { doctor = bean; } public void setDoctor(INestedTestBean bean) {
doctor = bean;
}
public void setLawyer(INestedTestBean bean) { lawyer = bean; } public void setLawyer(INestedTestBean bean) {
lawyer = bean;
}
public Number getSomeNumber() { return someNumber; } public Number getSomeNumber() {
return someNumber;
}
public void setSomeNumber(Number someNumber) { this.someNumber = someNumber; } public void setSomeNumber(Number someNumber) {
this.someNumber = someNumber;
}
public Colour getFavouriteColour() { return favouriteColour; } public Colour getFavouriteColour() {
return favouriteColour;
}
public void setFavouriteColour(Colour favouriteColour) { this.favouriteColour = favouriteColour; } public void setFavouriteColour(Colour favouriteColour) {
this.favouriteColour = favouriteColour;
}
public Boolean getSomeBoolean() { return someBoolean; } public Boolean getSomeBoolean() {
return someBoolean;
}
public void setSomeBoolean(Boolean someBoolean) { this.someBoolean = someBoolean; } public void setSomeBoolean(Boolean someBoolean) {
this.someBoolean = someBoolean;
}
public IndexedTestBean getNestedIndexedBean() { return nestedIndexedBean; } public IndexedTestBean getNestedIndexedBean() {
return nestedIndexedBean;
}
public void setNestedIndexedBean(IndexedTestBean nestedIndexedBean) { this.nestedIndexedBean = nestedIndexedBean; } public void setNestedIndexedBean(IndexedTestBean nestedIndexedBean) {
this.nestedIndexedBean = nestedIndexedBean;
}
public List<?> getOtherColours() { return otherColours; } public List<?> getOtherColours() {
return otherColours;
}
public void setOtherColours(List<?> otherColours) { this.otherColours = otherColours; } public void setOtherColours(List<?> otherColours) {
this.otherColours = otherColours;
}
public List<?> getPets() { return pets; } public List<?> getPets() {
return pets;
}
public void setPets(List<?> pets) { this.pets = pets; } public void setPets(List<?> pets) {
this.pets = pets;
}
/** /**
* @see ITestBean#exceptional(Throwable) * @see ITestBean#exceptional(Throwable)
@ -242,23 +353,34 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
} }
} }
public void unreliableFileOperation() throws IOException { throw new IOException(); } public void unreliableFileOperation() throws IOException {
throw new IOException();
}
/** /**
* @see ITestBean#returnsThis() * @see ITestBean#returnsThis()
*/ */
public Object returnsThis() { return this; } public Object returnsThis() {
return this;
}
/** /**
* @see IOther#absquatulate() * @see IOther#absquatulate()
*/ */
public void absquatulate() { } public void absquatulate() {
}
public int haveBirthday() { return age++; } public int haveBirthday() {
return age++;
}
public void destroy() { this.destroyed = true; } public void destroy() {
this.destroyed = true;
}
public boolean wasDestroyed() { return destroyed; } public boolean wasDestroyed() {
return destroyed;
}
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
@ -275,7 +397,9 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
} }
@Override @Override
public int hashCode() { return this.age; } public int hashCode() {
return this.age;
}
public int compareTo(Object other) { public int compareTo(Object other) {
if ((this.name != null) && (other instanceof TestBean)) { if ((this.name != null) && (other instanceof TestBean)) {