Formatting pass, primarily to align with Spring's convention of hard tab indentation.
This commit is contained in:
parent
f8270428df
commit
f9918f9b2e
|
@ -29,70 +29,74 @@ 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>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>
|
*
|
||||||
*
|
* <p>
|
||||||
* @author Rod Johnson
|
* Configuration-annotated classes are also candidates for component scanning thanks to the
|
||||||
* @author Chris Beams
|
* fact that this annotation is meta-annotated with {@link Component @Component}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Rod Johnson
|
||||||
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Target({ ElementType.TYPE })
|
@Target( { ElementType.TYPE })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@Documented
|
@Documented
|
||||||
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 "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the default autowiring strategy.
|
* Specifies the default autowiring strategy.
|
||||||
*
|
*
|
||||||
* @see Autowire
|
* @see Autowire
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Autowire defaultAutowire() default Autowire.INHERITED;
|
Autowire defaultAutowire() default Autowire.INHERITED;
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Dependency check strategy. By default, the dependency check is
|
// * Dependency check strategy. By default, the dependency check is
|
||||||
// * unspecified, that is the default Spring option will apply. In most cases,
|
// * unspecified, that is the default Spring option will apply. In most cases,
|
||||||
// * it means no dependency check will be done.
|
// * it means no dependency check will be done.
|
||||||
// *
|
// *
|
||||||
// * @see DependencyCheck
|
// * @see DependencyCheck
|
||||||
// * @return
|
// * @return
|
||||||
// */
|
// */
|
||||||
// DependencyCheck defaultDependencyCheck() default DependencyCheck.UNSPECIFIED;
|
// DependencyCheck defaultDependencyCheck() default DependencyCheck.UNSPECIFIED;
|
||||||
//
|
//
|
||||||
// /**
|
// /**
|
||||||
// * Bean instantiation strategy. By default, it is unspecified.
|
// * Bean instantiation strategy. By default, it is unspecified.
|
||||||
// *
|
// *
|
||||||
// * @see Lazy
|
// * @see Lazy
|
||||||
// * @return
|
// * @return
|
||||||
// */
|
// */
|
||||||
// Lazy defaultLazy() default Lazy.UNSPECIFIED;
|
// Lazy defaultLazy() default Lazy.UNSPECIFIED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do we autowire with aspects from the enclosing factory scope?
|
* Do we autowire with aspects from the enclosing factory scope?
|
||||||
*/
|
*/
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,308 +30,312 @@ 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,
|
*
|
||||||
* this class follows the fluent interface / builder pattern such that a model can be built up
|
* <p>
|
||||||
* easily by method chaining.</p>
|
* Like the rest of the {@link org.springframework.config.java.model model} package, this
|
||||||
*
|
* class follows the fluent interface / builder pattern such that a model can be built up
|
||||||
* @author Chris Beams
|
* easily by method chaining.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
// TODO: SJC-242 update documentation in light of generalization changes
|
// TODO: SJC-242 update documentation in light of generalization changes
|
||||||
// consider removing all refs to Bean, ExternalBean, etc.
|
// consider removing all refs to Bean, ExternalBean, etc.
|
||||||
public final class ConfigurationClass extends ModelClass implements Validatable {
|
public final class ConfigurationClass extends ModelClass implements Validatable {
|
||||||
|
|
||||||
private String beanName;
|
private String beanName;
|
||||||
|
|
||||||
private int modifiers;
|
private int modifiers;
|
||||||
|
|
||||||
private Configuration metadata;
|
private Configuration metadata;
|
||||||
|
|
||||||
private HashSet<ModelMethod> methods = new HashSet<ModelMethod>();
|
private HashSet<ModelMethod> methods = new HashSet<ModelMethod>();
|
||||||
|
|
||||||
private HashSet<Annotation> pluginAnnotations = new HashSet<Annotation>();
|
|
||||||
|
|
||||||
private ConfigurationClass declaringClass;
|
private HashSet<Annotation> pluginAnnotations = new HashSet<Annotation>();
|
||||||
|
|
||||||
public ConfigurationClass() { }
|
private ConfigurationClass declaringClass;
|
||||||
|
|
||||||
// TODO: get rid of constructors used only for testing. put in testing util.
|
public ConfigurationClass() {
|
||||||
/**
|
}
|
||||||
* Creates a new ConfigurationClass named <var>className.</var>
|
|
||||||
*
|
|
||||||
* @param name fully-qualified Configuration class being represented
|
|
||||||
*
|
|
||||||
* @see #setClassName(String)
|
|
||||||
*/
|
|
||||||
ConfigurationClass(String name) {
|
|
||||||
this(name, null, defaultAnnotation(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigurationClass(String name, Configuration metadata) {
|
// TODO: get rid of constructors used only for testing. put in testing util.
|
||||||
this(name, null, metadata, 0);
|
/**
|
||||||
}
|
* Creates a new ConfigurationClass named <var>className.</var>
|
||||||
|
*
|
||||||
|
* @param name fully-qualified Configuration class being represented
|
||||||
|
*
|
||||||
|
* @see #setClassName(String)
|
||||||
|
*/
|
||||||
|
ConfigurationClass(String name) {
|
||||||
|
this(name, null, defaultAnnotation(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
ConfigurationClass(String name, int modifiers) {
|
ConfigurationClass(String name, Configuration metadata) {
|
||||||
this(name, null, defaultAnnotation(), modifiers);
|
this(name, null, metadata, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Configuration defaultAnnotation() {
|
|
||||||
@Configuration class Prototype { }
|
|
||||||
return Prototype.class.getAnnotation(Configuration.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
ConfigurationClass(String name, int modifiers) {
|
||||||
* Creates a new ConfigurationClass object.
|
this(name, null, defaultAnnotation(), modifiers);
|
||||||
*
|
}
|
||||||
* @param name Fully qualified name of the class being represented
|
|
||||||
* @param id Bean name/id (if any) of this configuration class. used only in the case
|
|
||||||
* of XML integration where {@link Configuration} beans may have a
|
|
||||||
* user-specified id.
|
|
||||||
* @param metadata Configuration annotation resident on this class. May be null indicating
|
|
||||||
* that the user specified this class to be processed but failed to properly
|
|
||||||
* annotate it.
|
|
||||||
* @param modifiers Per {@link java.lang.reflect.Modifier}
|
|
||||||
*/
|
|
||||||
public ConfigurationClass(String name, String id, Configuration metadata, int modifiers) {
|
|
||||||
super(name);
|
|
||||||
Assert.hasText(name, "Configuration class name must have text");
|
|
||||||
|
|
||||||
setBeanName(id);
|
private static Configuration defaultAnnotation() {
|
||||||
setMetadata(metadata);
|
@Configuration
|
||||||
setModifiers(modifiers);
|
class Prototype {
|
||||||
}
|
}
|
||||||
|
return Prototype.class.getAnnotation(Configuration.class);
|
||||||
public ConfigurationClass addMethod(ModelMethod method) {
|
}
|
||||||
method.setDeclaringClass(this);
|
|
||||||
methods.add(method);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBeanName() {
|
/**
|
||||||
return beanName == null ? getName() : beanName;
|
* Creates a new ConfigurationClass object.
|
||||||
}
|
*
|
||||||
|
* @param name Fully qualified name of the class being represented
|
||||||
|
* @param id Bean name/id (if any) of this configuration class. used only in the case of
|
||||||
|
* XML integration where {@link Configuration} beans may have a user-specified
|
||||||
|
* id.
|
||||||
|
* @param metadata Configuration annotation resident on this class. May be null
|
||||||
|
* indicating that the user specified this class to be processed but failed to
|
||||||
|
* properly annotate it.
|
||||||
|
* @param modifiers Per {@link java.lang.reflect.Modifier}
|
||||||
|
*/
|
||||||
|
public ConfigurationClass(String name, String id, Configuration metadata, int modifiers) {
|
||||||
|
super(name);
|
||||||
|
Assert.hasText(name, "Configuration class name must have text");
|
||||||
|
|
||||||
public ConfigurationClass setBeanName(String id) {
|
setBeanName(id);
|
||||||
this.beanName = id;
|
setMetadata(metadata);
|
||||||
return this;
|
setModifiers(modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ModelMethod> getMethods() {
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Annotation[] getPluginAnnotations() {
|
public ConfigurationClass addMethod(ModelMethod method) {
|
||||||
return pluginAnnotations.toArray(new Annotation[pluginAnnotations.size()]);
|
method.setDeclaringClass(this);
|
||||||
}
|
methods.add(method);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
public String getBeanName() {
|
||||||
* Add a {@link Extension @Plugin}-annotated annotation to this configuration class.
|
return beanName == null ? getName() : beanName;
|
||||||
*
|
}
|
||||||
* @param pluginAnno type-level <code>Plugin</code> annotation
|
|
||||||
*/
|
public ConfigurationClass setBeanName(String id) {
|
||||||
public ConfigurationClass addPluginAnnotation(Annotation pluginAnno) {
|
this.beanName = id;
|
||||||
pluginAnnotations.add(pluginAnno);
|
return this;
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
public Set<ModelMethod> getMethods() {
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation[] getPluginAnnotations() {
|
||||||
|
return pluginAnnotations.toArray(new Annotation[pluginAnnotations.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a {@link Extension @Plugin}-annotated annotation to this configuration class.
|
||||||
|
*
|
||||||
|
* @param pluginAnno type-level <code>Plugin</code> annotation
|
||||||
|
*/
|
||||||
|
public ConfigurationClass addPluginAnnotation(Annotation pluginAnno) {
|
||||||
|
pluginAnnotations.add(pluginAnno);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public ConfigurationClass setDeclaringClass(ConfigurationClass configurationClass) {
|
public ConfigurationClass setDeclaringClass(ConfigurationClass configurationClass) {
|
||||||
this.declaringClass = configurationClass;
|
this.declaringClass = configurationClass;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationClass getDeclaringClass() {
|
public ConfigurationClass getDeclaringClass() {
|
||||||
return declaringClass;
|
return declaringClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getModifiers() {
|
public int getModifiers() {
|
||||||
return modifiers;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationClass setModifiers(int modifiers) {
|
public ConfigurationClass setModifiers(int modifiers) {
|
||||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
|
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
|
||||||
this.modifiers = modifiers;
|
this.modifiers = modifiers;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Configuration getMetadata() {
|
public Configuration getMetadata() {
|
||||||
return this.metadata;
|
return this.metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationClass setMetadata(Configuration configAnno) {
|
public ConfigurationClass setMetadata(Configuration configAnno) {
|
||||||
this.metadata = configAnno;
|
this.metadata = configAnno;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(List<UsageError> errors) {
|
public void validate(List<UsageError> errors) {
|
||||||
|
|
||||||
// configuration classes must be annotated with @Configuration
|
// configuration classes must be annotated with @Configuration
|
||||||
if (metadata == null)
|
if (metadata == null)
|
||||||
errors.add(new NonAnnotatedConfigurationError());
|
errors.add(new NonAnnotatedConfigurationError());
|
||||||
|
|
||||||
// a configuration class may not be final (CGLIB limitation)
|
// a configuration class may not be final (CGLIB limitation)
|
||||||
if (Modifier.isFinal(modifiers))
|
if (Modifier.isFinal(modifiers))
|
||||||
errors.add(new FinalConfigurationError());
|
errors.add(new FinalConfigurationError());
|
||||||
|
|
||||||
for(ModelMethod method : methods)
|
for (ModelMethod method : methods)
|
||||||
method.validate(errors);
|
method.validate(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@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
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = super.hashCode();
|
int result = super.hashCode();
|
||||||
result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
|
result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
|
||||||
result = prime * result + ((beanName == null) ? 0 : beanName.hashCode());
|
result = prime * result + ((beanName == null) ? 0 : beanName.hashCode());
|
||||||
result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
|
result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
|
||||||
result = prime * result + ((methods == null) ? 0 : methods.hashCode());
|
result = prime * result + ((methods == null) ? 0 : methods.hashCode());
|
||||||
result = prime * result + modifiers;
|
result = prime * result + modifiers;
|
||||||
result = prime * result + ((pluginAnnotations == null) ? 0 : pluginAnnotations.hashCode());
|
result = prime * result + ((pluginAnnotations == null) ? 0 : pluginAnnotations.hashCode());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj)
|
||||||
return true;
|
return true;
|
||||||
if (!super.equals(obj))
|
if (!super.equals(obj))
|
||||||
return false;
|
return false;
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass())
|
||||||
return false;
|
return false;
|
||||||
ConfigurationClass other = (ConfigurationClass) obj;
|
ConfigurationClass other = (ConfigurationClass) obj;
|
||||||
if (declaringClass == null) {
|
if (declaringClass == null) {
|
||||||
if (other.declaringClass != null)
|
if (other.declaringClass != null)
|
||||||
return false;
|
return false;
|
||||||
} else if (!declaringClass.equals(other.declaringClass))
|
} else if (!declaringClass.equals(other.declaringClass))
|
||||||
return false;
|
return false;
|
||||||
if (beanName == null) {
|
if (beanName == null) {
|
||||||
if (other.beanName != null)
|
if (other.beanName != null)
|
||||||
return false;
|
return false;
|
||||||
} else if (!beanName.equals(other.beanName))
|
} else if (!beanName.equals(other.beanName))
|
||||||
return false;
|
return false;
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
if (other.metadata != null)
|
if (other.metadata != null)
|
||||||
return false;
|
return false;
|
||||||
} else if (!metadata.equals(other.metadata))
|
} else if (!metadata.equals(other.metadata))
|
||||||
return false;
|
return false;
|
||||||
if (methods == null) {
|
if (methods == null) {
|
||||||
if (other.methods != null)
|
if (other.methods != null)
|
||||||
return false;
|
return false;
|
||||||
} else if (!methods.equals(other.methods))
|
} else if (!methods.equals(other.methods))
|
||||||
return false;
|
return false;
|
||||||
if (modifiers != other.modifiers)
|
if (modifiers != other.modifiers)
|
||||||
return false;
|
return false;
|
||||||
if (pluginAnnotations == null) {
|
if (pluginAnnotations == null) {
|
||||||
if (other.pluginAnnotations != null)
|
if (other.pluginAnnotations != null)
|
||||||
return false;
|
return false;
|
||||||
} else if (!pluginAnnotations.equals(other.pluginAnnotations))
|
} else if (!pluginAnnotations.equals(other.pluginAnnotations))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Configuration classes must be annotated with {@link Configuration @Configuration}. */
|
/** Configuration classes must be annotated with {@link Configuration @Configuration}. */
|
||||||
public class NonAnnotatedConfigurationError extends UsageError {
|
public class NonAnnotatedConfigurationError extends UsageError {
|
||||||
public NonAnnotatedConfigurationError() {
|
public NonAnnotatedConfigurationError() {
|
||||||
super(ConfigurationClass.this, -1);
|
super(ConfigurationClass.this, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configuration classes must be non-final to accommodate CGLIB subclassing. */
|
/** Configuration classes must be non-final to accommodate CGLIB subclassing. */
|
||||||
public class FinalConfigurationError extends UsageError {
|
public class FinalConfigurationError extends UsageError {
|
||||||
public FinalConfigurationError() {
|
public FinalConfigurationError() {
|
||||||
super(ConfigurationClass.this, -1);
|
super(ConfigurationClass.this, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return format("@%s class may not be final. Remove the final modifier to continue.",
|
return format("@%s class may not be final. Remove the final modifier to continue.",
|
||||||
Configuration.class.getSimpleName());
|
Configuration.class.getSimpleName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class InvalidPluginException extends UsageError {
|
public class InvalidPluginException extends UsageError {
|
||||||
|
|
||||||
private final Annotation invalidPluginAnnotation;
|
private final Annotation invalidPluginAnnotation;
|
||||||
|
|
||||||
public InvalidPluginException(Annotation invalidPluginAnnotation) {
|
public InvalidPluginException(Annotation invalidPluginAnnotation) {
|
||||||
super(ConfigurationClass.this, -1);
|
super(ConfigurationClass.this, -1);
|
||||||
this.invalidPluginAnnotation = invalidPluginAnnotation;
|
this.invalidPluginAnnotation = invalidPluginAnnotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return format("Annotation [%s] was not annotated with @Plugin", invalidPluginAnnotation);
|
return format("Annotation [%s] was not annotated with @Plugin", invalidPluginAnnotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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()
|
||||||
*/
|
*/
|
||||||
public class IllegalBeanOverrideError extends UsageError {
|
public class IllegalBeanOverrideError extends UsageError {
|
||||||
private final ConfigurationClass authoritativeClass;
|
private final ConfigurationClass authoritativeClass;
|
||||||
private final ModelMethod finalMethodInQuestion;
|
private final ModelMethod finalMethodInQuestion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return format("Illegal attempt by '%s' to override bean definition originally "
|
return format("Illegal attempt by '%s' to override bean definition originally "
|
||||||
+ "specified by %s.%s. Consider removing 'allowOverride=false' from original method.",
|
+ "specified by %s.%s. Consider removing 'allowOverride=false' from original method.",
|
||||||
finalMethodInQuestion.getName(), authoritativeClass.getSimpleName(),
|
finalMethodInQuestion.getName(), authoritativeClass.getSimpleName(),
|
||||||
finalMethodInQuestion.getName());
|
finalMethodInQuestion.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasMethod(String methodName) {
|
public boolean hasMethod(String methodName) {
|
||||||
return getMethod(methodName) != null;
|
return getMethod(methodName) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelMethod getMethod(String methodName) {
|
public ModelMethod getMethod(String methodName) {
|
||||||
|
|
||||||
for(ModelMethod method : methods)
|
for (ModelMethod method : methods)
|
||||||
if(methodName.equals(method.getName()))
|
if (methodName.equals(method.getName()))
|
||||||
return method;
|
return method;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,154 +21,157 @@ 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>
|
||||||
* @author Chris Beams
|
* Interface follows the builder pattern for method chaining.
|
||||||
* @see org.springframework.config.java.internal.parsing.ConfigurationParser
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @see org.springframework.config.java.internal.parsing.ConfigurationParser
|
||||||
*/
|
*/
|
||||||
public final class ConfigurationModel implements Validatable {
|
public final class ConfigurationModel implements Validatable {
|
||||||
|
|
||||||
/* list is used because order and collection equality matters. */
|
/* list is used because order and collection equality matters. */
|
||||||
private final ArrayList<ConfigurationClass> configurationClasses = new ArrayList<ConfigurationClass>();
|
private final ArrayList<ConfigurationClass> configurationClasses = new ArrayList<ConfigurationClass>();
|
||||||
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
|
||||||
*/
|
*/
|
||||||
public ConfigurationModel add(ConfigurationClass configurationClass) {
|
public ConfigurationModel add(ConfigurationClass configurationClass) {
|
||||||
configurationClasses.add(configurationClass);
|
configurationClasses.add(configurationClass);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerValidator(Validator validator) {
|
|
||||||
validators.add(validator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public void registerValidator(Validator validator) {
|
||||||
* Return configuration classes that have been directly added to this model.
|
validators.add(validator);
|
||||||
*
|
}
|
||||||
* @see #getAllConfigurationClasses()
|
|
||||||
*/
|
|
||||||
public ConfigurationClass[] getConfigurationClasses() {
|
|
||||||
return configurationClasses.toArray(new ConfigurationClass[] { });
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Return all configuration classes, including all imported configuration classes. This method
|
* Return configuration classes that have been directly added to this model.
|
||||||
// * should be generally preferred over {@link #getConfigurationClasses()}
|
*
|
||||||
// *
|
* @see #getAllConfigurationClasses()
|
||||||
// * @see #getConfigurationClasses()
|
*/
|
||||||
// */
|
public ConfigurationClass[] getConfigurationClasses() {
|
||||||
// public ConfigurationClass[] getAllConfigurationClasses() {
|
return configurationClasses.toArray(new ConfigurationClass[] {});
|
||||||
// ArrayList<ConfigurationClass> allConfigClasses = new ArrayList<ConfigurationClass>();
|
}
|
||||||
//
|
|
||||||
// for (ConfigurationClass configClass : configurationClasses)
|
|
||||||
// allConfigClasses.addAll(configClass.getSelfAndAllImports());
|
|
||||||
//
|
|
||||||
// return allConfigClasses.toArray(new ConfigurationClass[allConfigClasses.size()]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public ConfigurationClass[] getAllConfigurationClasses() {
|
|
||||||
return configurationClasses.toArray(new ConfigurationClass[configurationClasses.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Recurses through the model validating each object along the way and aggregating any <var>errors</var>.
|
// * Return all configuration classes, including all imported configuration classes.
|
||||||
*
|
// This method
|
||||||
* @see ConfigurationClass#validate(java.util.List)
|
// * should be generally preferred over {@link #getConfigurationClasses()}
|
||||||
* @see ModelMethod#validate(java.util.List)
|
// *
|
||||||
* @see Validator
|
// * @see #getConfigurationClasses()
|
||||||
* @see UsageError
|
// */
|
||||||
*/
|
// public ConfigurationClass[] getAllConfigurationClasses() {
|
||||||
public void validate(List<UsageError> errors) {
|
// ArrayList<ConfigurationClass> allConfigClasses = new ArrayList<ConfigurationClass>();
|
||||||
// user must specify at least one configuration
|
//
|
||||||
if (configurationClasses.isEmpty())
|
// for (ConfigurationClass configClass : configurationClasses)
|
||||||
errors.add(new EmptyModelError());
|
// allConfigClasses.addAll(configClass.getSelfAndAllImports());
|
||||||
|
//
|
||||||
// cascade through model and allow handlers to register validators
|
// return allConfigClasses.toArray(new ConfigurationClass[allConfigClasses.size()]);
|
||||||
// depending on where they are registered (with the model, the class, or the method)
|
// }
|
||||||
// they will be called directly or indirectly below
|
|
||||||
for (ConfigurationClass configClass : getAllConfigurationClasses()) {
|
|
||||||
for(ModelMethod method : configClass.getMethods()) {
|
|
||||||
for(Validator validator : method.getValidators()) {
|
|
||||||
if(validator.supports(method))
|
|
||||||
method.registerValidator(validator);
|
|
||||||
// TODO: support class-level validation
|
|
||||||
// if(validator.supports(configClass))
|
|
||||||
// configClass.registerValidator(validator);
|
|
||||||
if(validator.supports(this))
|
|
||||||
this.registerValidator(validator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// process any validators registered directly with this model object
|
|
||||||
for(Validator validator : validators)
|
|
||||||
validator.validate(this, errors);
|
|
||||||
|
|
||||||
// each individual configuration class must be well-formed
|
public ConfigurationClass[] getAllConfigurationClasses() {
|
||||||
// note that each configClass detects usage errors on its imports recursively
|
return configurationClasses.toArray(new ConfigurationClass[configurationClasses.size()]);
|
||||||
// note that each configClass will recursively process its respective methods
|
}
|
||||||
for (ConfigurationClass configClass : configurationClasses)
|
|
||||||
configClass.validate(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String toString() {
|
* Recurses through the model validating each object along the way and aggregating any
|
||||||
return format("%s: configurationClasses=%s",
|
* <var>errors</var>.
|
||||||
getClass().getSimpleName(), configurationClasses);
|
*
|
||||||
}
|
* @see ConfigurationClass#validate(java.util.List)
|
||||||
|
* @see ModelMethod#validate(java.util.List)
|
||||||
|
* @see Validator
|
||||||
|
* @see UsageError
|
||||||
|
*/
|
||||||
|
public void validate(List<UsageError> errors) {
|
||||||
|
// user must specify at least one configuration
|
||||||
|
if (configurationClasses.isEmpty())
|
||||||
|
errors.add(new EmptyModelError());
|
||||||
|
|
||||||
@Override
|
// cascade through model and allow handlers to register validators
|
||||||
public int hashCode() {
|
// depending on where they are registered (with the model, the class, or the method)
|
||||||
final int prime = 31;
|
// they will be called directly or indirectly below
|
||||||
int result = 1;
|
for (ConfigurationClass configClass : getAllConfigurationClasses()) {
|
||||||
result = prime * result + ((configurationClasses == null) ? 0 : configurationClasses.hashCode());
|
for (ModelMethod method : configClass.getMethods()) {
|
||||||
return result;
|
for (Validator validator : method.getValidators()) {
|
||||||
}
|
if (validator.supports(method))
|
||||||
|
method.registerValidator(validator);
|
||||||
|
// TODO: support class-level validation
|
||||||
|
// if(validator.supports(configClass))
|
||||||
|
// configClass.registerValidator(validator);
|
||||||
|
if (validator.supports(this))
|
||||||
|
this.registerValidator(validator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
// process any validators registered directly with this model object
|
||||||
public boolean equals(Object obj) {
|
for (Validator validator : validators)
|
||||||
if (this == obj)
|
validator.validate(this, errors);
|
||||||
return true;
|
|
||||||
if (obj == null)
|
// each individual configuration class must be well-formed
|
||||||
return false;
|
// note that each configClass detects usage errors on its imports recursively
|
||||||
if (getClass() != obj.getClass())
|
// note that each configClass will recursively process its respective methods
|
||||||
return false;
|
for (ConfigurationClass configClass : configurationClasses)
|
||||||
ConfigurationModel other = (ConfigurationModel) obj;
|
configClass.validate(errors);
|
||||||
if (configurationClasses == null) {
|
}
|
||||||
if (other.configurationClasses != null)
|
|
||||||
return false;
|
@Override
|
||||||
} else if (!configurationClasses.equals(other.configurationClasses))
|
public String toString() {
|
||||||
return false;
|
return format("%s: configurationClasses=%s", getClass().getSimpleName(), configurationClasses);
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((configurationClasses == null) ? 0 : configurationClasses.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
ConfigurationModel other = (ConfigurationModel) obj;
|
||||||
|
if (configurationClasses == null) {
|
||||||
|
if (other.configurationClasses != null)
|
||||||
|
return false;
|
||||||
|
} else if (!configurationClasses.equals(other.configurationClasses))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class EmptyModelError extends UsageError {
|
public class EmptyModelError extends UsageError {
|
||||||
public EmptyModelError() {
|
public EmptyModelError() {
|
||||||
super(null, 0);
|
super(null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return format("Configuration model was empty. Make sure at least one "
|
return format("Configuration model was empty. Make sure at least one "
|
||||||
+ "@%s class has been specified.", Configuration.class.getSimpleName());
|
+ "@%s class has been specified.", Configuration.class.getSimpleName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,10 @@ 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.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@ -34,22 +35,21 @@ import net.sf.cglib.proxy.NoOp;
|
||||||
@Documented
|
@Documented
|
||||||
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 {};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,54 +22,54 @@ import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: rename to UsageException / move outside .internal?
|
* TODO: rename to UsageException / move outside .internal?
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class MalformedConfigurationException extends RuntimeException {
|
public class MalformedConfigurationException extends RuntimeException {
|
||||||
|
|
||||||
private final List<? extends UsageError> errors;
|
private final List<? extends UsageError> errors;
|
||||||
|
|
||||||
public MalformedConfigurationException(String message) {
|
public MalformedConfigurationException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.errors = new ArrayList<UsageError>();
|
this.errors = new ArrayList<UsageError>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MalformedConfigurationException(UsageError... errors) {
|
public MalformedConfigurationException(UsageError... errors) {
|
||||||
super(toString(errors));
|
super(toString(errors));
|
||||||
this.errors = Arrays.asList(errors);
|
this.errors = Arrays.asList(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsError(Class<? extends UsageError> errorType) {
|
public boolean containsError(Class<? extends UsageError> errorType) {
|
||||||
for (UsageError error : errors)
|
for (UsageError error : errors)
|
||||||
if (error.getClass().isAssignableFrom(errorType))
|
if (error.getClass().isAssignableFrom(errorType))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a list of syntax errors as output suitable for diagnosis via System.err.
|
* Render a list of syntax errors as output suitable for diagnosis via System.err.
|
||||||
*/
|
*/
|
||||||
private static String toString(UsageError... errors) {
|
private static String toString(UsageError... errors) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
|
|
||||||
if (errors.length == 1)
|
if (errors.length == 1)
|
||||||
sb.append("A usage error has ");
|
sb.append("A usage error has ");
|
||||||
else
|
else
|
||||||
sb.append(errors.length + " usage errors have ");
|
sb.append(errors.length + " usage errors have ");
|
||||||
|
|
||||||
sb.append("been detected:\n");
|
sb.append("been detected:\n");
|
||||||
|
|
||||||
for (int i = 0; i < errors.length; i++) {
|
for (int i = 0; i < errors.length; i++) {
|
||||||
sb.append(errors[i].toString());
|
sb.append(errors[i].toString());
|
||||||
if ((i + 1) < errors.length)
|
if ((i + 1) < errors.length)
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,139 +21,141 @@ 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
|
||||||
*/
|
*/
|
||||||
// TODO: Consider eliminating in favor of just ConfigurationClass
|
// TODO: Consider eliminating in favor of just ConfigurationClass
|
||||||
public class ModelClass implements BeanMetadataElement {
|
public class ModelClass implements BeanMetadataElement {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private boolean isInterface;
|
private boolean isInterface;
|
||||||
private String source;
|
private String source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
*
|
*
|
||||||
* @param name fully-qualified name of the class being represented
|
* @param name fully-qualified name of the class being represented
|
||||||
*/
|
*/
|
||||||
public ModelClass(String name) {
|
public ModelClass(String name) {
|
||||||
this(name, false);
|
this(name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new ModelClass instance
|
* Creates a new ModelClass instance
|
||||||
*
|
*
|
||||||
* @param name fully-qualified name of the class being represented
|
* @param name fully-qualified name of the class being represented
|
||||||
* @param isInterface whether the represented type is an interface
|
* @param isInterface whether the represented type is an interface
|
||||||
*/
|
*/
|
||||||
public ModelClass(String name, boolean isInterface) {
|
public ModelClass(String name, boolean isInterface) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.isInterface = isInterface;
|
this.isInterface = isInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the fully-qualified name of this class.
|
* Returns the fully-qualified name of this class.
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the fully-qualified name of this class.
|
* Sets the fully-qualified name of this class.
|
||||||
*/
|
*/
|
||||||
public void setName(String className) {
|
public void setName(String className) {
|
||||||
this.name = className;
|
this.name = className;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'.
|
* Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'.
|
||||||
*/
|
*/
|
||||||
public String getSimpleName() {
|
public String getSimpleName() {
|
||||||
return name == null ? null : ClassUtils.getShortName(name);
|
return name == null ? null : ClassUtils.getShortName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the class represented by this ModelClass instance is an interface.
|
* Returns whether the class represented by this ModelClass instance is an interface.
|
||||||
*/
|
*/
|
||||||
public boolean isInterface() {
|
public boolean isInterface() {
|
||||||
return isInterface;
|
return isInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signifies that this class is (true) or is not (false) an interface.
|
* Signifies that this class is (true) or is not (false) an interface.
|
||||||
*/
|
*/
|
||||||
public void setInterface(boolean isInterface) {
|
public void setInterface(boolean isInterface) {
|
||||||
this.isInterface = isInterface;
|
this.isInterface = isInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the source location for this class. Must be a resource-path formatted string.
|
* Set the source location for this class. Must be a resource-path formatted string.
|
||||||
*
|
*
|
||||||
* @param source resource path to the .java file that declares this class.
|
* @param source resource path to the .java file that declares this class.
|
||||||
*/
|
*/
|
||||||
public void setSource(Object source) {
|
public void setSource(Object source) {
|
||||||
Assert.isInstanceOf(String.class, source);
|
Assert.isInstanceOf(String.class, source);
|
||||||
this.source = (String) source;
|
this.source = (String) source;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* <pre>
|
* return
|
||||||
* ModelClass: name=Foo
|
*
|
||||||
* </pre>
|
* <pre>
|
||||||
*/
|
* ModelClass: name=Foo
|
||||||
@Override
|
* </pre>
|
||||||
public String toString() {
|
*/
|
||||||
return String.format("%s: name=%s", getClass().getSimpleName(), getSimpleName());
|
@Override
|
||||||
}
|
public String toString() {
|
||||||
|
return String.format("%s: name=%s", getClass().getSimpleName(), getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = (prime * result) + (isInterface ? 1231 : 1237);
|
result = (prime * result) + (isInterface ? 1231 : 1237);
|
||||||
result = (prime * result) + ((name == null) ? 0 : name.hashCode());
|
result = (prime * result) + ((name == null) ? 0 : name.hashCode());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ModelClass other = (ModelClass) obj;
|
ModelClass other = (ModelClass) obj;
|
||||||
if (isInterface != other.isInterface)
|
if (isInterface != other.isInterface)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
if (other.name != null)
|
if (other.name != null)
|
||||||
return false;
|
return false;
|
||||||
} else if (!name.equals(other.name))
|
} else if (!name.equals(other.name))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,207 +34,206 @@ import org.springframework.util.Assert;
|
||||||
/** TODO: JAVADOC */
|
/** TODO: JAVADOC */
|
||||||
public final class ModelMethod implements Validatable {
|
public final class ModelMethod implements Validatable {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final int modifiers;
|
private final int modifiers;
|
||||||
private final ModelClass returnType;
|
private final ModelClass returnType;
|
||||||
private final List<Annotation> annotations = new ArrayList<Annotation>();
|
private final List<Annotation> annotations = new ArrayList<Annotation>();
|
||||||
private transient ConfigurationClass declaringClass;
|
private transient ConfigurationClass declaringClass;
|
||||||
private transient int lineNumber;
|
private transient int lineNumber;
|
||||||
private transient Factory factoryAnno;
|
private transient Factory factoryAnno;
|
||||||
private transient final List<Validator> validators = new ArrayList<Validator>();
|
private transient final List<Validator> validators = new ArrayList<Validator>();
|
||||||
|
|
||||||
public ModelMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
|
public ModelMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
|
||||||
Assert.hasText(name);
|
Assert.hasText(name);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
Assert.notNull(annotations);
|
Assert.notNull(annotations);
|
||||||
for(Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
this.annotations.add(annotation);
|
this.annotations.add(annotation);
|
||||||
if(factoryAnno == null)
|
if (factoryAnno == null)
|
||||||
factoryAnno = annotation.annotationType().getAnnotation(Factory.class);
|
factoryAnno = annotation.annotationType().getAnnotation(Factory.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers);
|
|
||||||
this.modifiers = modifiers;
|
|
||||||
|
|
||||||
Assert.notNull(returnType);
|
|
||||||
this.returnType = returnType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers);
|
||||||
return name;
|
this.modifiers = modifiers;
|
||||||
}
|
|
||||||
|
|
||||||
public ModelClass getReturnType() {
|
|
||||||
return returnType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
Assert.notNull(returnType);
|
||||||
* @see java.lang.reflect.Modifier
|
this.returnType = returnType;
|
||||||
*/
|
}
|
||||||
public int getModifiers() {
|
|
||||||
return modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public String getName() {
|
||||||
* Returns the annotation on this method matching <var>annoType</var> or null
|
return name;
|
||||||
* IllegalStateException} if not present.
|
}
|
||||||
*
|
|
||||||
* @see #getRequiredAnnotation(Class)
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T extends Annotation> T getAnnotation(Class<T> annoType) {
|
|
||||||
for(Annotation anno : annotations)
|
|
||||||
if(anno.annotationType().equals(annoType))
|
|
||||||
return (T) anno;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public ModelClass getReturnType() {
|
||||||
* Returns the annotation on this method matching <var>annoType</var> or throws
|
return returnType;
|
||||||
* {@link IllegalStateException} if not present.
|
}
|
||||||
*
|
|
||||||
* @see #getAnnotation(Class)
|
|
||||||
*/
|
|
||||||
public <T extends Annotation> T getRequiredAnnotation(Class<T> annoType) {
|
|
||||||
T anno = getAnnotation(annoType);
|
|
||||||
|
|
||||||
if(anno == null)
|
|
||||||
throw new IllegalStateException(
|
|
||||||
format("annotation %s not found on %s", annoType.getSimpleName(), this));
|
|
||||||
|
|
||||||
return anno;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up bi-directional relationship between this method and its declaring class.
|
* @see java.lang.reflect.Modifier
|
||||||
*
|
*/
|
||||||
* @see ConfigurationClass#addMethod(ModelMethod)
|
public int getModifiers() {
|
||||||
*/
|
return modifiers;
|
||||||
public void setDeclaringClass(ConfigurationClass declaringClass) {
|
}
|
||||||
this.declaringClass = declaringClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigurationClass getDeclaringClass() {
|
/**
|
||||||
return declaringClass;
|
* Returns the annotation on this method matching <var>annoType</var> or null
|
||||||
}
|
* IllegalStateException} if not present.
|
||||||
|
*
|
||||||
|
* @see #getRequiredAnnotation(Class)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Annotation> T getAnnotation(Class<T> annoType) {
|
||||||
|
for (Annotation anno : annotations)
|
||||||
|
if (anno.annotationType().equals(annoType))
|
||||||
|
return (T) anno;
|
||||||
|
|
||||||
public void setLineNumber(int lineNumber) {
|
return null;
|
||||||
this.lineNumber = lineNumber;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public int getLineNumber() {
|
/**
|
||||||
return lineNumber;
|
* Returns the annotation on this method matching <var>annoType</var> or throws
|
||||||
}
|
* {@link IllegalStateException} if not present.
|
||||||
|
*
|
||||||
public void registerValidator(Validator validator) {
|
* @see #getAnnotation(Class)
|
||||||
validators.add(validator);
|
*/
|
||||||
}
|
public <T extends Annotation> T getRequiredAnnotation(Class<T> annoType) {
|
||||||
|
T anno = getAnnotation(annoType);
|
||||||
public void validate(List<UsageError> errors) {
|
|
||||||
for(Validator validator : validators)
|
|
||||||
validator.validate(this, errors);
|
|
||||||
|
|
||||||
if (Modifier.isPrivate(getModifiers()))
|
|
||||||
errors.add(new PrivateMethodError());
|
|
||||||
|
|
||||||
if (Modifier.isFinal(getModifiers()))
|
if (anno == null)
|
||||||
errors.add(new FinalMethodError());
|
throw new IllegalStateException(format("annotation %s not found on %s", annoType.getSimpleName(),
|
||||||
}
|
this));
|
||||||
|
|
||||||
public BeanDefinitionRegistrar getRegistrar() {
|
return anno;
|
||||||
return getInstance(factoryAnno.registrarType());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Validator> getValidators() {
|
/**
|
||||||
HashSet<Validator> validator = new HashSet<Validator>();
|
* Sets up bi-directional relationship between this method and its declaring class.
|
||||||
|
*
|
||||||
for(Class<? extends Validator> validatorType : factoryAnno.validatorTypes())
|
* @see ConfigurationClass#addMethod(ModelMethod)
|
||||||
validator.add(getInstance(validatorType));
|
*/
|
||||||
|
public void setDeclaringClass(ConfigurationClass declaringClass) {
|
||||||
return validator;
|
this.declaringClass = declaringClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Callback getCallback() {
|
public ConfigurationClass getDeclaringClass() {
|
||||||
Class<? extends Callback> callbackType = factoryAnno.callbackType();
|
return declaringClass;
|
||||||
|
}
|
||||||
if(callbackType.equals(NoOp.class))
|
|
||||||
return NoOp.INSTANCE;
|
|
||||||
|
|
||||||
return getInstance(callbackType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public void setLineNumber(int lineNumber) {
|
||||||
public String toString() {
|
this.lineNumber = lineNumber;
|
||||||
String returnTypeName = returnType == null ? "<unknown>" : returnType.getSimpleName();
|
}
|
||||||
return String.format("%s: name=%s; returnType=%s; modifiers=%d",
|
|
||||||
getClass().getSimpleName(), name, returnTypeName, modifiers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public int getLineNumber() {
|
||||||
public int hashCode() {
|
return lineNumber;
|
||||||
final int prime = 31;
|
}
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ((annotations == null) ? 0 : annotations.hashCode());
|
|
||||||
result = prime * result + modifiers;
|
|
||||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
|
||||||
result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public void registerValidator(Validator validator) {
|
||||||
public boolean equals(Object obj) {
|
validators.add(validator);
|
||||||
if (this == obj)
|
}
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
ModelMethod other = (ModelMethod) obj;
|
|
||||||
if (annotations == null) {
|
|
||||||
if (other.annotations != null)
|
|
||||||
return false;
|
|
||||||
} else if (!annotations.equals(other.annotations))
|
|
||||||
return false;
|
|
||||||
if (modifiers != other.modifiers)
|
|
||||||
return false;
|
|
||||||
if (name == null) {
|
|
||||||
if (other.name != null)
|
|
||||||
return false;
|
|
||||||
} else if (!name.equals(other.name))
|
|
||||||
return false;
|
|
||||||
if (returnType == null) {
|
|
||||||
if (other.returnType != null)
|
|
||||||
return false;
|
|
||||||
} else if (!returnType.equals(other.returnType))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** JavaConfigMethods must be visible (non-private) in order to accommodate CGLIB. */
|
public void validate(List<UsageError> errors) {
|
||||||
public class PrivateMethodError extends UsageError {
|
for (Validator validator : validators)
|
||||||
public PrivateMethodError() {
|
validator.validate(this, errors);
|
||||||
super(getDeclaringClass(), getLineNumber());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (Modifier.isPrivate(getModifiers()))
|
||||||
public String getDescription() {
|
errors.add(new PrivateMethodError());
|
||||||
return format("method '%s' may not be private", getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** JavaConfigMethods must be extensible (non-final) in order to accommodate CGLIB. */
|
if (Modifier.isFinal(getModifiers()))
|
||||||
public class FinalMethodError extends UsageError {
|
errors.add(new FinalMethodError());
|
||||||
public FinalMethodError() {
|
}
|
||||||
super(getDeclaringClass(), getLineNumber());
|
|
||||||
}
|
public BeanDefinitionRegistrar getRegistrar() {
|
||||||
|
return getInstance(factoryAnno.registrarType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Validator> getValidators() {
|
||||||
|
HashSet<Validator> validator = new HashSet<Validator>();
|
||||||
|
|
||||||
|
for (Class<? extends Validator> validatorType : factoryAnno.validatorTypes())
|
||||||
|
validator.add(getInstance(validatorType));
|
||||||
|
|
||||||
|
return validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback getCallback() {
|
||||||
|
Class<? extends Callback> callbackType = factoryAnno.callbackType();
|
||||||
|
|
||||||
|
if (callbackType.equals(NoOp.class))
|
||||||
|
return NoOp.INSTANCE;
|
||||||
|
|
||||||
|
return getInstance(callbackType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String returnTypeName = returnType == null ? "<unknown>" : returnType.getSimpleName();
|
||||||
|
return String.format("%s: name=%s; returnType=%s; modifiers=%d", getClass().getSimpleName(), name,
|
||||||
|
returnTypeName, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((annotations == null) ? 0 : annotations.hashCode());
|
||||||
|
result = prime * result + modifiers;
|
||||||
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
|
result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
ModelMethod other = (ModelMethod) obj;
|
||||||
|
if (annotations == null) {
|
||||||
|
if (other.annotations != null)
|
||||||
|
return false;
|
||||||
|
} else if (!annotations.equals(other.annotations))
|
||||||
|
return false;
|
||||||
|
if (modifiers != other.modifiers)
|
||||||
|
return false;
|
||||||
|
if (name == null) {
|
||||||
|
if (other.name != null)
|
||||||
|
return false;
|
||||||
|
} else if (!name.equals(other.name))
|
||||||
|
return false;
|
||||||
|
if (returnType == null) {
|
||||||
|
if (other.returnType != null)
|
||||||
|
return false;
|
||||||
|
} else if (!returnType.equals(other.returnType))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** JavaConfigMethods must be visible (non-private) in order to accommodate CGLIB. */
|
||||||
|
public class PrivateMethodError extends UsageError {
|
||||||
|
public PrivateMethodError() {
|
||||||
|
super(getDeclaringClass(), getLineNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return format("method '%s' may not be private", getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** JavaConfigMethods must be extensible (non-final) in order to accommodate CGLIB. */
|
||||||
|
public class FinalMethodError extends UsageError {
|
||||||
|
public FinalMethodError() {
|
||||||
|
super(getDeclaringClass(), getLineNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return format("method '%s' may not be final - remove the final modifier to continue", getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return format("method '%s' may not be final - remove the final modifier to continue",
|
|
||||||
getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() { }
|
|
||||||
|
|
||||||
public static final String SINGLETON = "singleton"; // see BeanDefinition.SCOPE_SINGLETON;
|
private Scopes() {
|
||||||
|
}
|
||||||
|
|
||||||
public static final String PROTOTYPE = "prototype"; // see BeanDefinition.SCOPE_PROTOTYPE;
|
public static final String SINGLETON = "singleton";
|
||||||
|
|
||||||
public static final String REQUEST = "request"; // see WebApplicationContext.SCOPE_REQUEST;
|
public static final String PROTOTYPE = "prototype";
|
||||||
|
|
||||||
public static final String SESSION = "session"; // see WebApplicationContext.SCOPE_SESSION;
|
public static final String REQUEST = "request";
|
||||||
|
|
||||||
|
public static final String SESSION = "session";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,57 +16,58 @@
|
||||||
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
|
||||||
*/
|
*/
|
||||||
public abstract class UsageError {
|
public abstract class UsageError {
|
||||||
|
|
||||||
private final ModelClass clazz;
|
private final ModelClass clazz;
|
||||||
private final int lineNumber;
|
private final int lineNumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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()
|
||||||
public UsageError(ModelClass modelClass, int lineNumber) {
|
*/
|
||||||
this.clazz = modelClass;
|
public UsageError(ModelClass modelClass, int lineNumber) {
|
||||||
this.lineNumber = lineNumber;
|
this.clazz = modelClass;
|
||||||
}
|
this.lineNumber = lineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Human-readable description of this error suitable for console output or IDE tooling.
|
* Human-readable description of this error suitable for console output or IDE tooling.
|
||||||
*/
|
*/
|
||||||
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() {
|
*/
|
||||||
if (clazz == null)
|
public final String getAttributedDescription() {
|
||||||
return getDescription();
|
if (clazz == null)
|
||||||
|
return getDescription();
|
||||||
|
|
||||||
return String.format("%s:%d: %s", clazz.getSource(), lineNumber, getDescription());
|
return String.format("%s:%d: %s", clazz.getSource(), lineNumber, getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegates directly to {@link #getAttributedDescription()}.
|
* Delegates directly to {@link #getAttributedDescription()}.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getAttributedDescription();
|
return getAttributedDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,10 @@ import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import sun.security.x509.Extension;
|
import sun.security.x509.Extension;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Misc utils
|
* Misc utils
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
// TODO: SJC-242 general - check cycles with s101
|
// TODO: SJC-242 general - check cycles with s101
|
||||||
|
@ -25,140 +26,142 @@ import sun.security.x509.Extension;
|
||||||
// TODO: SJC-242 rename, repackage, document
|
// TODO: SJC-242 rename, repackage, document
|
||||||
public class Util {
|
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.
|
*/
|
||||||
*/
|
public static <T> T getInstance(Class<? extends T> clazz) {
|
||||||
public static <T> T getInstance(Class<? extends T> clazz) {
|
try {
|
||||||
try {
|
Constructor<? extends T> noArgCtor = clazz.getDeclaredConstructor();
|
||||||
Constructor<? extends T> noArgCtor = clazz.getDeclaredConstructor();
|
ReflectionUtils.makeAccessible(noArgCtor);
|
||||||
ReflectionUtils.makeAccessible(noArgCtor);
|
return noArgCtor.newInstance();
|
||||||
return noArgCtor.newInstance();
|
} catch (Exception ex) {
|
||||||
} catch (Exception ex) {
|
ReflectionUtils.handleReflectionException(ex);
|
||||||
ReflectionUtils.handleReflectionException(ex);
|
throw new IllegalStateException(format("Unexpected reflection exception - %s: %s", ex.getClass()
|
||||||
throw new IllegalStateException(
|
.getName(), ex.getMessage()));
|
||||||
format("Unexpected reflection exception - %s: %s",
|
}
|
||||||
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 fqClassName fully-qualified class name
|
* @param <T> type of class to be returned
|
||||||
*
|
* @param fqClassName fully-qualified class name
|
||||||
* @return newly loaded class instance, null if class could not be found
|
*
|
||||||
*
|
* @return newly loaded class instance, null if class could not be found
|
||||||
* @see #loadRequiredClass(String)
|
*
|
||||||
* @see #loadToolingSafeClass(String)
|
* @see #loadRequiredClass(String)
|
||||||
* @see ClassUtils#getDefaultClassLoader()
|
* @see #loadToolingSafeClass(String)
|
||||||
*/
|
* @see ClassUtils#getDefaultClassLoader()
|
||||||
@SuppressWarnings("unchecked")
|
*/
|
||||||
public static <T> Class<? extends T> loadClass(String fqClassName) {
|
@SuppressWarnings("unchecked")
|
||||||
try {
|
public static <T> Class<? extends T> loadClass(String fqClassName) {
|
||||||
return (Class<? extends T>) ClassUtils.getDefaultClassLoader().loadClass(fqClassName);
|
try {
|
||||||
} catch (ClassNotFoundException ex) {
|
return (Class<? extends T>) ClassUtils.getDefaultClassLoader().loadClass(fqClassName);
|
||||||
return null;
|
} catch (ClassNotFoundException ex) {
|
||||||
}
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the specified class using the default class loader, rethrowing any
|
* Loads the specified class using the default class loader, rethrowing any
|
||||||
* {@link ClassNotFoundException} as an unchecked exception.
|
* {@link ClassNotFoundException} as an unchecked exception.
|
||||||
*
|
*
|
||||||
* @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
|
||||||
*
|
*
|
||||||
* @return newly loaded class instance
|
* @return newly loaded class instance
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if configClassName cannot be loaded.
|
* @throws IllegalArgumentException if configClassName cannot be loaded.
|
||||||
*
|
*
|
||||||
* @see #loadClass(String)
|
* @see #loadClass(String)
|
||||||
* @see #loadToolingSafeClass(String)
|
* @see #loadToolingSafeClass(String)
|
||||||
* @see ClassUtils#getDefaultClassLoader()
|
* @see ClassUtils#getDefaultClassLoader()
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> Class<? extends T> loadRequiredClass(String fqClassName) {
|
public static <T> Class<? extends T> loadRequiredClass(String fqClassName) {
|
||||||
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
|
||||||
* <p>
|
* available to the tooling.
|
||||||
* ASM class reading is used throughout JavaConfig, but there are certain cases where
|
* <p>
|
||||||
* classloading cannot be avoided - specifically in cases where users define their own
|
* ASM class reading is used throughout JavaConfig, but there are certain cases where
|
||||||
* {@link Extension} or {@link Factory} annotations. This method should therefore be used sparingly
|
* classloading cannot be avoided - specifically in cases where users define their own
|
||||||
* but consistently where required.
|
* {@link Extension} or {@link Factory} annotations. This method should therefore be
|
||||||
* <p>
|
* used sparingly but consistently where required.
|
||||||
* Because {@link ClassNotFoundException} is compensated for by returning null, callers must
|
* <p>
|
||||||
* take care to handle the null case appropriately.
|
* Because {@link ClassNotFoundException} is compensated for by returning null, callers
|
||||||
* <p>
|
* must take care to handle the null case appropriately.
|
||||||
* In cases where the WARN logging statement is not desired, use the {@link #loadClass(String)}
|
* <p>
|
||||||
* method, which returns null but issues no logging statements.
|
* In cases where the WARN logging statement is not desired, use the
|
||||||
* <p>
|
* {@link #loadClass(String)} method, which returns null but issues no logging
|
||||||
* This method should only ever return null in the case of a user-defined type be processed at
|
* statements.
|
||||||
* tooling time. Therefore, tooling may not be able to represent any custom annotation
|
* <p>
|
||||||
* semantics, but JavaConfig itself will not have any problem loading and respecting them at
|
* This method should only ever return null in the case of a user-defined type be
|
||||||
* actual runtime.
|
* processed at tooling time. Therefore, tooling may not be able to represent any custom
|
||||||
*
|
* annotation semantics, but JavaConfig itself will not have any problem loading and
|
||||||
* @param <T> type of class to be returned
|
* respecting them at actual runtime.
|
||||||
* @param fqClassName fully-qualified class name
|
*
|
||||||
*
|
* @param <T> type of class to be returned
|
||||||
* @return newly loaded class, null if class could not be found.
|
* @param fqClassName fully-qualified class name
|
||||||
*
|
*
|
||||||
* @see #loadClass(String)
|
* @return newly loaded class, null if class could not be found.
|
||||||
* @see #loadRequiredClass(String)
|
*
|
||||||
* @see ClassUtils#getDefaultClassLoader()
|
* @see #loadClass(String)
|
||||||
*/
|
* @see #loadRequiredClass(String)
|
||||||
@SuppressWarnings("unchecked")
|
* @see ClassUtils#getDefaultClassLoader()
|
||||||
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName) {
|
*/
|
||||||
try {
|
@SuppressWarnings("unchecked")
|
||||||
return (Class<? extends T>) ClassUtils.getDefaultClassLoader().loadClass(fqClassName);
|
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName) {
|
||||||
} catch (ClassNotFoundException ex) {
|
try {
|
||||||
log.warn(format("Unable to load class [%s], likely due to tooling-specific restrictions."
|
return (Class<? extends T>) ClassUtils.getDefaultClassLoader().loadClass(fqClassName);
|
||||||
+ "Attempting to continue, but unexpected errors may occur", fqClassName), ex);
|
} catch (ClassNotFoundException ex) {
|
||||||
return null;
|
log.warn(format("Unable to load class [%s], likely due to tooling-specific restrictions."
|
||||||
}
|
+ "Attempting to continue, but unexpected errors may occur", fqClassName), ex);
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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>
|
||||||
*
|
*
|
||||||
* @throws RuntimeException if <var>pathToClass</var> does not exist
|
* @throws RuntimeException if <var>pathToClass</var> does not exist
|
||||||
*/
|
*/
|
||||||
public static InputStream getClassAsStream(String pathToClass) {
|
public static InputStream getClassAsStream(String pathToClass) {
|
||||||
String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX;
|
String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX;
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,19 @@ 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.
|
||||||
*
|
*
|
||||||
* @see Validator
|
* @see Validator
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
public interface Validatable {
|
public interface Validatable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates this object, adding any errors to the supplied list of <var>errors</var>.
|
* Validates this object, adding any errors to the supplied list of <var>errors</var>.
|
||||||
*/
|
*/
|
||||||
public void validate(List<UsageError> errors);
|
public void validate(List<UsageError> errors);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
/** Marker interface */
|
/** Marker interface */
|
||||||
//TODO: SJC-242 document
|
// TODO: SJC-242 document
|
||||||
//TODO: SJC-242 rename
|
// TODO: SJC-242 rename
|
||||||
public interface Validator {
|
public interface Validator {
|
||||||
boolean supports(Object object);
|
boolean supports(Object object);
|
||||||
|
|
||||||
void validate(Object object, List<UsageError> errors);
|
void validate(Object object, List<UsageError> errors);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,19 +30,19 @@ import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all {@link MethodInterceptor} implementations.
|
* Base class for all {@link MethodInterceptor} implementations.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractMethodInterceptor implements BeanFactoryAware, MethodInterceptor {
|
public abstract class AbstractMethodInterceptor implements BeanFactoryAware, MethodInterceptor {
|
||||||
protected final Log log = LogFactory.getLog(this.getClass());
|
protected final Log log = LogFactory.getLog(this.getClass());
|
||||||
protected DefaultListableBeanFactory beanFactory;
|
protected DefaultListableBeanFactory beanFactory;
|
||||||
|
|
||||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||||
Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
|
Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
|
||||||
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
|
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getBeanName(Method method) {
|
protected String getBeanName(Method method) {
|
||||||
return method.getName();
|
return method.getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,181 +37,184 @@ 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
|
||||||
|
* methods may throw any exception, which will be caught and handled by the Spring container
|
||||||
|
* on processing of the configuration class.<br>
|
||||||
|
* Bean creation methods must return an object type. The decision to return a class or an
|
||||||
|
* interface will be significant in the event of proxying. Bean methods that return
|
||||||
|
* interfaces will be proxied using dynamic proxies; those that return a class will require
|
||||||
|
* 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 follows. This ensures that references between beans are strongly typed:
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* <p>Bean creation methods must be non-private (default, public or protected). Bean creation
|
|
||||||
* methods may throw any exception, which will be caught and handled by the Spring container on
|
|
||||||
* processing of the configuration class.<br>
|
|
||||||
* Bean creation methods must return an object type. The decision to return a class or an interface
|
|
||||||
* will be significant in the event of proxying. Bean methods that return interfaces will be proxied
|
|
||||||
* using dynamic proxies; those that return a class will require 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
|
|
||||||
* follows. This ensures that references between beans are strongly typed:</p>
|
|
||||||
*
|
|
||||||
* @see Configuration
|
* @see Configuration
|
||||||
* @see BeanNamingStrategy
|
* @see BeanNamingStrategy
|
||||||
*
|
*
|
||||||
* @author Rod Johnson
|
* @author Rod Johnson
|
||||||
* @author Costin Leau
|
* @author Costin Leau
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@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 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Role this bean plays in the overall application configuration.
|
* Role this bean plays in the overall application configuration.
|
||||||
*
|
*
|
||||||
* @see BeanDefinition#ROLE_APPLICATION
|
* @see BeanDefinition#ROLE_APPLICATION
|
||||||
* @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;
|
||||||
/**
|
|
||||||
* Bean aliases.
|
|
||||||
*/
|
|
||||||
String[] aliases() default { };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scope: whether the bean is a singleton, prototype or custom scope.
|
* Bean aliases.
|
||||||
* Default is singleton.
|
*/
|
||||||
*/
|
String[] aliases() default {};
|
||||||
String scope() default Scopes.SINGLETON;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean autowire strategy.
|
* Scope: whether the bean is a singleton, prototype or custom scope. Default is
|
||||||
*/
|
* singleton.
|
||||||
Autowire autowire() default Autowire.INHERITED;
|
*/
|
||||||
|
String scope() default Scopes.SINGLETON;
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Bean lazy strategy.
|
* Bean autowire strategy.
|
||||||
// */
|
*/
|
||||||
// Lazy lazy() default Lazy.UNSPECIFIED;
|
Autowire autowire() default Autowire.INHERITED;
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * A bean may be marked as primary, useful for disambiguation when looking
|
|
||||||
// * up beans by type.
|
|
||||||
// *
|
|
||||||
// * @see org.springframework.config.java.context.JavaConfigApplicationContext#getBean(Class);
|
|
||||||
// */
|
|
||||||
// Primary primary() default Primary.UNSPECIFIED;
|
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Bean init method name. Normally this is not needed, as the initialization
|
// * Bean lazy strategy.
|
||||||
* (with parameterization) can be done directly through java code.
|
// */
|
||||||
*/
|
// Lazy lazy() default Lazy.UNSPECIFIED;
|
||||||
String initMethodName() default "";
|
//
|
||||||
|
// /**
|
||||||
|
// * A bean may be marked as primary, useful for disambiguation when looking
|
||||||
|
// * up beans by type.
|
||||||
|
// *
|
||||||
|
// * @see
|
||||||
|
// org.springframework.config.java.context.JavaConfigApplicationContext#getBean(Class);
|
||||||
|
// */
|
||||||
|
// Primary primary() default Primary.UNSPECIFIED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean destroy method name.
|
* Bean init method name. Normally this is not needed, as the initialization (with
|
||||||
*/
|
* parameterization) can be done directly through java code.
|
||||||
String destroyMethodName() default "";
|
*/
|
||||||
|
String initMethodName() default "";
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Bean dependency check strategy.
|
* Bean destroy method name.
|
||||||
// */
|
*/
|
||||||
// DependencyCheck dependencyCheck() default DependencyCheck.UNSPECIFIED;
|
String destroyMethodName() default "";
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Beans on which the current bean depends on.
|
// * Bean dependency check strategy.
|
||||||
*/
|
// */
|
||||||
String[] dependsOn() default { };
|
// DependencyCheck dependencyCheck() default DependencyCheck.UNSPECIFIED;
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Metadata for the current bean.
|
* Beans on which the current bean depends on.
|
||||||
// */
|
*/
|
||||||
// Meta[] meta() default { };
|
String[] dependsOn() default {};
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Allow the bean to be overridden in another JavaConfig, XML or other
|
// * Metadata for the current bean.
|
||||||
* non-Java configuration. This is consistent with
|
// */
|
||||||
* DefaultListableBeanFactory's allowBeanDefinitionOverriding property,
|
// Meta[] meta() default { };
|
||||||
* which defaults to true.
|
|
||||||
*
|
/**
|
||||||
* @return whether overriding of this bean is allowed
|
* Allow the bean to be overridden in another JavaConfig, XML or other non-Java
|
||||||
*/
|
* configuration. This is consistent with DefaultListableBeanFactory's
|
||||||
boolean allowOverriding() default true;
|
* allowBeanDefinitionOverriding property, which defaults to true.
|
||||||
|
*
|
||||||
|
* @return whether overriding of this bean is allowed
|
||||||
|
*/
|
||||||
|
boolean allowOverriding() default true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detects any user errors when declaring {@link Bean}-annotated methods.
|
* Detects any user errors when declaring {@link Bean}-annotated methods.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
class BeanValidator implements Validator {
|
class BeanValidator implements Validator {
|
||||||
|
|
||||||
public boolean supports(Object object) {
|
public boolean supports(Object object) {
|
||||||
return object instanceof ModelMethod;
|
return object instanceof ModelMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validate(Object object, List<UsageError> errors) {
|
||||||
|
ModelMethod method = (ModelMethod) object;
|
||||||
|
|
||||||
|
// TODO: re-enable for @ScopedProxy support
|
||||||
|
// if (method.getAnnotation(ScopedProxy.class) == null)
|
||||||
|
// return;
|
||||||
|
//
|
||||||
|
// Bean bean = method.getRequiredAnnotation(Bean.class);
|
||||||
|
//
|
||||||
|
// if (bean.scope().equals(DefaultScopes.SINGLETON)
|
||||||
|
// || bean.scope().equals(DefaultScopes.PROTOTYPE))
|
||||||
|
// errors.add(new InvalidScopedProxyDeclarationError(method));
|
||||||
|
}
|
||||||
|
|
||||||
public void validate(Object object, List<UsageError> errors) {
|
|
||||||
ModelMethod method = (ModelMethod) object;
|
|
||||||
|
|
||||||
// TODO: re-enable for @ScopedProxy support
|
|
||||||
// if (method.getAnnotation(ScopedProxy.class) == null)
|
|
||||||
// return;
|
|
||||||
//
|
|
||||||
// Bean bean = method.getRequiredAnnotation(Bean.class);
|
|
||||||
//
|
|
||||||
// if (bean.scope().equals(DefaultScopes.SINGLETON)
|
|
||||||
// || bean.scope().equals(DefaultScopes.PROTOTYPE))
|
|
||||||
// errors.add(new InvalidScopedProxyDeclarationError(method));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detects any illegally-overridden {@link Bean} definitions within a particular
|
* Detects any illegally-overridden {@link Bean} definitions within a particular
|
||||||
* {@link ConfigurationModel}
|
* {@link ConfigurationModel}
|
||||||
*
|
*
|
||||||
* @see Bean#allowOverriding()
|
* @see Bean#allowOverriding()
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
class IllegalBeanOverrideValidator implements Validator {
|
class IllegalBeanOverrideValidator implements Validator {
|
||||||
|
|
||||||
public boolean supports(Object object) {
|
|
||||||
return object instanceof ConfigurationModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validate(Object object, List<UsageError> errors) {
|
public boolean supports(Object object) {
|
||||||
ConfigurationModel model = (ConfigurationModel) object;
|
return object instanceof ConfigurationModel;
|
||||||
|
}
|
||||||
ConfigurationClass[] allClasses = model.getAllConfigurationClasses();
|
|
||||||
|
|
||||||
for (int i = 0; i < allClasses.length; i++) {
|
|
||||||
for (ModelMethod method : allClasses[i].getMethods()) {
|
|
||||||
Bean bean = method.getAnnotation(Bean.class);
|
|
||||||
|
|
||||||
if (bean == null || bean.allowOverriding())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (int j = i + 1; j < allClasses.length; j++)
|
public void validate(Object object, List<UsageError> errors) {
|
||||||
if (allClasses[j].hasMethod(method.getName()))
|
ConfigurationModel model = (ConfigurationModel) object;
|
||||||
errors.add(allClasses[i].new IllegalBeanOverrideError(allClasses[j], method));
|
|
||||||
}
|
ConfigurationClass[] allClasses = model.getAllConfigurationClasses();
|
||||||
}
|
|
||||||
}
|
for (int i = 0; i < allClasses.length; i++) {
|
||||||
|
for (ModelMethod method : allClasses[i].getMethods()) {
|
||||||
|
Bean bean = method.getAnnotation(Bean.class);
|
||||||
|
|
||||||
|
if (bean == null || bean.allowOverriding())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (int j = i + 1; j < allClasses.length; j++)
|
||||||
|
if (allClasses[j].hasMethod(method.getName()))
|
||||||
|
errors.add(allClasses[i].new IllegalBeanOverrideError(allClasses[j], method));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -34,51 +35,54 @@ 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
|
|
||||||
// boolean isScopedProxy = (AnnotationUtils.findAnnotation(method, ScopedProxy.class) != null);
|
|
||||||
//
|
|
||||||
// String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
|
|
||||||
// if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
|
|
||||||
// beanName = scopedBeanName;
|
|
||||||
|
|
||||||
if (factoryContainsBean(beanName)) {
|
// TODO: re-enable for @ScopedProxy support
|
||||||
// we have an already existing cached instance of this bean -> retrieve it
|
// boolean isScopedProxy = (AnnotationUtils.findAnnotation(method,
|
||||||
Object cachedBean = beanFactory.getBean(beanName);
|
// ScopedProxy.class) != null);
|
||||||
if (log.isInfoEnabled())
|
//
|
||||||
log.info(format("Returning cached singleton object [%s] for @Bean method %s.%s",
|
// String scopedBeanName =
|
||||||
cachedBean, method.getDeclaringClass().getSimpleName(), beanName));
|
// ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
|
||||||
|
// if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
|
||||||
|
// beanName = scopedBeanName;
|
||||||
|
|
||||||
return cachedBean;
|
if (factoryContainsBean(beanName)) {
|
||||||
}
|
// we have an already existing cached instance of this bean -> retrieve it
|
||||||
|
Object cachedBean = beanFactory.getBean(beanName);
|
||||||
|
if (log.isInfoEnabled())
|
||||||
|
log.info(format("Returning cached singleton object [%s] for @Bean method %s.%s", cachedBean,
|
||||||
|
method.getDeclaringClass().getSimpleName(), beanName));
|
||||||
|
|
||||||
return proxy.invokeSuper(obj, args);
|
return cachedBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return proxy.invokeSuper(obj, args);
|
||||||
* Check the beanFactory to see whether the bean named <var>beanName</var> already exists.
|
}
|
||||||
* Accounts for the fact that the requested bean may be "in creation", i.e.: we're in the
|
|
||||||
* middle of servicing the initial request for this bean. From JavaConfig's perspective,
|
/**
|
||||||
* this means that the bean does not actually yet exist, and that it is now our job to
|
* Check the beanFactory to see whether the bean named <var>beanName</var> already
|
||||||
* create it for the first time by executing the logic in the corresponding Bean method.
|
* exists. Accounts for the fact that the requested bean may be "in creation", i.e.:
|
||||||
* <p>
|
* we're in the middle of servicing the initial request for this bean. From JavaConfig's
|
||||||
* Said another way, this check repurposes {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)}
|
* perspective, this means that the bean does not actually yet exist, and that it is now
|
||||||
* to determine whether the container is calling this method or the user is calling this method.
|
* our job to create it for the first time by executing the logic in the corresponding
|
||||||
*
|
* Bean method.
|
||||||
* @param beanName name of bean to check for
|
* <p>
|
||||||
*
|
* Said another way, this check repurposes
|
||||||
* @return true if <var>beanName</var> already exists in beanFactory
|
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
|
||||||
*/
|
* the container is calling this method or the user is calling this method.
|
||||||
private boolean factoryContainsBean(String beanName) {
|
*
|
||||||
return beanFactory.containsBean(beanName)
|
* @param beanName name of bean to check for
|
||||||
&& !beanFactory.isCurrentlyInCreation(beanName);
|
*
|
||||||
}
|
* @return true if <var>beanName</var> already exists in beanFactory
|
||||||
|
*/
|
||||||
|
private boolean factoryContainsBean(String beanName) {
|
||||||
|
return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,178 +26,183 @@ import org.springframework.util.Assert;
|
||||||
// TODO: SJC-242 document BeanHandler
|
// TODO: SJC-242 document BeanHandler
|
||||||
// TODO: SJC-242 make package-private
|
// TODO: SJC-242 make package-private
|
||||||
class BeanRegistrar implements BeanDefinitionRegistrar {
|
class BeanRegistrar implements BeanDefinitionRegistrar {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(BeanRegistrar.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures that <var>member</var> is a method and is annotated (directly or indirectly)
|
|
||||||
* with {@link Bean @Bean}.
|
|
||||||
*/
|
|
||||||
public boolean accepts(Method method) {
|
|
||||||
return AnnotationUtils.findAnnotation(method, Bean.class) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: SJC-242 method too long
|
|
||||||
public void register(ModelMethod method, BeanDefinitionRegistry registry) {
|
|
||||||
RootBeanDefinition beanDef = new JavaConfigBeanDefinition();
|
|
||||||
|
|
||||||
ConfigurationClass configClass = method.getDeclaringClass();
|
|
||||||
|
|
||||||
beanDef.setFactoryBeanName(configClass.getBeanName());
|
|
||||||
beanDef.setFactoryMethodName(method.getName());
|
|
||||||
|
|
||||||
Bean bean = method.getRequiredAnnotation(Bean.class);
|
private static final Log logger = LogFactory.getLog(BeanRegistrar.class);
|
||||||
|
|
||||||
Configuration defaults = configClass.getMetadata();
|
|
||||||
|
|
||||||
// consider scoping
|
/**
|
||||||
beanDef.setScope(bean.scope());
|
* Ensures that <var>member</var> is a method and is annotated (directly or indirectly)
|
||||||
|
* with {@link Bean @Bean}.
|
||||||
|
*/
|
||||||
|
public boolean accepts(Method method) {
|
||||||
|
return AnnotationUtils.findAnnotation(method, Bean.class) != null;
|
||||||
|
}
|
||||||
|
|
||||||
// consider autowiring
|
// TODO: SJC-242 method too long
|
||||||
if (bean.autowire() != AnnotationUtils.getDefaultValue(Bean.class, "autowire"))
|
public void register(ModelMethod method, BeanDefinitionRegistry registry) {
|
||||||
beanDef.setAutowireMode(bean.autowire().value());
|
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
|
||||||
else if (defaults.defaultAutowire() != AnnotationUtils.getDefaultValue(Configuration.class, "defaultAutowire"))
|
|
||||||
beanDef.setAutowireMode(defaults.defaultAutowire().value());
|
|
||||||
|
|
||||||
String beanName = method.getName();
|
ConfigurationClass configClass = method.getDeclaringClass();
|
||||||
|
|
||||||
// has this already been overriden (i.e.: via XML)?
|
beanDef.setFactoryBeanName(configClass.getBeanName());
|
||||||
if (containsBeanDefinitionIncludingAncestry(beanName, registry)) {
|
beanDef.setFactoryMethodName(method.getName());
|
||||||
BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry);
|
|
||||||
|
|
||||||
// is the existing bean definition one that was created by JavaConfig?
|
Bean bean = method.getRequiredAnnotation(Bean.class);
|
||||||
if (!(existingBeanDef instanceof JavaConfigBeanDefinition)) {
|
|
||||||
// no -> then it's an external override, probably XML
|
|
||||||
|
|
||||||
// ensure that overriding is ok
|
Configuration defaults = configClass.getMetadata();
|
||||||
if (bean.allowOverriding() == false) {
|
|
||||||
UsageError error = configClass.new IllegalBeanOverrideError(null, method);
|
|
||||||
throw new MalformedConfigurationException(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// overriding is legal, return immediately
|
// consider scoping
|
||||||
logger.info(format("Skipping loading bean definition for %s: a definition for bean '%s' already exists. "
|
beanDef.setScope(bean.scope());
|
||||||
+ "This is likely due to an override in XML.",
|
|
||||||
method, beanName));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// propagate this bean's 'role' attribute
|
// consider autowiring
|
||||||
beanDef.setRole(bean.role());
|
if (bean.autowire() != AnnotationUtils.getDefaultValue(Bean.class, "autowire"))
|
||||||
|
beanDef.setAutowireMode(bean.autowire().value());
|
||||||
|
else if (defaults.defaultAutowire() != AnnotationUtils.getDefaultValue(Configuration.class,
|
||||||
|
"defaultAutowire"))
|
||||||
|
beanDef.setAutowireMode(defaults.defaultAutowire().value());
|
||||||
|
|
||||||
// consider aliases
|
String beanName = method.getName();
|
||||||
for (String alias : bean.aliases())
|
|
||||||
registry.registerAlias(beanName, alias);
|
|
||||||
|
|
||||||
// TODO: re-enable for Lazy support
|
// has this already been overriden (i.e.: via XML)?
|
||||||
// // is this bean marked as primary for disambiguation?
|
if (containsBeanDefinitionIncludingAncestry(beanName, registry)) {
|
||||||
// if (bean.primary() == Primary.TRUE)
|
BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry);
|
||||||
// beanDef.setPrimary(true);
|
|
||||||
//
|
|
||||||
// // is this bean lazily instantiated?
|
|
||||||
// if ((bean.lazy() == Lazy.TRUE)
|
|
||||||
// || ((bean.lazy() == Lazy.UNSPECIFIED) && (defaults.defaultLazy() == Lazy.TRUE)))
|
|
||||||
// beanDef.setLazyInit(true);
|
|
||||||
|
|
||||||
// does this bean have a custom init-method specified?
|
// is the existing bean definition one that was created by JavaConfig?
|
||||||
String initMethodName = bean.initMethodName();
|
if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
|
||||||
if (hasText(initMethodName))
|
// no -> then it's an external override, probably XML
|
||||||
beanDef.setInitMethodName(initMethodName);
|
|
||||||
|
|
||||||
// does this bean have a custom destroy-method specified?
|
// ensure that overriding is ok
|
||||||
String destroyMethodName = bean.destroyMethodName();
|
if (bean.allowOverriding() == false) {
|
||||||
if (hasText(destroyMethodName))
|
UsageError error = configClass.new IllegalBeanOverrideError(null, method);
|
||||||
beanDef.setDestroyMethodName(destroyMethodName);
|
throw new MalformedConfigurationException(error);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: re-enable for @ScopedProxy support
|
// overriding is legal, return immediately
|
||||||
// is this method annotated with @ScopedProxy?
|
logger.info(format(
|
||||||
// ScopedProxy scopedProxy = method.getAnnotation(ScopedProxy.class);
|
"Skipping loading bean definition for %s: a definition for bean '%s' already exists. "
|
||||||
// if (scopedProxy != null) {
|
+ "This is likely due to an override in XML.", method, beanName));
|
||||||
// RootBeanDefinition targetDef = beanDef;
|
return;
|
||||||
//
|
}
|
||||||
// // Create a scoped proxy definition for the original bean name,
|
}
|
||||||
// // "hiding" the target bean in an internal target definition.
|
|
||||||
// String targetBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
|
|
||||||
// RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
|
|
||||||
// scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
|
|
||||||
//
|
|
||||||
// if (scopedProxy.proxyTargetClass())
|
|
||||||
// targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
|
|
||||||
// // ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
|
|
||||||
// // don't need to set it explicitly here.
|
|
||||||
// else
|
|
||||||
// scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
|
|
||||||
//
|
|
||||||
// // The target bean should be ignored in favor of the scoped proxy.
|
|
||||||
// targetDef.setAutowireCandidate(false);
|
|
||||||
//
|
|
||||||
// // Register the target bean as separate bean in the factory
|
|
||||||
// registry.registerBeanDefinition(targetBeanName, targetDef);
|
|
||||||
//
|
|
||||||
// // replace the original bean definition with the target one
|
|
||||||
// beanDef = scopedProxyDefinition;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: re-enable for @Meta support
|
// propagate this bean's 'role' attribute
|
||||||
// does this bean method have any @Meta annotations?
|
beanDef.setRole(bean.role());
|
||||||
// for (Meta meta : bean.meta())
|
|
||||||
// beanDef.addMetadataAttribute(new BeanMetadataAttribute(meta.key(), meta.value()));
|
|
||||||
|
|
||||||
if(bean.dependsOn().length > 0)
|
// consider aliases
|
||||||
beanDef.setDependsOn(bean.dependsOn());
|
for (String alias : bean.aliases())
|
||||||
|
registry.registerAlias(beanName, alias);
|
||||||
|
|
||||||
logger.info(format("Registering bean definition for @Bean method %s.%s()",
|
// TODO: re-enable for Lazy support
|
||||||
configClass.getName(), beanName));
|
// // is this bean marked as primary for disambiguation?
|
||||||
|
// if (bean.primary() == Primary.TRUE)
|
||||||
registry.registerBeanDefinition(beanName, beanDef);
|
// beanDef.setPrimary(true);
|
||||||
|
//
|
||||||
}
|
// // is this bean lazily instantiated?
|
||||||
|
// if ((bean.lazy() == Lazy.TRUE)
|
||||||
private boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
|
// || ((bean.lazy() == Lazy.UNSPECIFIED) && (defaults.defaultLazy() == Lazy.TRUE)))
|
||||||
try {
|
// beanDef.setLazyInit(true);
|
||||||
getBeanDefinitionIncludingAncestry(beanName, registry);
|
|
||||||
return true;
|
|
||||||
} catch (NoSuchBeanDefinitionException ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
|
// does this bean have a custom init-method specified?
|
||||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, registry);
|
String initMethodName = bean.initMethodName();
|
||||||
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory)registry;
|
if (hasText(initMethodName))
|
||||||
|
beanDef.setInitMethodName(initMethodName);
|
||||||
do {
|
|
||||||
if (clbf.containsBeanDefinition(beanName))
|
|
||||||
return registry.getBeanDefinition(beanName);
|
|
||||||
|
|
||||||
BeanFactory parent = clbf.getParentBeanFactory();
|
// does this bean have a custom destroy-method specified?
|
||||||
if (parent == null) {
|
String destroyMethodName = bean.destroyMethodName();
|
||||||
clbf = null;
|
if (hasText(destroyMethodName))
|
||||||
} else if (parent instanceof ConfigurableListableBeanFactory) {
|
beanDef.setDestroyMethodName(destroyMethodName);
|
||||||
clbf = (ConfigurableListableBeanFactory) parent;
|
|
||||||
// TODO: re-enable
|
|
||||||
// } else if (parent instanceof AbstractApplicationContext) {
|
|
||||||
// clbf = ((AbstractApplicationContext) parent).getBeanFactory();
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("unknown parent type: " + parent.getClass().getName());
|
|
||||||
}
|
|
||||||
} while (clbf != null);
|
|
||||||
|
|
||||||
throw new NoSuchBeanDefinitionException(
|
// TODO: re-enable for @ScopedProxy support
|
||||||
format("No bean definition matching name '%s' "
|
// is this method annotated with @ScopedProxy?
|
||||||
+ "could be found in %s or its ancestry", beanName, registry));
|
// ScopedProxy scopedProxy = method.getAnnotation(ScopedProxy.class);
|
||||||
}
|
// if (scopedProxy != null) {
|
||||||
|
// RootBeanDefinition targetDef = beanDef;
|
||||||
|
//
|
||||||
|
// // Create a scoped proxy definition for the original bean name,
|
||||||
|
// // "hiding" the target bean in an internal target definition.
|
||||||
|
// String targetBeanName =
|
||||||
|
// ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
|
||||||
|
// RootBeanDefinition scopedProxyDefinition = new
|
||||||
|
// RootBeanDefinition(ScopedProxyFactoryBean.class);
|
||||||
|
// scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName",
|
||||||
|
// targetBeanName);
|
||||||
|
//
|
||||||
|
// if (scopedProxy.proxyTargetClass())
|
||||||
|
// targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE,
|
||||||
|
// Boolean.TRUE);
|
||||||
|
// // ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
|
||||||
|
// // don't need to set it explicitly here.
|
||||||
|
// else
|
||||||
|
// scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass",
|
||||||
|
// Boolean.FALSE);
|
||||||
|
//
|
||||||
|
// // The target bean should be ignored in favor of the scoped proxy.
|
||||||
|
// targetDef.setAutowireCandidate(false);
|
||||||
|
//
|
||||||
|
// // Register the target bean as separate bean in the factory
|
||||||
|
// registry.registerBeanDefinition(targetBeanName, targetDef);
|
||||||
|
//
|
||||||
|
// // replace the original bean definition with the target one
|
||||||
|
// beanDef = scopedProxyDefinition;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: re-enable for @Meta support
|
||||||
|
// does this bean method have any @Meta annotations?
|
||||||
|
// for (Meta meta : bean.meta())
|
||||||
|
// beanDef.addMetadataAttribute(new BeanMetadataAttribute(meta.key(),
|
||||||
|
// meta.value()));
|
||||||
|
|
||||||
|
if (bean.dependsOn().length > 0)
|
||||||
|
beanDef.setDependsOn(bean.dependsOn());
|
||||||
|
|
||||||
|
logger.info(format("Registering bean definition for @Bean method %s.%s()", configClass.getName(),
|
||||||
|
beanName));
|
||||||
|
|
||||||
|
registry.registerBeanDefinition(beanName, beanDef);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
|
||||||
|
try {
|
||||||
|
getBeanDefinitionIncludingAncestry(beanName, registry);
|
||||||
|
return true;
|
||||||
|
} catch (NoSuchBeanDefinitionException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
|
||||||
|
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, registry);
|
||||||
|
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) registry;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (clbf.containsBeanDefinition(beanName))
|
||||||
|
return registry.getBeanDefinition(beanName);
|
||||||
|
|
||||||
|
BeanFactory parent = clbf.getParentBeanFactory();
|
||||||
|
if (parent == null) {
|
||||||
|
clbf = null;
|
||||||
|
} else if (parent instanceof ConfigurableListableBeanFactory) {
|
||||||
|
clbf = (ConfigurableListableBeanFactory) parent;
|
||||||
|
// TODO: re-enable
|
||||||
|
// } else if (parent instanceof AbstractApplicationContext) {
|
||||||
|
// clbf = ((AbstractApplicationContext) parent).getBeanFactory();
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("unknown parent type: " + parent.getClass().getName());
|
||||||
|
}
|
||||||
|
} while (clbf != null);
|
||||||
|
|
||||||
|
throw new NoSuchBeanDefinitionException(format("No bean definition matching name '%s' "
|
||||||
|
+ "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 {
|
|
||||||
}
|
}
|
|
@ -24,93 +24,92 @@ 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.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
class AddAnnotationAdapter extends ClassAdapter {
|
class AddAnnotationAdapter extends ClassAdapter {
|
||||||
private String annotationDesc;
|
private String annotationDesc;
|
||||||
private boolean isAnnotationPresent;
|
private boolean isAnnotationPresent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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);
|
this.annotationDesc = annotationDesc;
|
||||||
this.annotationDesc = annotationDesc;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
||||||
int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version;
|
String[] interfaces) {
|
||||||
cv.visit(v, access, name, signature, superName, interfaces);
|
int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version;
|
||||||
}
|
cv.visit(v, access, name, signature, superName, interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to ensure that the desired annotation is not already present.
|
* Checks to ensure that the desired annotation is not already present.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||||
if (visible && desc.equals(annotationDesc)) {
|
if (visible && desc.equals(annotationDesc)) {
|
||||||
isAnnotationPresent = true;
|
isAnnotationPresent = true;
|
||||||
}
|
}
|
||||||
return cv.visitAnnotation(desc, visible);
|
return cv.visitAnnotation(desc, visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||||
addAnnotation();
|
addAnnotation();
|
||||||
cv.visitInnerClass(name, outerName, innerName, access);
|
cv.visitInnerClass(name, outerName, innerName, access);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||||
addAnnotation();
|
addAnnotation();
|
||||||
return cv.visitField(access, name, desc, signature, value);
|
return cv.visitField(access, name, desc, signature, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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,
|
||||||
addAnnotation();
|
String[] exceptions) {
|
||||||
return cv.visitMethod(access, name, desc, signature, exceptions);
|
addAnnotation();
|
||||||
}
|
return cv.visitMethod(access, name, desc, signature, exceptions);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kicks off the process of actually adding the desired annotation.
|
* Kicks off the process of actually adding the desired annotation.
|
||||||
*
|
*
|
||||||
* @see #addAnnotation()
|
* @see #addAnnotation()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
addAnnotation();
|
addAnnotation();
|
||||||
cv.visitEnd();
|
cv.visitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually adds the desired annotation.
|
* Actually adds the desired annotation.
|
||||||
*/
|
*/
|
||||||
private void addAnnotation() {
|
private void addAnnotation() {
|
||||||
if (!isAnnotationPresent) {
|
if (!isAnnotationPresent) {
|
||||||
AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true);
|
AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true);
|
||||||
if (av != null) {
|
if (av != null) {
|
||||||
av.visitEnd();
|
av.visitEnd();
|
||||||
}
|
}
|
||||||
isAnnotationPresent = true;
|
isAnnotationPresent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -57,176 +56,173 @@ import org.springframework.config.java.ModelMethod;
|
||||||
*/
|
*/
|
||||||
public class ConfigurationEnhancer {
|
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 =
|
|
||||||
new LinkedHashSet<BeanDefinitionRegistrar>();
|
|
||||||
|
|
||||||
private final ArrayList<Callback> callbackInstances =
|
|
||||||
new ArrayList<Callback>();
|
|
||||||
|
|
||||||
private final CallbackFilter callbackFilter =
|
|
||||||
new CallbackFilter() {
|
|
||||||
public int accept(Method candidateMethod) {
|
|
||||||
Iterator<BeanDefinitionRegistrar> iter = registrars.iterator();
|
|
||||||
for(int i=0; iter.hasNext(); i++)
|
|
||||||
if(iter.next().accepts(candidateMethod))
|
|
||||||
return i;
|
|
||||||
|
|
||||||
throw new IllegalStateException(format("No registrar is capable of " +
|
|
||||||
"handling method [%s]. Perhaps you forgot to add a catch-all registrar?",
|
|
||||||
candidateMethod.getName()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
private final LinkedHashSet<BeanDefinitionRegistrar> registrars = new LinkedHashSet<BeanDefinitionRegistrar>();
|
||||||
* Creates a new {@link ConfigurationEnhancer} instance.
|
|
||||||
*/
|
private final ArrayList<Callback> callbackInstances = new ArrayList<Callback>();
|
||||||
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
|
|
||||||
notNull(beanFactory, "beanFactory must be non-null");
|
private final CallbackFilter callbackFilter = new CallbackFilter() {
|
||||||
notNull(model, "model must be non-null");
|
public int accept(Method candidateMethod) {
|
||||||
|
Iterator<BeanDefinitionRegistrar> iter = registrars.iterator();
|
||||||
populateRegistrarsAndCallbacks(beanFactory, model);
|
for (int i = 0; iter.hasNext(); i++)
|
||||||
}
|
if (iter.next().accepts(candidateMethod))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
throw new IllegalStateException(format("No registrar is capable of "
|
||||||
|
+ "handling method [%s]. Perhaps you forgot to add a catch-all registrar?",
|
||||||
|
candidateMethod.getName()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the contents of {@code model} in order to populate {@link #registrars},
|
* Creates a new {@link ConfigurationEnhancer} instance.
|
||||||
* {@link #callbackInstances} and {@link #callbackTypes} appropriately.
|
*/
|
||||||
*
|
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
|
||||||
* @see #callbackFilter
|
notNull(beanFactory, "beanFactory must be non-null");
|
||||||
*/
|
notNull(model, "model must be non-null");
|
||||||
private void populateRegistrarsAndCallbacks(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
|
|
||||||
|
|
||||||
for (ConfigurationClass configClass : model.getAllConfigurationClasses()) {
|
|
||||||
for (ModelMethod method : configClass.getMethods()) {
|
|
||||||
registrars.add(method.getRegistrar());
|
|
||||||
|
|
||||||
Callback callback = method.getCallback();
|
|
||||||
|
|
||||||
if(callback instanceof BeanFactoryAware)
|
|
||||||
((BeanFactoryAware)callback).setBeanFactory(beanFactory);
|
|
||||||
|
|
||||||
callbackInstances.add(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// register a 'catch-all' registrar
|
|
||||||
registrars.add(new BeanDefinitionRegistrar() {
|
|
||||||
|
|
||||||
public boolean accepts(Method method) {
|
populateRegistrarsAndCallbacks(beanFactory, model);
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void register(ModelMethod method, BeanDefinitionRegistry registry) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
});
|
|
||||||
callbackInstances.add(NoOp.INSTANCE);
|
|
||||||
|
|
||||||
for(Callback callback : callbackInstances)
|
|
||||||
callbackTypes.add(callback.getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the specified class and generates a CGLIB subclass of it equipped with container-aware
|
* Reads the contents of {@code model} in order to populate {@link #registrars},
|
||||||
* callbacks capable of respecting scoping and other bean semantics.
|
* {@link #callbackInstances} and {@link #callbackTypes} appropriately.
|
||||||
*
|
*
|
||||||
* @return fully-qualified name of the enhanced subclass
|
* @see #callbackFilter
|
||||||
*/
|
*/
|
||||||
public String enhance(String configClassName) {
|
private void populateRegistrarsAndCallbacks(DefaultListableBeanFactory beanFactory,
|
||||||
if (log.isInfoEnabled())
|
ConfigurationModel model) {
|
||||||
log.info("Enhancing " + configClassName);
|
|
||||||
|
|
||||||
Class<?> superclass = loadRequiredClass(configClassName);
|
for (ConfigurationClass configClass : model.getAllConfigurationClasses()) {
|
||||||
|
for (ModelMethod method : configClass.getMethods()) {
|
||||||
|
registrars.add(method.getRegistrar());
|
||||||
|
|
||||||
Class<?> subclass = createClass(newEnhancer(superclass), superclass);
|
Callback callback = method.getCallback();
|
||||||
|
|
||||||
subclass = nestOneClassDeeperIfAspect(superclass, subclass);
|
|
||||||
|
|
||||||
if (log.isInfoEnabled())
|
if (callback instanceof BeanFactoryAware)
|
||||||
log.info(format("Successfully enhanced %s; enhanced class name is: %s",
|
((BeanFactoryAware) callback).setBeanFactory(beanFactory);
|
||||||
configClassName, subclass.getName()));
|
|
||||||
|
|
||||||
return subclass.getName();
|
callbackInstances.add(callback);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// register a 'catch-all' registrar
|
||||||
* Creates a new CGLIB {@link Enhancer} instance.
|
registrars.add(new BeanDefinitionRegistrar() {
|
||||||
*/
|
|
||||||
private Enhancer newEnhancer(Class<?> superclass) {
|
public boolean accepts(Method method) {
|
||||||
Enhancer enhancer = new Enhancer();
|
return true;
|
||||||
|
}
|
||||||
// because callbackFilter and callbackTypes are dynamically populated
|
|
||||||
// there's no opportunity for caching. This does not appear to be causing
|
public void register(ModelMethod method, BeanDefinitionRegistry registry) {
|
||||||
// any performance problem.
|
// no-op
|
||||||
enhancer.setUseCache(false);
|
}
|
||||||
|
});
|
||||||
enhancer.setSuperclass(superclass);
|
callbackInstances.add(NoOp.INSTANCE);
|
||||||
enhancer.setUseFactory(false);
|
|
||||||
enhancer.setCallbackFilter(callbackFilter);
|
for (Callback callback : callbackInstances)
|
||||||
enhancer.setCallbackTypes(callbackTypes.toArray(new Class<?>[]{}));
|
callbackTypes.add(callback.getClass());
|
||||||
|
}
|
||||||
return enhancer;
|
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Loads the specified class and generates a CGLIB subclass of it equipped with
|
||||||
|
* container-aware callbacks capable of respecting scoping and other bean semantics.
|
||||||
|
*
|
||||||
|
* @return fully-qualified name of the enhanced subclass
|
||||||
|
*/
|
||||||
|
public String enhance(String configClassName) {
|
||||||
|
if (log.isInfoEnabled())
|
||||||
|
log.info("Enhancing " + configClassName);
|
||||||
|
|
||||||
|
Class<?> superclass = loadRequiredClass(configClassName);
|
||||||
|
|
||||||
|
Class<?> subclass = createClass(newEnhancer(superclass), superclass);
|
||||||
|
|
||||||
|
subclass = nestOneClassDeeperIfAspect(superclass, subclass);
|
||||||
|
|
||||||
|
if (log.isInfoEnabled())
|
||||||
|
log.info(format("Successfully enhanced %s; enhanced class name is: %s", configClassName, subclass
|
||||||
|
.getName()));
|
||||||
|
|
||||||
|
return subclass.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new CGLIB {@link Enhancer} instance.
|
||||||
|
*/
|
||||||
|
private Enhancer newEnhancer(Class<?> superclass) {
|
||||||
|
Enhancer enhancer = new Enhancer();
|
||||||
|
|
||||||
|
// because callbackFilter and callbackTypes are dynamically populated
|
||||||
|
// there's no opportunity for caching. This does not appear to be causing
|
||||||
|
// any performance problem.
|
||||||
|
enhancer.setUseCache(false);
|
||||||
|
|
||||||
|
enhancer.setSuperclass(superclass);
|
||||||
|
enhancer.setUseFactory(false);
|
||||||
|
enhancer.setCallbackFilter(callbackFilter);
|
||||||
|
enhancer.setCallbackTypes(callbackTypes.toArray(new Class<?>[] {}));
|
||||||
|
|
||||||
|
return enhancer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses enhancer to generate a subclass of superclass, ensuring that
|
||||||
|
* {@link #callbackInstances} are registered for the new subclass.
|
||||||
|
*/
|
||||||
|
private Class<?> createClass(Enhancer enhancer, Class<?> superclass) {
|
||||||
|
Class<?> subclass = enhancer.createClass();
|
||||||
|
|
||||||
|
Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[] {}));
|
||||||
|
|
||||||
|
return subclass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Works around a constraint imposed by the AspectJ 5 annotation-style programming
|
||||||
|
* model. See comments inline for detail.
|
||||||
|
*
|
||||||
|
* @return original subclass instance unless superclass is annnotated with @Aspect, in
|
||||||
|
* which case a subclass of the subclass is returned
|
||||||
|
*/
|
||||||
|
private Class<?> nestOneClassDeeperIfAspect(Class<?> superclass, Class<?> origSubclass) {
|
||||||
|
boolean superclassIsAnAspect = false;
|
||||||
|
|
||||||
|
// check for @Aspect by name rather than by class literal to avoid
|
||||||
|
// requiring AspectJ as a runtime dependency.
|
||||||
|
for (Annotation anno : superclass.getAnnotations())
|
||||||
|
if (anno.annotationType().getName().equals("org.aspectj.lang.annotation.Aspect"))
|
||||||
|
superclassIsAnAspect = true;
|
||||||
|
|
||||||
|
if (!superclassIsAnAspect)
|
||||||
|
return origSubclass;
|
||||||
|
|
||||||
|
// the superclass is annotated with AspectJ's @Aspect.
|
||||||
|
// this means that we must create a subclass of the subclass
|
||||||
|
// in order to avoid some guard logic in Spring core that disallows
|
||||||
|
// extending a concrete aspect class.
|
||||||
|
Enhancer enhancer = newEnhancer(origSubclass);
|
||||||
|
enhancer.setStrategy(new DefaultGeneratorStrategy() {
|
||||||
|
@Override
|
||||||
|
protected byte[] transform(byte[] b) throws Exception {
|
||||||
|
ClassWriter writer = new ClassWriter(false);
|
||||||
|
ClassAdapter adapter = new AddAnnotationAdapter(writer,
|
||||||
|
"Lorg/aspectj/lang/annotation/Aspect;");
|
||||||
|
ClassReader reader = new ClassReader(b);
|
||||||
|
reader.accept(adapter, false);
|
||||||
|
return writer.toByteArray();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// create a subclass of the original subclass
|
||||||
|
Class<?> newSubclass = createClass(enhancer, origSubclass);
|
||||||
|
|
||||||
|
return newSubclass;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses enhancer to generate a subclass of superclass, ensuring that
|
|
||||||
* {@link #callbackInstances} are registered for the new subclass.
|
|
||||||
*/
|
|
||||||
private Class<?> createClass(Enhancer enhancer, Class<?> superclass) {
|
|
||||||
Class<?> subclass = enhancer.createClass();
|
|
||||||
|
|
||||||
Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[] {}));
|
|
||||||
|
|
||||||
return subclass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Works around a constraint imposed by the AspectJ 5 annotation-style programming model. See
|
|
||||||
* comments inline for detail.
|
|
||||||
*
|
|
||||||
* @return original subclass instance unless superclass is annnotated with @Aspect, in which
|
|
||||||
* case a subclass of the subclass is returned
|
|
||||||
*/
|
|
||||||
private Class<?> nestOneClassDeeperIfAspect(Class<?> superclass, Class<?> origSubclass) {
|
|
||||||
boolean superclassIsAnAspect = false;
|
|
||||||
|
|
||||||
// check for @Aspect by name rather than by class literal to avoid
|
|
||||||
// requiring AspectJ as a runtime dependency.
|
|
||||||
for(Annotation anno : superclass.getAnnotations())
|
|
||||||
if(anno.annotationType().getName().equals("org.aspectj.lang.annotation.Aspect"))
|
|
||||||
superclassIsAnAspect = true;
|
|
||||||
|
|
||||||
if(!superclassIsAnAspect)
|
|
||||||
return origSubclass;
|
|
||||||
|
|
||||||
// the superclass is annotated with AspectJ's @Aspect.
|
|
||||||
// this means that we must create a subclass of the subclass
|
|
||||||
// in order to avoid some guard logic in Spring core that disallows
|
|
||||||
// extending a concrete aspect class.
|
|
||||||
Enhancer enhancer = newEnhancer(origSubclass);
|
|
||||||
enhancer.setStrategy(new DefaultGeneratorStrategy() {
|
|
||||||
@Override
|
|
||||||
protected byte[] transform(byte[] b) throws Exception {
|
|
||||||
ClassWriter writer = new ClassWriter(false);
|
|
||||||
ClassAdapter adapter =
|
|
||||||
new AddAnnotationAdapter(writer, "Lorg/aspectj/lang/annotation/Aspect;");
|
|
||||||
ClassReader reader = new ClassReader(b);
|
|
||||||
reader.accept(adapter, false);
|
|
||||||
return writer.toByteArray();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// create a subclass of the original subclass
|
|
||||||
Class<?> newSubclass = createClass(enhancer, origSubclass);
|
|
||||||
|
|
||||||
return newSubclass;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,48 +19,47 @@ 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
|
||||||
*/
|
*/
|
||||||
class AnnotationAdapter implements AnnotationVisitor {
|
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}
|
||||||
*/
|
*/
|
||||||
public AnnotationAdapter(AnnotationVisitor delegate) {
|
public AnnotationAdapter(AnnotationVisitor delegate) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visit(String arg0, Object arg1) {
|
public void visit(String arg0, Object arg1) {
|
||||||
delegate.visit(arg0, arg1);
|
delegate.visit(arg0, arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
|
public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
|
||||||
return delegate.visitAnnotation(arg0, arg1);
|
return delegate.visitAnnotation(arg0, arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnnotationVisitor visitArray(String arg0) {
|
public AnnotationVisitor visitArray(String arg0) {
|
||||||
return delegate.visitArray(arg0);
|
return delegate.visitArray(arg0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitEnum(String arg0, String arg1, String arg2) {
|
public void visitEnum(String arg0, String arg1, String arg2) {
|
||||||
delegate.visitEnum(arg0, arg1, arg2);
|
delegate.visitEnum(arg0, arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
delegate.visitEnd();
|
delegate.visitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,108 +30,115 @@ import org.springframework.config.java.Util;
|
||||||
*/
|
*/
|
||||||
class AsmUtils {
|
class AsmUtils {
|
||||||
|
|
||||||
public static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor();
|
public static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor();
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(AsmUtils.class);
|
private static final Log log = LogFactory.getLog(AsmUtils.class);
|
||||||
|
|
||||||
/**
|
|
||||||
* @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"
|
|
||||||
*/
|
|
||||||
public static String convertClassNameToInternalName(String className) {
|
|
||||||
return className.replace('.', '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a type descriptor to a classname suitable for classloading
|
|
||||||
* with Class.forName().
|
|
||||||
*
|
|
||||||
* @param typeDescriptor see ASM guide section 2.1.3
|
|
||||||
*/
|
|
||||||
public static String convertTypeDescriptorToClassName(String typeDescriptor) {
|
|
||||||
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 converting from primitive to object here won't be intuitive to users
|
|
||||||
if("V".equals(typeDescriptor))
|
|
||||||
return Void.class.getName();
|
|
||||||
if("I".equals(typeDescriptor))
|
|
||||||
return Integer.class.getName();
|
|
||||||
if("Z".equals(typeDescriptor))
|
|
||||||
return Boolean.class.getName();
|
|
||||||
|
|
||||||
// strip the leading array/object/primitive identifier
|
/**
|
||||||
if(typeDescriptor.startsWith("[["))
|
* @param className a standard, dot-delimeted, fully-qualified Java class name
|
||||||
internalName = typeDescriptor.substring(3);
|
* @return internal version of className, as per ASM guide section 2.1.2
|
||||||
else if(typeDescriptor.startsWith("["))
|
* "Internal Names"
|
||||||
internalName = typeDescriptor.substring(2);
|
*/
|
||||||
else
|
public static String convertClassNameToInternalName(String className) {
|
||||||
internalName = typeDescriptor.substring(1);
|
return className.replace('.', '/');
|
||||||
|
}
|
||||||
|
|
||||||
// convert slashes to dots
|
/**
|
||||||
String className = internalName.replace('/', '.');
|
* Convert a type descriptor to a classname suitable for classloading with
|
||||||
|
* Class.forName().
|
||||||
|
*
|
||||||
|
* @param typeDescriptor see ASM guide section 2.1.3
|
||||||
|
*/
|
||||||
|
public static String convertTypeDescriptorToClassName(String typeDescriptor) {
|
||||||
|
final String internalName; // See ASM guide section 2.1.2
|
||||||
|
|
||||||
// and strip trailing semicolon (if present)
|
// TODO: SJC-242 should catch all possible cases. use case statement and switch on
|
||||||
if(className.endsWith(";"))
|
// char
|
||||||
className = className.substring(0, internalName.length()-1);
|
// TODO: SJC-242 converting from primitive to object here won't be intuitive to
|
||||||
|
// users
|
||||||
|
if ("V".equals(typeDescriptor))
|
||||||
|
return Void.class.getName();
|
||||||
|
if ("I".equals(typeDescriptor))
|
||||||
|
return Integer.class.getName();
|
||||||
|
if ("Z".equals(typeDescriptor))
|
||||||
|
return Boolean.class.getName();
|
||||||
|
|
||||||
return className;
|
// strip the leading array/object/primitive identifier
|
||||||
}
|
if (typeDescriptor.startsWith("[["))
|
||||||
|
internalName = typeDescriptor.substring(3);
|
||||||
|
else if (typeDescriptor.startsWith("["))
|
||||||
|
internalName = typeDescriptor.substring(2);
|
||||||
|
else
|
||||||
|
internalName = typeDescriptor.substring(1);
|
||||||
|
|
||||||
/**
|
// convert slashes to dots
|
||||||
* @param methodDescriptor see ASM guide section 2.1.4
|
String className = internalName.replace('/', '.');
|
||||||
*/
|
|
||||||
public static String getReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
// and strip trailing semicolon (if present)
|
||||||
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')')+1);
|
if (className.endsWith(";"))
|
||||||
return convertTypeDescriptorToClassName(returnTypeDescriptor);
|
className = className.substring(0, internalName.length() - 1);
|
||||||
}
|
|
||||||
|
return className;
|
||||||
/**
|
}
|
||||||
* Creates a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class'
|
|
||||||
* to pathToClass before attempting to load.
|
/**
|
||||||
*
|
* @param methodDescriptor see ASM guide section 2.1.4
|
||||||
* @throws RuntimeException if <var>pathToClass</var>+.class cannot be found on the classpath
|
*/
|
||||||
* @throws RuntimeException if an IOException occurs when creating the new ClassReader
|
public static String getReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||||
*/
|
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1);
|
||||||
public static ClassReader newClassReader(String pathToClass) {
|
return convertTypeDescriptorToClassName(returnTypeDescriptor);
|
||||||
InputStream is = Util.getClassAsStream(pathToClass);
|
}
|
||||||
return newClassReader(is);
|
|
||||||
}
|
/**
|
||||||
|
* Creates a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class' to
|
||||||
/**
|
* pathToClass before attempting to load.
|
||||||
* Convenience method that simply returns a new ASM {@link ClassReader} instance based on
|
*
|
||||||
* the supplied <var>bytes</var> byte array. This method is exactly equivalent to calling
|
* @throws RuntimeException if <var>pathToClass</var>+.class cannot be found on the
|
||||||
* new ClassReader(byte[]), and is mainly provided for symmetry with usage of
|
* classpath
|
||||||
* {@link #newClassReader(InputStream)}.
|
* @throws RuntimeException if an IOException occurs when creating the new ClassReader
|
||||||
*
|
*/
|
||||||
* @param bytes byte array that will be provided as input to the new ClassReader instance.
|
public static ClassReader newClassReader(String pathToClass) {
|
||||||
*
|
InputStream is = Util.getClassAsStream(pathToClass);
|
||||||
* @return
|
return newClassReader(is);
|
||||||
*/
|
}
|
||||||
public static ClassReader newClassReader(byte[] bytes) {
|
|
||||||
return new ClassReader(bytes);
|
/**
|
||||||
}
|
* Convenience method that simply returns a new ASM {@link ClassReader} instance based
|
||||||
|
* on the supplied <var>bytes</var> byte array. This method is exactly equivalent to
|
||||||
/**
|
* calling new ClassReader(byte[]), and is mainly provided for symmetry with usage of
|
||||||
* Convenience method that creates and returns a new ASM {@link ClassReader} for the given
|
* {@link #newClassReader(InputStream)}.
|
||||||
* InputStream <var>is</var>, closing the InputStream after creating the ClassReader and rethrowing
|
*
|
||||||
* any IOException thrown during ClassReader instantiation as an unchecked exception. Logs and ignores
|
* @param bytes byte array that will be provided as input to the new ClassReader
|
||||||
* any IOException thrown when closing the InputStream.
|
* instance.
|
||||||
*
|
*
|
||||||
* @param is InputStream that will be provided to the new ClassReader instance.
|
* @return
|
||||||
*/
|
*/
|
||||||
public static ClassReader newClassReader(InputStream is) {
|
public static ClassReader newClassReader(byte[] bytes) {
|
||||||
try {
|
return new ClassReader(bytes);
|
||||||
return new ClassReader(is);
|
}
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new RuntimeException("An unexpected exception occurred while creating ASM ClassReader: " + ex);
|
/**
|
||||||
} finally {
|
* Convenience method that creates and returns a new ASM {@link ClassReader} for the
|
||||||
try {
|
* given InputStream <var>is</var>, closing the InputStream after creating the
|
||||||
is.close();
|
* ClassReader and rethrowing any IOException thrown during ClassReader instantiation as
|
||||||
} catch (IOException ex) {
|
* an unchecked exception. Logs and ignores any IOException thrown when closing the
|
||||||
log.error("Ignoring exception thrown while closing InputStream", ex);
|
* InputStream.
|
||||||
}
|
*
|
||||||
}
|
* @param is InputStream that will be provided to the new ClassReader instance.
|
||||||
}
|
*/
|
||||||
|
public static ClassReader newClassReader(InputStream is) {
|
||||||
|
try {
|
||||||
|
return new ClassReader(is);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException("An unexpected exception occurred while creating ASM ClassReader: "
|
||||||
|
+ ex);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
log.error("Ignoring exception thrown while closing InputStream", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,113 +34,114 @@ 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
|
||||||
*/
|
*/
|
||||||
class ConfigurationClassMethodVisitor extends MethodAdapter {
|
class ConfigurationClassMethodVisitor extends MethodAdapter {
|
||||||
|
|
||||||
private final ConfigurationClass configClass;
|
private final ConfigurationClass configClass;
|
||||||
private final String methodName;
|
private final String methodName;
|
||||||
private final int modifiers;
|
private final int modifiers;
|
||||||
private final ModelClass returnType;
|
private final ModelClass returnType;
|
||||||
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
||||||
|
|
||||||
private boolean isModelMethod = false;
|
private boolean isModelMethod = false;
|
||||||
private int lineNumber;
|
private int lineNumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ConfigurationClassMethodVisitor} instance.
|
* Creates a new {@link ConfigurationClassMethodVisitor} instance.
|
||||||
*
|
*
|
||||||
* @param configClass model object to which this method will be added
|
* @param configClass model object to which this method will be added
|
||||||
* @param methodName name of the method declared in the {@link Configuration} class
|
* @param methodName name of the method declared in the {@link Configuration} class
|
||||||
* @param methodDescriptor ASM representation of the method signature
|
* @param methodDescriptor ASM representation of the method signature
|
||||||
* @param modifiers modifiers for this method
|
* @param modifiers modifiers for this method
|
||||||
*/
|
*/
|
||||||
public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName,
|
public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName,
|
||||||
String methodDescriptor, int modifiers) {
|
String methodDescriptor, int modifiers) {
|
||||||
super(AsmUtils.EMPTY_VISITOR);
|
super(AsmUtils.EMPTY_VISITOR);
|
||||||
|
|
||||||
this.configClass = configClass;
|
this.configClass = configClass;
|
||||||
this.methodName = methodName;
|
this.methodName = methodName;
|
||||||
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor);
|
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor);
|
||||||
this.modifiers = modifiers;
|
this.modifiers = modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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) {
|
||||||
String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||||
|
|
||||||
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName);
|
|
||||||
|
|
||||||
if(annoClass == null)
|
|
||||||
return super.visitAnnotation(annoTypeDesc, visible);
|
|
||||||
|
|
||||||
Annotation annotation = createMutableAnnotation(annoClass);
|
|
||||||
|
|
||||||
annotations.add(annotation);
|
|
||||||
|
|
||||||
return new MutableAnnotationVisitor(annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName);
|
||||||
* Provides the line number of this method within its declaring class. In reality,
|
|
||||||
* this number is always inaccurate - <var>lineNo</var> represents the line number
|
|
||||||
* of the first instruction in this method. Method declaration line numbers are
|
|
||||||
* not in any way tracked in the bytecode. Any tooling or output that reads this
|
|
||||||
* value will have to compensate and estimate where the actual method declaration
|
|
||||||
* is.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitLineNumber(int lineNo, Label start) {
|
|
||||||
this.lineNumber = lineNo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* enclosing {@link #configClass}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitEnd() {
|
|
||||||
for(Annotation anno : annotations) {
|
|
||||||
if(anno.annotationType().getAnnotation(Factory.class) != null) {
|
|
||||||
isModelMethod = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isModelMethod)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Annotation[] annoArray = annotations.toArray(new Annotation[] { });
|
|
||||||
ModelMethod method = new ModelMethod(methodName, modifiers, returnType, annoArray);
|
|
||||||
method.setLineNumber(lineNumber);
|
|
||||||
configClass.addMethod(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines return type from ASM <var>methodDescriptor</var> and determines whether
|
|
||||||
* that type is an interface.
|
|
||||||
*/
|
|
||||||
private static ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
|
||||||
final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor));
|
|
||||||
|
|
||||||
// detect whether the return type is an interface
|
if (annoClass == null)
|
||||||
newClassReader(convertClassNameToResourcePath(returnType.getName())).accept(
|
return super.visitAnnotation(annoTypeDesc, visible);
|
||||||
new ClassAdapter(AsmUtils.EMPTY_VISITOR) {
|
|
||||||
@Override
|
Annotation annotation = createMutableAnnotation(annoClass);
|
||||||
public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) {
|
|
||||||
returnType.setInterface((arg1 & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE);
|
annotations.add(annotation);
|
||||||
}
|
|
||||||
}, false);
|
return new MutableAnnotationVisitor(annotation);
|
||||||
|
}
|
||||||
return returnType;
|
|
||||||
}
|
/**
|
||||||
|
* Provides the line number of this method within its declaring class. In reality, this
|
||||||
|
* number is always inaccurate - <var>lineNo</var> represents the line number of the
|
||||||
|
* first instruction in this method. Method declaration line numbers are not in any way
|
||||||
|
* tracked in the bytecode. Any tooling or output that reads this value will have to
|
||||||
|
* compensate and estimate where the actual method declaration is.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitLineNumber(int lineNo, Label start) {
|
||||||
|
this.lineNumber = lineNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 enclosing
|
||||||
|
* {@link #configClass}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitEnd() {
|
||||||
|
for (Annotation anno : annotations) {
|
||||||
|
if (anno.annotationType().getAnnotation(Factory.class) != null) {
|
||||||
|
isModelMethod = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isModelMethod)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Annotation[] annoArray = annotations.toArray(new Annotation[] {});
|
||||||
|
ModelMethod method = new ModelMethod(methodName, modifiers, returnType, annoArray);
|
||||||
|
method.setLineNumber(lineNumber);
|
||||||
|
configClass.addMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines return type from ASM <var>methodDescriptor</var> and determines whether
|
||||||
|
* that type is an interface.
|
||||||
|
*/
|
||||||
|
private static ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||||
|
final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor));
|
||||||
|
|
||||||
|
// detect whether the return type is an interface
|
||||||
|
newClassReader(convertClassNameToResourcePath(returnType.getName())).accept(
|
||||||
|
new ClassAdapter(AsmUtils.EMPTY_VISITOR) {
|
||||||
|
@Override
|
||||||
|
public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) {
|
||||||
|
returnType.setInterface((arg1 & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
return returnType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,198 +37,196 @@ 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
|
||||||
*/
|
*/
|
||||||
class ConfigurationClassVisitor extends ClassAdapter {
|
class ConfigurationClassVisitor extends ClassAdapter {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(ConfigurationClassVisitor.class);
|
private static final Log log = LogFactory.getLog(ConfigurationClassVisitor.class);
|
||||||
private static final String OBJECT_DESC = convertClassNameToResourcePath(Object.class.getName());
|
private static final String OBJECT_DESC = convertClassNameToResourcePath(Object.class.getName());
|
||||||
|
|
||||||
private final ConfigurationClass configClass;
|
private final ConfigurationClass configClass;
|
||||||
private final ConfigurationModel model;
|
private final ConfigurationModel model;
|
||||||
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
||||||
|
|
||||||
private boolean processInnerClasses = true;
|
private boolean processInnerClasses = true;
|
||||||
|
|
||||||
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model) {
|
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model) {
|
||||||
super(AsmUtils.EMPTY_VISITOR);
|
super(AsmUtils.EMPTY_VISITOR);
|
||||||
this.configClass = configClass;
|
this.configClass = configClass;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProcessInnerClasses(boolean processInnerClasses) {
|
public void setProcessInnerClasses(boolean processInnerClasses) {
|
||||||
this.processInnerClasses = processInnerClasses;
|
this.processInnerClasses = processInnerClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3,
|
public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3,
|
||||||
String superTypeDesc, String[] arg5) {
|
String superTypeDesc, String[] arg5) {
|
||||||
visitSuperType(superTypeDesc);
|
visitSuperType(superTypeDesc);
|
||||||
|
|
||||||
configClass.setName(convertResourcePathToClassName(classTypeDesc));
|
configClass.setName(convertResourcePathToClassName(classTypeDesc));
|
||||||
|
|
||||||
// ASM always adds ACC_SUPER to the opcodes/modifiers for class definitions.
|
// ASM always adds ACC_SUPER to the opcodes/modifiers for class definitions.
|
||||||
// Unknown as to why (JavaDoc is silent on the matter), but it should be
|
// Unknown as to why (JavaDoc is silent on the matter), but it should be
|
||||||
// eliminated in order to comply with java.lang.reflect.Modifier values.
|
// eliminated in order to comply with java.lang.reflect.Modifier values.
|
||||||
configClass.setModifiers(modifiers - Opcodes.ACC_SUPER);
|
configClass.setModifiers(modifiers - Opcodes.ACC_SUPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void visitSuperType(String superTypeDesc) {
|
private void visitSuperType(String superTypeDesc) {
|
||||||
// traverse up the type hierarchy unless the next ancestor is java.lang.Object
|
// traverse up the type hierarchy unless the next ancestor is java.lang.Object
|
||||||
if(OBJECT_DESC.equals(superTypeDesc))
|
if (OBJECT_DESC.equals(superTypeDesc))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model);
|
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model);
|
||||||
|
|
||||||
ClassReader reader = AsmUtils.newClassReader(superTypeDesc);
|
ClassReader reader = AsmUtils.newClassReader(superTypeDesc);
|
||||||
reader.accept(visitor, false);
|
reader.accept(visitor, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a class level annotation on a {@link Configuration @Configuration} class.
|
* Visits a class level annotation on a {@link Configuration @Configuration} class.
|
||||||
* Accounts for all possible class-level annotations that are respected by JavaConfig
|
* Accounts for all possible class-level annotations that are respected by JavaConfig
|
||||||
* including AspectJ's {@code @Aspect} annotation.
|
* including AspectJ's {@code @Aspect} annotation.
|
||||||
* <p>
|
* <p>
|
||||||
* Upon encountering such an annotation, update the {@link #configClass} model object
|
* Upon encountering such an annotation, update the {@link #configClass} model object
|
||||||
* appropriately, and then return an {@link AnnotationVisitor} implementation that can
|
* appropriately, and then return an {@link AnnotationVisitor} implementation that can
|
||||||
* populate the annotation appropriately with data.
|
* populate the annotation appropriately with data.
|
||||||
*
|
*
|
||||||
* @see MutableAnnotation
|
* @see MutableAnnotation
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||||
|
|
||||||
if (Configuration.class.getName().equals(annoTypeName)) {
|
if (Configuration.class.getName().equals(annoTypeName)) {
|
||||||
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class);
|
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class);
|
||||||
configClass.setMetadata(mutableConfiguration);
|
configClass.setMetadata(mutableConfiguration);
|
||||||
return new MutableAnnotationVisitor(mutableConfiguration);
|
return new MutableAnnotationVisitor(mutableConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: re-enable for @Import support
|
// TODO: re-enable for @Import support
|
||||||
// if (Import.class.getName().equals(annoTypeName)) {
|
// if (Import.class.getName().equals(annoTypeName)) {
|
||||||
// ImportStack importStack = ImportStackHolder.getImportStack();
|
// ImportStack importStack = ImportStackHolder.getImportStack();
|
||||||
//
|
//
|
||||||
// if(importStack.contains(configClass))
|
// if(importStack.contains(configClass))
|
||||||
// throw new CircularImportException(configClass, importStack);
|
// throw new CircularImportException(configClass, importStack);
|
||||||
//
|
//
|
||||||
// importStack.push(configClass);
|
// importStack.push(configClass);
|
||||||
//
|
//
|
||||||
// return new ImportAnnotationVisitor(model);
|
// return new ImportAnnotationVisitor(model);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
// Detect @Plugin annotations
|
// Detect @Plugin annotations
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor();
|
PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor();
|
||||||
|
|
||||||
String className = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
String className = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||||
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
|
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
|
||||||
ClassReader reader = AsmUtils.newClassReader(resourcePath);
|
ClassReader reader = AsmUtils.newClassReader(resourcePath);
|
||||||
reader.accept(classVisitor, false);
|
reader.accept(classVisitor, false);
|
||||||
|
|
||||||
if(!classVisitor.hasPluginAnnotation())
|
if (!classVisitor.hasPluginAnnotation())
|
||||||
return super.visitAnnotation(annoTypeDesc, visible);
|
return super.visitAnnotation(annoTypeDesc, visible);
|
||||||
|
|
||||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
|
||||||
|
|
||||||
if(annoType == null)
|
|
||||||
return super.visitAnnotation(annoTypeDesc, visible);
|
|
||||||
|
|
||||||
Annotation pluginAnno = createMutableAnnotation(annoType);
|
|
||||||
configClass.addPluginAnnotation(pluginAnno);
|
|
||||||
return new MutableAnnotationVisitor(pluginAnno);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PluginAnnotationDetectingClassVisitor extends ClassAdapter {
|
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
||||||
private boolean hasPluginAnnotation = false;
|
|
||||||
private final Extension pluginAnnotation = createMutableAnnotation(Extension.class);
|
|
||||||
|
|
||||||
public PluginAnnotationDetectingClassVisitor() {
|
if (annoType == null)
|
||||||
super(AsmUtils.EMPTY_VISITOR);
|
return super.visitAnnotation(annoTypeDesc, visible);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Annotation pluginAnno = createMutableAnnotation(annoType);
|
||||||
public AnnotationVisitor visitAnnotation(String typeDesc, boolean arg1) {
|
configClass.addPluginAnnotation(pluginAnno);
|
||||||
if(Extension.class.getName().equals(AsmUtils.convertTypeDescriptorToClassName(typeDesc))) {
|
return new MutableAnnotationVisitor(pluginAnno);
|
||||||
hasPluginAnnotation = true;
|
}
|
||||||
return new MutableAnnotationVisitor(pluginAnnotation);
|
|
||||||
}
|
|
||||||
return super.visitAnnotation(typeDesc, arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasPluginAnnotation() {
|
private static class PluginAnnotationDetectingClassVisitor extends ClassAdapter {
|
||||||
return hasPluginAnnotation;
|
private boolean hasPluginAnnotation = false;
|
||||||
}
|
private final Extension pluginAnnotation = createMutableAnnotation(Extension.class);
|
||||||
|
|
||||||
public Extension getPluginAnnotation() {
|
public PluginAnnotationDetectingClassVisitor() {
|
||||||
return pluginAnnotation;
|
super(AsmUtils.EMPTY_VISITOR);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Delegates all {@link Configuration @Configuration} class method parsing to
|
public AnnotationVisitor visitAnnotation(String typeDesc, boolean arg1) {
|
||||||
* {@link ConfigurationClassMethodVisitor}.
|
if (Extension.class.getName().equals(AsmUtils.convertTypeDescriptorToClassName(typeDesc))) {
|
||||||
*/
|
hasPluginAnnotation = true;
|
||||||
@Override
|
return new MutableAnnotationVisitor(pluginAnnotation);
|
||||||
public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor,
|
}
|
||||||
String arg3, String[] arg4) {
|
return super.visitAnnotation(typeDesc, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers);
|
public boolean hasPluginAnnotation() {
|
||||||
}
|
return hasPluginAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
public Extension getPluginAnnotation() {
|
||||||
* Implementation deals with inner classes here even though it would have
|
return pluginAnnotation;
|
||||||
* been more intuitive to deal with outer classes. Due to limitations in ASM
|
}
|
||||||
* (resulting from limitations in the VM spec) we cannot directly look for outer classes
|
}
|
||||||
* in all cases, so instead build up a model of {@link #innerClasses} and process
|
|
||||||
* declaring class logic in a kind of inverted manner.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
|
||||||
if(processInnerClasses == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
String innerClassName = convertResourcePathToClassName(name);
|
/**
|
||||||
String configClassName = configClass.getName();
|
* Delegates all {@link Configuration @Configuration} class method parsing to
|
||||||
|
* {@link ConfigurationClassMethodVisitor}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, String arg3,
|
||||||
|
String[] arg4) {
|
||||||
|
|
||||||
// if the innerClassName is equal to configClassName, we just
|
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers);
|
||||||
// ran into the outermost inner class look up the outer class
|
}
|
||||||
// associated with this
|
|
||||||
if(innerClassName.equals(configClassName)) {
|
|
||||||
if(innerClasses.containsKey(outerName)) {
|
|
||||||
configClass.setDeclaringClass(innerClasses.get(outerName));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigurationClass innerConfigClass = new ConfigurationClass();
|
/**
|
||||||
|
* Implementation deals with inner classes here even though it would have been more
|
||||||
|
* intuitive to deal with outer classes. Due to limitations in ASM (resulting from
|
||||||
|
* limitations in the VM spec) we cannot directly look for outer classes in all cases,
|
||||||
|
* so instead build up a model of {@link #innerClasses} and process declaring class
|
||||||
|
* logic in a kind of inverted manner.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||||
|
if (processInnerClasses == false)
|
||||||
|
return;
|
||||||
|
|
||||||
ConfigurationClassVisitor ccVisitor =
|
String innerClassName = convertResourcePathToClassName(name);
|
||||||
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel());
|
String configClassName = configClass.getName();
|
||||||
ccVisitor.setProcessInnerClasses(false);
|
|
||||||
|
|
||||||
ClassReader reader = AsmUtils.newClassReader(name);
|
// if the innerClassName is equal to configClassName, we just
|
||||||
reader.accept(ccVisitor, false);
|
// ran into the outermost inner class look up the outer class
|
||||||
|
// associated with this
|
||||||
|
if (innerClassName.equals(configClassName)) {
|
||||||
|
if (innerClasses.containsKey(outerName)) {
|
||||||
|
configClass.setDeclaringClass(innerClasses.get(outerName));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(innerClasses.containsKey(outerName))
|
ConfigurationClass innerConfigClass = new ConfigurationClass();
|
||||||
innerConfigClass.setDeclaringClass(innerClasses.get(outerName));
|
|
||||||
|
|
||||||
// is the inner class a @Configuration class? If so, add it to the list
|
ConfigurationClassVisitor ccVisitor = new ConfigurationClassVisitor(innerConfigClass,
|
||||||
if(innerConfigClass.getMetadata() != null)
|
new ConfigurationModel());
|
||||||
innerClasses.put(name, innerConfigClass);
|
ccVisitor.setProcessInnerClasses(false);
|
||||||
}
|
|
||||||
|
ClassReader reader = AsmUtils.newClassReader(name);
|
||||||
|
reader.accept(ccVisitor, false);
|
||||||
|
|
||||||
|
if (innerClasses.containsKey(outerName))
|
||||||
|
innerConfigClass.setDeclaringClass(innerClasses.get(outerName));
|
||||||
|
|
||||||
|
// is the inner class a @Configuration class? If so, add it to the list
|
||||||
|
if (innerConfigClass.getMetadata() != null)
|
||||||
|
innerClasses.put(name, innerConfigClass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,43 +29,43 @@ import org.objectweb.asm.AnnotationVisitor;
|
||||||
|
|
||||||
/** TODO: JAVADOC */
|
/** TODO: JAVADOC */
|
||||||
class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(MutableAnnotationArrayVisitor.class);
|
|
||||||
|
|
||||||
private final ArrayList<Object> values = new ArrayList<Object>();
|
|
||||||
private final MutableAnnotation mutableAnno;
|
|
||||||
private final String attribName;
|
|
||||||
|
|
||||||
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName) {
|
private static final Log log = LogFactory.getLog(MutableAnnotationArrayVisitor.class);
|
||||||
super(AsmUtils.EMPTY_VISITOR);
|
|
||||||
|
|
||||||
this.mutableAnno = mutableAnno;
|
private final ArrayList<Object> values = new ArrayList<Object>();
|
||||||
this.attribName = attribName;
|
private final MutableAnnotation mutableAnno;
|
||||||
}
|
private final String attribName;
|
||||||
|
|
||||||
@Override
|
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName) {
|
||||||
public void visit(String na, Object value) {
|
super(AsmUtils.EMPTY_VISITOR);
|
||||||
values.add(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
this.mutableAnno = mutableAnno;
|
||||||
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
|
this.attribName = attribName;
|
||||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
}
|
||||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
|
||||||
|
|
||||||
if(annoType == null)
|
|
||||||
return super.visitAnnotation(na, annoTypeDesc);
|
|
||||||
|
|
||||||
Annotation anno = createMutableAnnotation(annoType);
|
|
||||||
values.add(anno);
|
|
||||||
return new MutableAnnotationVisitor(anno);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitEnd() {
|
public void visit(String na, Object value) {
|
||||||
Class<?> arrayType = mutableAnno.getAttributeType(attribName);
|
values.add(value);
|
||||||
Object[] array = (Object[])Array.newInstance(arrayType.getComponentType(), 0);
|
}
|
||||||
mutableAnno.setAttributeValue(attribName, values.toArray(array));
|
|
||||||
}
|
@Override
|
||||||
|
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
|
||||||
|
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||||
|
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
||||||
|
|
||||||
|
if (annoType == null)
|
||||||
|
return super.visitAnnotation(na, annoTypeDesc);
|
||||||
|
|
||||||
|
Annotation anno = createMutableAnnotation(annoType);
|
||||||
|
values.add(anno);
|
||||||
|
return new MutableAnnotationVisitor(anno);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEnd() {
|
||||||
|
Class<?> arrayType = mutableAnno.getAttributeType(attribName);
|
||||||
|
Object[] array = (Object[]) Array.newInstance(arrayType.getComponentType(), 0);
|
||||||
|
mutableAnno.setAttributeValue(attribName, values.toArray(array));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,170 +33,173 @@ import org.springframework.util.StringUtils;
|
||||||
/** TODO: JAVADOC */
|
/** TODO: JAVADOC */
|
||||||
final class MutableAnnotationInvocationHandler implements InvocationHandler {
|
final class MutableAnnotationInvocationHandler implements InvocationHandler {
|
||||||
|
|
||||||
private final Class<? extends Annotation> annoType;
|
private final Class<? extends Annotation> annoType;
|
||||||
private final HashMap<String, Object> attributes = new HashMap<String, Object>();
|
private final HashMap<String, Object> attributes = new HashMap<String, Object>();
|
||||||
private final HashMap<String, Class<?>> attributeTypes = new HashMap<String, Class<?>>();
|
private final HashMap<String, Class<?>> attributeTypes = new HashMap<String, Class<?>>();
|
||||||
|
|
||||||
public MutableAnnotationInvocationHandler(Class<? extends Annotation> annoType) {
|
public MutableAnnotationInvocationHandler(Class<? extends Annotation> annoType) {
|
||||||
// pre-populate the attributes hash will all the names
|
// pre-populate the attributes hash will all the names
|
||||||
// and default values of the attributes defined in 'annoType'
|
// and default values of the attributes defined in 'annoType'
|
||||||
Method[] attribs = annoType.getDeclaredMethods();
|
Method[] attribs = annoType.getDeclaredMethods();
|
||||||
for(Method attrib : attribs) {
|
for (Method attrib : attribs) {
|
||||||
this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName()));
|
this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName()));
|
||||||
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName()));
|
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.annoType = annoType;
|
this.annoType = annoType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
Assert.isInstanceOf(Annotation.class, proxy);
|
Assert.isInstanceOf(Annotation.class, proxy);
|
||||||
|
|
||||||
String methodName = method.getName();
|
String methodName = method.getName();
|
||||||
|
|
||||||
// first -> check to see if this method is an attribute on our annotation
|
// first -> check to see if this method is an attribute on our annotation
|
||||||
if(attributes.containsKey(methodName))
|
if (attributes.containsKey(methodName))
|
||||||
return attributes.get(methodName);
|
return attributes.get(methodName);
|
||||||
|
|
||||||
|
|
||||||
// second -> is it a method from java.lang.annotation.Annotation?
|
// second -> is it a method from java.lang.annotation.Annotation?
|
||||||
if(methodName.equals("annotationType"))
|
if (methodName.equals("annotationType"))
|
||||||
return annoType;
|
return annoType;
|
||||||
|
|
||||||
|
|
||||||
// third -> is it a method from java.lang.Object?
|
// third -> is it a method from java.lang.Object?
|
||||||
if(methodName.equals("toString"))
|
if (methodName.equals("toString"))
|
||||||
return format("@%s(%s)", annoType.getName(), getAttribs());
|
return format("@%s(%s)", annoType.getName(), getAttribs());
|
||||||
|
|
||||||
if(methodName.equals("equals"))
|
if (methodName.equals("equals"))
|
||||||
return isEqualTo(proxy, args[0]);
|
return isEqualTo(proxy, args[0]);
|
||||||
|
|
||||||
if(methodName.equals("hashCode"))
|
if (methodName.equals("hashCode"))
|
||||||
return calculateHashCode(proxy);
|
return calculateHashCode(proxy);
|
||||||
|
|
||||||
|
|
||||||
// finally -> is it a method specified by MutableAnno?
|
// finally -> is it a method specified by MutableAnno?
|
||||||
if(methodName.equals("setAttributeValue")) {
|
if (methodName.equals("setAttributeValue")) {
|
||||||
attributes.put((String)args[0], args[1]);
|
attributes.put((String) args[0], args[1]);
|
||||||
return null; // setAttributeValue has a 'void' return type
|
return null; // setAttributeValue has a 'void' return type
|
||||||
}
|
}
|
||||||
|
|
||||||
if(methodName.equals("getAttributeType"))
|
if (methodName.equals("getAttributeType"))
|
||||||
return attributeTypes.get(args[0]);
|
return attributeTypes.get(args[0]);
|
||||||
|
|
||||||
throw new UnsupportedOperationException("this proxy does not support method: " + methodName);
|
throw new UnsupportedOperationException("this proxy does not support method: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conforms to the hashCode() specification for Annotation.
|
* Conforms to the hashCode() specification for Annotation.
|
||||||
*
|
*
|
||||||
* @see Annotation#hashCode()
|
* @see Annotation#hashCode()
|
||||||
*/
|
*/
|
||||||
private Object calculateHashCode(Object proxy) {
|
private Object calculateHashCode(Object proxy) {
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
|
|
||||||
for (String attribName : attributes.keySet()) {
|
for (String attribName : attributes.keySet()) {
|
||||||
Object attribValue = attributes.get(attribName);
|
Object attribValue = attributes.get(attribName);
|
||||||
|
|
||||||
final int attribNameHashCode = attribName.hashCode();
|
final int attribNameHashCode = attribName.hashCode();
|
||||||
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
|
||||||
attribValueHashCode = 0;
|
// and before it has actually been visited (and populated) by
|
||||||
else if (attribValue.getClass().isArray())
|
// MutableAnnotationVisitor
|
||||||
attribValueHashCode = Arrays.hashCode((Object[]) attribValue);
|
attribValueHashCode = 0;
|
||||||
else
|
else if (attribValue.getClass().isArray())
|
||||||
attribValueHashCode = attribValue.hashCode();
|
attribValueHashCode = Arrays.hashCode((Object[]) attribValue);
|
||||||
|
else
|
||||||
|
attribValueHashCode = attribValue.hashCode();
|
||||||
|
|
||||||
sum += (127 * attribNameHashCode) ^ attribValueHashCode;
|
sum += (127 * attribNameHashCode) ^ attribValueHashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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>
|
||||||
* Conforms strictly to the equals() specification for Annotation</p>
|
* <p/>
|
||||||
*
|
* Conforms strictly to the equals() specification for Annotation
|
||||||
* @see Annotation#equals(Object)
|
* </p>
|
||||||
*/
|
*
|
||||||
private Object isEqualTo(Object proxy, Object other) {
|
* @see Annotation#equals(Object)
|
||||||
if (proxy == other)
|
*/
|
||||||
return true;
|
private Object isEqualTo(Object proxy, Object other) {
|
||||||
|
if (proxy == other)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (other == null)
|
if (other == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!annoType.isAssignableFrom(other.getClass()))
|
if (!annoType.isAssignableFrom(other.getClass()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (String attribName : attributes.keySet()) {
|
for (String attribName : attributes.keySet()) {
|
||||||
Object thisVal;
|
Object thisVal;
|
||||||
Object thatVal;
|
Object thatVal;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
thisVal = attributes.get(attribName);
|
thisVal = attributes.get(attribName);
|
||||||
thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other);
|
thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((thisVal == null) && (thatVal != null))
|
if ((thisVal == null) && (thatVal != null))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((thatVal == null) && (thisVal != null))
|
if ((thatVal == null) && (thisVal != null))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (thatVal.getClass().isArray()) {
|
if (thatVal.getClass().isArray()) {
|
||||||
if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) {
|
if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (thisVal instanceof Double) {
|
} else if (thisVal instanceof Double) {
|
||||||
if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal)))
|
if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal)))
|
||||||
return false;
|
return false;
|
||||||
} else if (thisVal instanceof Float) {
|
} else if (thisVal instanceof Float) {
|
||||||
if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal)))
|
if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal)))
|
||||||
return false;
|
return false;
|
||||||
} else if (!thisVal.equals(thatVal)) {
|
} else if (!thisVal.equals(thatVal)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAttribs() {
|
private String getAttribs() {
|
||||||
ArrayList<String> attribs = new ArrayList<String>();
|
ArrayList<String> attribs = new ArrayList<String>();
|
||||||
|
|
||||||
for (String attribName : attributes.keySet())
|
for (String attribName : attributes.keySet())
|
||||||
attribs.add(format("%s=%s", attribName, attributes.get(attribName)));
|
attribs.add(format("%s=%s", attribName, attributes.get(attribName)));
|
||||||
|
|
||||||
return StringUtils.collectionToDelimitedString(attribs, ", ");
|
return StringUtils.collectionToDelimitedString(attribs, ", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the type of the given annotation attribute.
|
* Retrieve the type of the given annotation attribute.
|
||||||
*/
|
*/
|
||||||
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) {
|
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) {
|
||||||
Method method = null;
|
Method method = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
method = annotationType.getDeclaredMethod(attributeName);
|
method = annotationType.getDeclaredMethod(attributeName);
|
||||||
}
|
} catch (Exception ex) {
|
||||||
catch (Exception ex) {
|
ReflectionUtils.handleReflectionException(ex);
|
||||||
ReflectionUtils.handleReflectionException(ex);
|
}
|
||||||
}
|
|
||||||
|
return method.getReturnType();
|
||||||
return method.getReturnType();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,22 +22,22 @@ import java.lang.reflect.Proxy;
|
||||||
/** TODO: JAVADOC */
|
/** TODO: JAVADOC */
|
||||||
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
|
||||||
*/
|
*/
|
||||||
public static <A extends Annotation> A createMutableAnnotation(Class<A> annoType) {
|
public static <A extends Annotation> A createMutableAnnotation(Class<A> annoType) {
|
||||||
MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType);
|
MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType);
|
||||||
ClassLoader classLoader = MutableAnnotationUtils.class.getClassLoader();
|
ClassLoader classLoader = MutableAnnotationUtils.class.getClassLoader();
|
||||||
Class<?>[] interfaces = new Class<?>[] {annoType, MutableAnnotation.class};
|
Class<?>[] interfaces = new Class<?>[] { annoType, MutableAnnotation.class };
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
||||||
return mutableAnno;
|
return mutableAnno;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,84 +32,85 @@ import org.springframework.util.Assert;
|
||||||
* Populates a given {@link MutableAnnotation} instance with its attributes.
|
* Populates a given {@link MutableAnnotation} instance with its attributes.
|
||||||
*/
|
*/
|
||||||
class MutableAnnotationVisitor implements AnnotationVisitor {
|
class MutableAnnotationVisitor implements AnnotationVisitor {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(MutableAnnotationVisitor.class);
|
|
||||||
|
|
||||||
protected final MutableAnnotation mutableAnno;
|
private static final Log log = LogFactory.getLog(MutableAnnotationVisitor.class);
|
||||||
|
|
||||||
/**
|
protected final MutableAnnotation mutableAnno;
|
||||||
* Creates a new {@link MutableAnnotationVisitor} instance that will populate
|
|
||||||
* the the attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation}
|
|
||||||
* instead of {@link MutableAnnotation} to avoid the need for callers to typecast.
|
|
||||||
*
|
|
||||||
* @param mutableAnno {@link MutableAnnotation} instance to visit and populate
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if <var>mutableAnno</var> is not of type
|
|
||||||
* {@link MutableAnnotation}
|
|
||||||
*
|
|
||||||
* @see MutableAnnotationUtils#createMutableAnnotation(Class)
|
|
||||||
*/
|
|
||||||
public MutableAnnotationVisitor(Annotation mutableAnno) {
|
|
||||||
Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable");
|
|
||||||
this.mutableAnno = (MutableAnnotation)mutableAnno;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationVisitor visitArray(final String attribName) {
|
/**
|
||||||
return new MutableAnnotationArrayVisitor(mutableAnno, attribName);
|
* Creates a new {@link MutableAnnotationVisitor} instance that will populate the the
|
||||||
}
|
* attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation} instead of
|
||||||
|
* {@link MutableAnnotation} to avoid the need for callers to typecast.
|
||||||
|
*
|
||||||
|
* @param mutableAnno {@link MutableAnnotation} instance to visit and populate
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if <var>mutableAnno</var> is not of type
|
||||||
|
* {@link MutableAnnotation}
|
||||||
|
*
|
||||||
|
* @see MutableAnnotationUtils#createMutableAnnotation(Class)
|
||||||
|
*/
|
||||||
|
public MutableAnnotationVisitor(Annotation mutableAnno) {
|
||||||
|
Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable");
|
||||||
|
this.mutableAnno = (MutableAnnotation) mutableAnno;
|
||||||
|
}
|
||||||
|
|
||||||
public void visit(String attribName, Object attribValue) {
|
public AnnotationVisitor visitArray(final String attribName) {
|
||||||
Class<?> attribReturnType = mutableAnno.getAttributeType(attribName);
|
return new MutableAnnotationArrayVisitor(mutableAnno, attribName);
|
||||||
|
}
|
||||||
|
|
||||||
if (attribReturnType.equals(Class.class)) {
|
public void visit(String attribName, Object attribValue) {
|
||||||
// the attribute type is Class -> load it and set it.
|
Class<?> attribReturnType = mutableAnno.getAttributeType(attribName);
|
||||||
String fqClassName = ((Type) attribValue).getClassName();
|
|
||||||
|
|
||||||
Class<?> classVal = loadToolingSafeClass(fqClassName);
|
|
||||||
|
|
||||||
if(classVal == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
mutableAnno.setAttributeValue(attribName, classVal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, assume the value can be set literally
|
if (attribReturnType.equals(Class.class)) {
|
||||||
mutableAnno.setAttributeValue(attribName, attribValue);
|
// the attribute type is Class -> load it and set it.
|
||||||
}
|
String fqClassName = ((Type) attribValue).getClassName();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
Class<?> classVal = loadToolingSafeClass(fqClassName);
|
||||||
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
|
|
||||||
String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor);
|
|
||||||
|
|
||||||
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName);
|
if (classVal == null)
|
||||||
|
return;
|
||||||
if(enumClass == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Enum enumValue = Enum.valueOf(enumClass, strEnumValue);
|
mutableAnno.setAttributeValue(attribName, classVal);
|
||||||
mutableAnno.setAttributeValue(attribName, enumValue);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) {
|
// otherwise, assume the value can be set literally
|
||||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDesc);
|
mutableAnno.setAttributeValue(attribName, attribValue);
|
||||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
}
|
||||||
|
|
||||||
if(annoType == null)
|
|
||||||
return AsmUtils.EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc);
|
|
||||||
|
|
||||||
Annotation anno = createMutableAnnotation(annoType);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Field attribute = mutableAnno.getClass().getField(attribName);
|
|
||||||
attribute.set(mutableAnno, anno);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MutableAnnotationVisitor(anno);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitEnd() { }
|
@SuppressWarnings("unchecked")
|
||||||
|
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
|
||||||
|
String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor);
|
||||||
|
|
||||||
|
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName);
|
||||||
|
|
||||||
|
if (enumClass == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Enum enumValue = Enum.valueOf(enumClass, strEnumValue);
|
||||||
|
mutableAnno.setAttributeValue(attribName, enumValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) {
|
||||||
|
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDesc);
|
||||||
|
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
||||||
|
|
||||||
|
if (annoType == null)
|
||||||
|
return AsmUtils.EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc);
|
||||||
|
|
||||||
|
Annotation anno = createMutableAnnotation(annoType);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field attribute = mutableAnno.getClass().getField(attribName);
|
||||||
|
attribute.set(mutableAnno, anno);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MutableAnnotationVisitor(anno);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitEnd() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,18 +31,18 @@ import org.springframework.core.Ordered;
|
||||||
@Target(ElementType.ANNOTATION_TYPE)
|
@Target(ElementType.ANNOTATION_TYPE)
|
||||||
@Inherited
|
@Inherited
|
||||||
public @interface Extension {
|
public @interface Extension {
|
||||||
/**
|
/**
|
||||||
* The class that handles this plugin.
|
* The class that handles this plugin.
|
||||||
*/
|
*/
|
||||||
// TODO: SJC-242 rename to handlerType / handlerClass
|
// TODO: SJC-242 rename to handlerType / handlerClass
|
||||||
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
|
||||||
*/
|
*/
|
||||||
int order() default 0;
|
int order() default 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
* Registers bean definitions based on {@link Extension} metadata
|
* Registers bean definitions based on {@link Extension} metadata
|
||||||
*/
|
*/
|
||||||
public interface ExtensionAnnotationBeanDefinitionRegistrar<A extends Annotation> {
|
public interface ExtensionAnnotationBeanDefinitionRegistrar<A extends Annotation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void handle(A annotation, BeanDefinitionRegistry registry);
|
void handle(A annotation, BeanDefinitionRegistry registry);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,145 +43,146 @@ 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
|
||||||
* {@link Resource}.
|
* {@link Resource}.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
class ConfigurationModelBeanDefinitionReader {
|
class ConfigurationModelBeanDefinitionReader {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class);
|
private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class);
|
||||||
|
|
||||||
private final DefaultListableBeanFactory beanFactory;
|
private final DefaultListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ConfigurationModelBeanDefinitionReader} instance.
|
* Creates a new {@link ConfigurationModelBeanDefinitionReader} instance.
|
||||||
*/
|
*/
|
||||||
public ConfigurationModelBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {
|
public ConfigurationModelBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {
|
||||||
this.beanFactory = beanFactory;
|
this.beanFactory = beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
public int loadBeanDefinitions(ConfigurationModel model) {
|
public int loadBeanDefinitions(ConfigurationModel model) {
|
||||||
int initialBeanDefCount = beanFactory.getBeanDefinitionCount();
|
int initialBeanDefCount = beanFactory.getBeanDefinitionCount();
|
||||||
|
|
||||||
for (ConfigurationClass configClass : model.getAllConfigurationClasses())
|
for (ConfigurationClass configClass : model.getAllConfigurationClasses())
|
||||||
loadBeanDefinitionsForConfigurationClass(configClass);
|
loadBeanDefinitionsForConfigurationClass(configClass);
|
||||||
|
|
||||||
return beanFactory.getBeanDefinitionCount() - initialBeanDefCount;
|
return beanFactory.getBeanDefinitionCount() - initialBeanDefCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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) {
|
||||||
doLoadBeanDefinitionForConfigurationClass(configClass);
|
doLoadBeanDefinitionForConfigurationClass(configClass);
|
||||||
|
|
||||||
for (ModelMethod method : configClass.getMethods())
|
|
||||||
loadBeanDefinitionsForModelMethod(method);
|
|
||||||
|
|
||||||
Annotation[] pluginAnnotations = configClass.getPluginAnnotations();
|
for (ModelMethod method : configClass.getMethods())
|
||||||
Arrays.sort(pluginAnnotations, new PluginComparator());
|
loadBeanDefinitionsForModelMethod(method);
|
||||||
for (Annotation annotation : pluginAnnotations)
|
|
||||||
loadBeanDefinitionsForExtensionAnnotation(annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the {@link Configuration} class itself as a bean definition.
|
|
||||||
*/
|
|
||||||
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
|
|
||||||
Configuration metadata = configClass.getMetadata();
|
|
||||||
|
|
||||||
if (metadata.checkRequired() == true) {
|
|
||||||
RootBeanDefinition requiredAnnotationPostProcessor = new RootBeanDefinition();
|
|
||||||
Class<?> beanClass = RequiredAnnotationBeanPostProcessor.class;
|
|
||||||
String beanName = beanClass.getName() + "#0";
|
|
||||||
requiredAnnotationPostProcessor.setBeanClass(beanClass);
|
|
||||||
requiredAnnotationPostProcessor.setResourceDescription("ensures @Required methods have been invoked");
|
|
||||||
beanFactory.registerBeanDefinition(beanName, requiredAnnotationPostProcessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
|
Annotation[] pluginAnnotations = configClass.getPluginAnnotations();
|
||||||
configBeanDef.setBeanClassName(configClass.getName());
|
Arrays.sort(pluginAnnotations, new PluginComparator());
|
||||||
|
for (Annotation annotation : pluginAnnotations)
|
||||||
|
loadBeanDefinitionsForExtensionAnnotation(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
String configBeanName = configClass.getBeanName();
|
/**
|
||||||
|
* Registers the {@link Configuration} class itself as a bean definition.
|
||||||
|
*/
|
||||||
|
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
|
||||||
|
Configuration metadata = configClass.getMetadata();
|
||||||
|
|
||||||
// consider the case where it's already been defined (probably in XML)
|
if (metadata.checkRequired() == true) {
|
||||||
// and potentially has PropertyValues and ConstructorArgs)
|
RootBeanDefinition requiredAnnotationPostProcessor = new RootBeanDefinition();
|
||||||
if (beanFactory.containsBeanDefinition(configBeanName)) {
|
Class<?> beanClass = RequiredAnnotationBeanPostProcessor.class;
|
||||||
if (log.isInfoEnabled())
|
String beanName = beanClass.getName() + "#0";
|
||||||
log.info(format("Copying property and constructor arg values from existing bean definition for "
|
requiredAnnotationPostProcessor.setBeanClass(beanClass);
|
||||||
+ "@Configuration class %s to new bean definition", configBeanName));
|
requiredAnnotationPostProcessor
|
||||||
AbstractBeanDefinition existing = (AbstractBeanDefinition)beanFactory.getBeanDefinition(configBeanName);
|
.setResourceDescription("ensures @Required methods have been invoked");
|
||||||
configBeanDef.setPropertyValues(existing.getPropertyValues());
|
beanFactory.registerBeanDefinition(beanName, requiredAnnotationPostProcessor);
|
||||||
configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues());
|
}
|
||||||
configBeanDef.setResource(existing.getResource());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log.isInfoEnabled())
|
GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
|
||||||
log.info(format("Registering bean definition for @Configuration class %s", configBeanName));
|
configBeanDef.setBeanClassName(configClass.getName());
|
||||||
|
|
||||||
beanFactory.registerBeanDefinition(configBeanName, configBeanDef);
|
String configBeanName = configClass.getBeanName();
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a particular {@link ModelMethod}, registering bean definitions
|
|
||||||
* with {@link #beanFactory} based on its contents.
|
|
||||||
*
|
|
||||||
* @see Factory
|
|
||||||
*/
|
|
||||||
private void loadBeanDefinitionsForModelMethod(ModelMethod method) {
|
|
||||||
method.getRegistrar().register(method, beanFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
// consider the case where it's already been defined (probably in XML)
|
||||||
private void loadBeanDefinitionsForExtensionAnnotation(Annotation anno) {
|
// and potentially has PropertyValues and ConstructorArgs)
|
||||||
//ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory);
|
if (beanFactory.containsBeanDefinition(configBeanName)) {
|
||||||
// there is a fixed assumption that in order for this annotation to have
|
if (log.isInfoEnabled())
|
||||||
// been registered in the first place, it must be meta-annotated with @Plugin
|
log.info(format(
|
||||||
// assert this as an invariant now
|
"Copying property and constructor arg values from existing bean definition for "
|
||||||
Class<?> annoClass = anno.getClass();
|
+ "@Configuration class %s to new bean definition", configBeanName));
|
||||||
Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class);
|
AbstractBeanDefinition existing = (AbstractBeanDefinition) beanFactory
|
||||||
Assert.isTrue(extensionAnno != null,
|
.getBeanDefinition(configBeanName);
|
||||||
format("%s annotation is not annotated as a @%s",
|
configBeanDef.setPropertyValues(existing.getPropertyValues());
|
||||||
annoClass, Extension.class.getSimpleName()));
|
configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues());
|
||||||
|
configBeanDef.setResource(existing.getResource());
|
||||||
|
}
|
||||||
|
|
||||||
Class<? extends ExtensionAnnotationBeanDefinitionRegistrar> extHandlerClass = extensionAnno.handler();
|
if (log.isInfoEnabled())
|
||||||
|
log.info(format("Registering bean definition for @Configuration class %s", configBeanName));
|
||||||
ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass);
|
|
||||||
extHandler.handle(anno, beanFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PluginComparator implements Comparator<Annotation> {
|
beanFactory.registerBeanDefinition(configBeanName, configBeanDef);
|
||||||
public int compare(Annotation a1, Annotation a2) {
|
}
|
||||||
Integer i1 = getOrder(a1);
|
|
||||||
Integer i2 = getOrder(a2);
|
|
||||||
return i1.compareTo(i2);
|
/**
|
||||||
}
|
* Reads a particular {@link ModelMethod}, registering bean definitions with
|
||||||
|
* {@link #beanFactory} based on its contents.
|
||||||
|
*
|
||||||
|
* @see Factory
|
||||||
|
*/
|
||||||
|
private void loadBeanDefinitionsForModelMethod(ModelMethod method) {
|
||||||
|
method.getRegistrar().register(method, beanFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void loadBeanDefinitionsForExtensionAnnotation(Annotation anno) {
|
||||||
|
// ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory);
|
||||||
|
// there is a fixed assumption that in order for this annotation to have
|
||||||
|
// been registered in the first place, it must be meta-annotated with @Plugin
|
||||||
|
// assert this as an invariant now
|
||||||
|
Class<?> annoClass = anno.getClass();
|
||||||
|
Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class);
|
||||||
|
Assert.isTrue(extensionAnno != null, format("%s annotation is not annotated as a @%s", annoClass,
|
||||||
|
Extension.class.getSimpleName()));
|
||||||
|
|
||||||
|
Class<? extends ExtensionAnnotationBeanDefinitionRegistrar> extHandlerClass = extensionAnno.handler();
|
||||||
|
|
||||||
|
ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass);
|
||||||
|
extHandler.handle(anno, beanFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PluginComparator implements Comparator<Annotation> {
|
||||||
|
public int compare(Annotation a1, Annotation a2) {
|
||||||
|
Integer i1 = getOrder(a1);
|
||||||
|
Integer i2 = getOrder(a2);
|
||||||
|
return i1.compareTo(i2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getOrder(Annotation a) {
|
||||||
|
Extension plugin = a.annotationType().getAnnotation(Extension.class);
|
||||||
|
if (plugin == null)
|
||||||
|
throw new IllegalArgumentException("annotation was not annotated with @Plugin: "
|
||||||
|
+ a.annotationType());
|
||||||
|
return plugin.order();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Integer getOrder(Annotation a) {
|
|
||||||
Extension plugin = a.annotationType().getAnnotation(Extension.class);
|
|
||||||
if(plugin == null)
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"annotation was not annotated with @Plugin: " + a.annotationType());
|
|
||||||
return plugin.order();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,128 +40,133 @@ 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 {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(ConfigurationPostProcessor.class);
|
private static final Log logger = LogFactory.getLog(ConfigurationPostProcessor.class);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the order in which this {@link BeanPostProcessor} will be executed.
|
|
||||||
* Returns {@link Ordered#HIGHEST_PRECEDENCE}.
|
|
||||||
*/
|
|
||||||
public int getOrder() {
|
|
||||||
return Ordered.HIGHEST_PRECEDENCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches <var>beanFactory</var> for any {@link Configuration} classes in order
|
* Returns the order in which this {@link BeanPostProcessor} will be executed. Returns
|
||||||
* to parse and enhance them. Also registers any {@link BeanPostProcessor} objects
|
* {@link Ordered#HIGHEST_PRECEDENCE}.
|
||||||
* necessary to fulfill JavaConfig requirements.
|
*/
|
||||||
*/
|
public int getOrder() {
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
|
return Ordered.HIGHEST_PRECEDENCE;
|
||||||
if(!(clBeanFactory instanceof DefaultListableBeanFactory))
|
}
|
||||||
throw new IllegalStateException("beanFactory must be of type "
|
|
||||||
+ DefaultListableBeanFactory.class.getSimpleName());
|
|
||||||
|
|
||||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory;
|
|
||||||
|
|
||||||
ConfigurationModel model = new ConfigurationModel();
|
|
||||||
|
|
||||||
parseAnyConfigurationClasses(beanFactory, model);
|
|
||||||
|
|
||||||
enhanceAnyConfigurationClasses(beanFactory, model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
|
/**
|
||||||
|
* Searches <var>beanFactory</var> for any {@link Configuration} classes in order to
|
||||||
// linked map is important for maintaining predictable ordering of configuration classes.
|
* parse and enhance them. Also registers any {@link BeanPostProcessor} objects
|
||||||
// this is important in bean / value override situations.
|
* necessary to fulfill JavaConfig requirements.
|
||||||
LinkedHashMap<String, ClassPathResource> configClassResources = new LinkedHashMap<String, ClassPathResource>();
|
*/
|
||||||
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
|
||||||
|
if (!(clBeanFactory instanceof DefaultListableBeanFactory))
|
||||||
|
throw new IllegalStateException("beanFactory must be of type "
|
||||||
|
+ DefaultListableBeanFactory.class.getSimpleName());
|
||||||
|
|
||||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory;
|
||||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
|
||||||
if (beanDef.isAbstract())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isConfigClass(beanDef)) {
|
ConfigurationModel model = new ConfigurationModel();
|
||||||
String path = ClassUtils.convertClassNameToResourcePath(beanDef.getBeanClassName());
|
|
||||||
configClassResources.put(beanName, new ClassPathResource(path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(beanFactory);
|
|
||||||
ConfigurationParser parser = new ConfigurationParser(model);
|
|
||||||
|
|
||||||
for (String id : configClassResources.keySet())
|
|
||||||
parser.parse(configClassResources.get(id), id);
|
|
||||||
|
|
||||||
ArrayList<UsageError> errors = new ArrayList<UsageError>();
|
parseAnyConfigurationClasses(beanFactory, model);
|
||||||
model.validate(errors);
|
|
||||||
if (errors.size() > 0)
|
|
||||||
throw new MalformedConfigurationException(errors.toArray(new UsageError[] { }));
|
|
||||||
|
|
||||||
modelBeanDefinitionReader.loadBeanDefinitions(model);
|
enhanceAnyConfigurationClasses(beanFactory, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Post-processes a BeanFactory in search of Configuration class BeanDefinitions; any candidates
|
|
||||||
* are then enhanced by a {@link ConfigurationEnhancer}. Candidate status is determined by
|
|
||||||
* BeanDefinition attribute metadata.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
* @see ConfigurationEnhancer
|
|
||||||
* @see BeanFactoryPostProcessor
|
|
||||||
*/
|
|
||||||
private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
|
|
||||||
|
|
||||||
ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model);
|
|
||||||
|
|
||||||
int configClassesEnhanced = 0;
|
private void parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
|
||||||
|
|
||||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
// linked map is important for maintaining predictable ordering of configuration
|
||||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
// classes.
|
||||||
|
// this is important in bean / value override situations.
|
||||||
|
LinkedHashMap<String, ClassPathResource> configClassResources = new LinkedHashMap<String, ClassPathResource>();
|
||||||
|
|
||||||
if (!isConfigClass(beanDef))
|
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||||
continue;
|
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||||
|
if (beanDef.isAbstract())
|
||||||
|
continue;
|
||||||
|
|
||||||
String configClassName = beanDef.getBeanClassName();
|
if (isConfigClass(beanDef)) {
|
||||||
|
String path = ClassUtils.convertClassNameToResourcePath(beanDef.getBeanClassName());
|
||||||
|
configClassResources.put(beanName, new ClassPathResource(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String enhancedClassName = enhancer.enhance(configClassName);
|
ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(
|
||||||
|
beanFactory);
|
||||||
|
ConfigurationParser parser = new ConfigurationParser(model);
|
||||||
|
|
||||||
if (logger.isDebugEnabled())
|
for (String id : configClassResources.keySet())
|
||||||
logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'",
|
parser.parse(configClassResources.get(id), id);
|
||||||
beanName, configClassName, enhancedClassName));
|
|
||||||
|
|
||||||
beanDef.setBeanClassName(enhancedClassName);
|
ArrayList<UsageError> errors = new ArrayList<UsageError>();
|
||||||
|
model.validate(errors);
|
||||||
|
if (errors.size() > 0)
|
||||||
|
throw new MalformedConfigurationException(errors.toArray(new UsageError[] {}));
|
||||||
|
|
||||||
configClassesEnhanced++;
|
modelBeanDefinitionReader.loadBeanDefinitions(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configClassesEnhanced == 0)
|
/**
|
||||||
logger.warn("Found no @Configuration class BeanDefinitions within " + beanFactory);
|
* Post-processes a BeanFactory in search of Configuration class BeanDefinitions; any
|
||||||
}
|
* candidates are then enhanced by a {@link ConfigurationEnhancer}. Candidate status is
|
||||||
|
* determined by BeanDefinition attribute metadata.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @see ConfigurationEnhancer
|
||||||
|
* @see BeanFactoryPostProcessor
|
||||||
|
*/
|
||||||
|
private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory,
|
||||||
|
ConfigurationModel model) {
|
||||||
|
|
||||||
/**
|
ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model);
|
||||||
* Determines whether the class for <var>beanDef</var> is a {@link Configuration}-annotated
|
|
||||||
* class. Returns false if <var>beanDef</var> has no class specified.
|
int configClassesEnhanced = 0;
|
||||||
* <p>
|
|
||||||
* Note: the classloading used within should not be problematic or interfere with tooling in any
|
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||||
* way. BeanFactoryPostProcessing happens only during actual runtime processing via
|
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||||
* {@link JavaConfigApplicationContext} or via XML using {@link ConfigurationPostProcessor}. In
|
|
||||||
* any case, tooling (Spring IDE) will hook in at a lower level than this class and
|
if (!isConfigClass(beanDef))
|
||||||
* thus never encounter this classloading. Should this become problematic, it would not be
|
continue;
|
||||||
* too difficult to replace the following with ASM logic that traverses the class hierarchy in
|
|
||||||
* order to find whether the class is directly or indirectly annotated with
|
String configClassName = beanDef.getBeanClassName();
|
||||||
* {@link Configuration}.
|
|
||||||
*/
|
String enhancedClassName = enhancer.enhance(configClassName);
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger
|
||||||
|
.debug(String
|
||||||
|
.format(
|
||||||
|
"Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'",
|
||||||
|
beanName, configClassName, enhancedClassName));
|
||||||
|
|
||||||
|
beanDef.setBeanClassName(enhancedClassName);
|
||||||
|
|
||||||
|
configClassesEnhanced++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configClassesEnhanced == 0)
|
||||||
|
logger.warn("Found no @Configuration class BeanDefinitions within " + beanFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the class for <var>beanDef</var> is a {@link Configuration}
|
||||||
|
* -annotated class. Returns false if <var>beanDef</var> has no class specified.
|
||||||
|
* <p>
|
||||||
|
* Note: the classloading used within should not be problematic or interfere with
|
||||||
|
* tooling in any way. BeanFactoryPostProcessing happens only during actual runtime
|
||||||
|
* processing via {@link JavaConfigApplicationContext} or via XML using
|
||||||
|
* {@link ConfigurationPostProcessor}. In any case, tooling (Spring IDE) will hook in at
|
||||||
|
* a lower level than this class and thus never encounter this classloading. Should this
|
||||||
|
* become problematic, it would not be too difficult to replace the following with ASM
|
||||||
|
* logic that traverses the class hierarchy in order to find whether the class is
|
||||||
|
* directly or indirectly annotated with {@link Configuration}.
|
||||||
|
*/
|
||||||
private static boolean isConfigClass(BeanDefinition beanDef) {
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,14 @@ package test.basic;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
|
||||||
public abstract class AbstractJavaConfigTests {
|
public abstract class AbstractJavaConfigTests {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//protected
|
// protected
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,49 +13,56 @@ 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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@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(
|
||||||
"ValueInjectionTests.xml", AutowiredConfigurationTests.class);
|
"ValueInjectionTests.xml", AutowiredConfigurationTests.class);
|
||||||
|
|
||||||
TestBean testBean = factory.getBean("testBean", TestBean.class);
|
TestBean testBean = factory.getBean("testBean", TestBean.class);
|
||||||
assertThat(testBean.getName(), equalTo("foo"));
|
assertThat(testBean.getName(), equalTo("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class ValueConfig {
|
static class ValueConfig {
|
||||||
|
|
||||||
@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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,82 +17,87 @@ 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}
|
||||||
*/
|
*/
|
||||||
private static BeanFactory initBeanFactory(Class<?>... configClasses) {
|
private static BeanFactory initBeanFactory(Class<?>... configClasses) {
|
||||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
|
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
|
||||||
|
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simplestPossibleConfiguration() {
|
public void simplestPossibleConfiguration() {
|
||||||
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
|
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
|
||||||
|
|
||||||
String stringBean = factory.getBean("stringBean", String.class);
|
String stringBean = factory.getBean("stringBean", String.class);
|
||||||
|
|
||||||
assertThat(stringBean, equalTo("foo"));
|
assertThat(stringBean, equalTo("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class SimplestPossibleConfig {
|
static class SimplestPossibleConfig {
|
||||||
public @Bean String stringBean() {
|
public @Bean
|
||||||
|
String stringBean() {
|
||||||
return "foo";
|
return "foo";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void configurationWithPrototypeScopedBeans() {
|
public void configurationWithPrototypeScopedBeans() {
|
||||||
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
|
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
|
||||||
|
|
||||||
TestBean foo = factory.getBean("foo", TestBean.class);
|
TestBean foo = factory.getBean("foo", TestBean.class);
|
||||||
ITestBean bar = factory.getBean("bar", ITestBean.class);
|
ITestBean bar = factory.getBean("bar", ITestBean.class);
|
||||||
ITestBean baz = factory.getBean("baz", ITestBean.class);
|
ITestBean baz = factory.getBean("baz", ITestBean.class);
|
||||||
|
|
||||||
assertThat(foo.getSpouse(), sameInstance(bar));
|
assertThat(foo.getSpouse(), sameInstance(bar));
|
||||||
assertThat(bar.getSpouse(), not(sameInstance(baz)));
|
assertThat(bar.getSpouse(), not(sameInstance(baz)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(scope=Scopes.PROTOTYPE)
|
@Bean(scope = Scopes.PROTOTYPE)
|
||||||
public TestBean baz() {
|
public TestBean baz() {
|
||||||
return new TestBean("bar");
|
return new TestBean("bar");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,19 +20,21 @@ import org.springframework.core.enums.ShortCodedLabeledEnum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: JAVADOC
|
* TODO: JAVADOC
|
||||||
*
|
*
|
||||||
* @author Rob Harrop
|
* @author Rob Harrop
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class Colour extends ShortCodedLabeledEnum {
|
public class Colour extends ShortCodedLabeledEnum {
|
||||||
|
|
||||||
public static final Colour RED = new Colour(0, "RED");
|
public static final Colour RED = new Colour(0, "RED");
|
||||||
|
|
||||||
public static final Colour BLUE = new Colour(1, "BLUE");
|
public static final Colour BLUE = new Colour(1, "BLUE");
|
||||||
|
|
||||||
public static final Colour GREEN = new Colour(2, "GREEN");
|
public static final Colour GREEN = new Colour(2, "GREEN");
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,20 @@ package test.beans;
|
||||||
|
|
||||||
/** TODO: JAVADOC */
|
/** TODO: JAVADOC */
|
||||||
public class DependsOnTestBean {
|
public class DependsOnTestBean {
|
||||||
public TestBean tb;
|
public TestBean tb;
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,6 @@ package test.beans;
|
||||||
/** TODO: JAVADOC */
|
/** TODO: JAVADOC */
|
||||||
public interface INestedTestBean {
|
public interface INestedTestBean {
|
||||||
|
|
||||||
String getCompany();
|
String getCompany();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,6 @@ package test.beans;
|
||||||
/** TODO: JAVADOC */
|
/** TODO: JAVADOC */
|
||||||
public interface IOther {
|
public interface IOther {
|
||||||
|
|
||||||
void absquatulate();
|
void absquatulate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,44 +19,44 @@ 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
|
||||||
*/
|
*/
|
||||||
public interface ITestBean {
|
public interface ITestBean {
|
||||||
|
|
||||||
int getAge();
|
int getAge();
|
||||||
|
|
||||||
void setAge(int age);
|
void setAge(int age);
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
void setName(String name);
|
void setName(String name);
|
||||||
|
|
||||||
ITestBean getSpouse();
|
ITestBean getSpouse();
|
||||||
|
|
||||||
void setSpouse(ITestBean spouse);
|
void setSpouse(ITestBean spouse);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* t null no error.
|
* t null no error.
|
||||||
*/
|
*/
|
||||||
void exceptional(Throwable t) throws Throwable;
|
void exceptional(Throwable t) throws Throwable;
|
||||||
|
|
||||||
Object returnsThis();
|
Object returnsThis();
|
||||||
|
|
||||||
INestedTestBean getDoctor();
|
INestedTestBean getDoctor();
|
||||||
|
|
||||||
INestedTestBean getLawyer();
|
INestedTestBean getLawyer();
|
||||||
|
|
||||||
IndexedTestBean getNestedIndexedBean();
|
IndexedTestBean getNestedIndexedBean();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment the age by one.
|
* Increment the age by one.
|
||||||
*
|
*
|
||||||
* @return the previous age
|
* @return the previous age
|
||||||
*/
|
*/
|
||||||
int haveBirthday();
|
int haveBirthday();
|
||||||
|
|
||||||
void unreliableFileOperation() throws IOException;
|
void unreliableFileOperation() throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,89 +27,119 @@ import java.util.TreeSet;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: JAVADOC
|
* TODO: JAVADOC
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 11.11.2003
|
* @since 11.11.2003
|
||||||
*/
|
*/
|
||||||
public class IndexedTestBean {
|
public class IndexedTestBean {
|
||||||
|
|
||||||
private TestBean[] array;
|
private TestBean[] array;
|
||||||
|
|
||||||
private Collection<TestBean> collection;
|
private Collection<TestBean> collection;
|
||||||
|
|
||||||
private List<TestBean> list;
|
private List<TestBean> list;
|
||||||
|
|
||||||
private Set<TestBean> set;
|
private Set<TestBean> set;
|
||||||
|
|
||||||
private SortedSet<TestBean> sortedSet;
|
private SortedSet<TestBean> sortedSet;
|
||||||
|
|
||||||
private Map<String, Object> map;
|
private Map<String, Object> map;
|
||||||
|
|
||||||
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) {
|
||||||
populate();
|
populate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void populate() {
|
public void populate() {
|
||||||
TestBean tb0 = new TestBean("name0", 0);
|
TestBean tb0 = new TestBean("name0", 0);
|
||||||
TestBean tb1 = new TestBean("name1", 0);
|
TestBean tb1 = new TestBean("name1", 0);
|
||||||
TestBean tb2 = new TestBean("name2", 0);
|
TestBean tb2 = new TestBean("name2", 0);
|
||||||
TestBean tb3 = new TestBean("name3", 0);
|
TestBean tb3 = new TestBean("name3", 0);
|
||||||
TestBean tb4 = new TestBean("name4", 0);
|
TestBean tb4 = new TestBean("name4", 0);
|
||||||
TestBean tb5 = new TestBean("name5", 0);
|
TestBean tb5 = new TestBean("name5", 0);
|
||||||
TestBean tb6 = new TestBean("name6", 0);
|
TestBean tb6 = new TestBean("name6", 0);
|
||||||
TestBean tb7 = new TestBean("name7", 0);
|
TestBean tb7 = new TestBean("name7", 0);
|
||||||
TestBean tbX = new TestBean("nameX", 0);
|
TestBean tbX = new TestBean("nameX", 0);
|
||||||
TestBean tbY = new TestBean("nameY", 0);
|
TestBean tbY = new TestBean("nameY", 0);
|
||||||
this.array = new TestBean[] { tb0, tb1 };
|
this.array = new TestBean[] { tb0, tb1 };
|
||||||
this.list = new ArrayList<TestBean>();
|
this.list = new ArrayList<TestBean>();
|
||||||
this.list.add(tb2);
|
this.list.add(tb2);
|
||||||
this.list.add(tb3);
|
this.list.add(tb3);
|
||||||
this.set = new TreeSet<TestBean>();
|
this.set = new TreeSet<TestBean>();
|
||||||
this.set.add(tb6);
|
this.set.add(tb6);
|
||||||
this.set.add(tb7);
|
this.set.add(tb7);
|
||||||
this.map = new HashMap<String, Object>();
|
this.map = new HashMap<String, Object>();
|
||||||
this.map.put("key1", tb4);
|
this.map.put("key1", tb4);
|
||||||
this.map.put("key2", tb5);
|
this.map.put("key2", tb5);
|
||||||
this.map.put("key.3", tb5);
|
this.map.put("key.3", tb5);
|
||||||
List<TestBean> list = new ArrayList<TestBean>();
|
List<TestBean> list = new ArrayList<TestBean>();
|
||||||
list.add(tbX);
|
list.add(tbX);
|
||||||
list.add(tbY);
|
list.add(tbY);
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,46 +17,47 @@ package test.beans;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple nested test bean used for testing bean factories, AOP framework etc.
|
* Simple nested test bean used for testing bean factories, AOP framework etc.
|
||||||
*
|
*
|
||||||
* @author Trevor D. Cook
|
* @author Trevor D. Cook
|
||||||
* @since 30.09.2003
|
* @since 30.09.2003
|
||||||
*/
|
*/
|
||||||
public class NestedTestBean implements INestedTestBean {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCompany(String company) {
|
public void setCompany(String company) {
|
||||||
this.company = ((company != null) ? company : "");
|
this.company = ((company != null) ? company : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCompany() {
|
public String getCompany() {
|
||||||
return company;
|
return company;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof NestedTestBean)) {
|
if (!(obj instanceof NestedTestBean)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedTestBean ntb = (NestedTestBean) obj;
|
NestedTestBean ntb = (NestedTestBean) obj;
|
||||||
return this.company.equals(ntb.company);
|
return this.company.equals(ntb.company);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return this.company.hashCode();
|
return this.company.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "NestedTestBean: " + this.company;
|
return "NestedTestBean: " + this.company;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,259 +37,383 @@ import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple test bean used for testing bean factories, AOP framework etc.
|
* Simple test bean used for testing bean factories, AOP framework etc.
|
||||||
*
|
*
|
||||||
* @author Rod Johnson
|
* @author Rod Johnson
|
||||||
* @since 15 April 2001
|
* @since 15 April 2001
|
||||||
*/
|
*/
|
||||||
public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOther, Comparable<Object> {
|
public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOther, Comparable<Object> {
|
||||||
|
|
||||||
private String beanName;
|
private String beanName;
|
||||||
|
|
||||||
private String country;
|
private String country;
|
||||||
|
|
||||||
private BeanFactory beanFactory;
|
private BeanFactory beanFactory;
|
||||||
|
|
||||||
private boolean postProcessed;
|
private boolean postProcessed;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private String sex;
|
private String sex;
|
||||||
|
|
||||||
private int age;
|
private int age;
|
||||||
|
|
||||||
private boolean jedi;
|
private boolean jedi;
|
||||||
|
|
||||||
private ITestBean spouse;
|
private ITestBean spouse;
|
||||||
|
|
||||||
private String touchy;
|
private String touchy;
|
||||||
|
|
||||||
private String[] stringArray;
|
private String[] stringArray;
|
||||||
|
|
||||||
private Integer[] someIntegerArray;
|
private Integer[] someIntegerArray;
|
||||||
|
|
||||||
private Date date = new Date();
|
private Date date = new Date();
|
||||||
|
|
||||||
private Float myFloat = new Float(0.0);
|
private Float myFloat = new Float(0.0);
|
||||||
|
|
||||||
private Collection<?> friends = new LinkedList<Object>();
|
private Collection<?> friends = new LinkedList<Object>();
|
||||||
|
|
||||||
private Set<?> someSet = new HashSet<Object>();
|
private Set<?> someSet = new HashSet<Object>();
|
||||||
|
|
||||||
private Map<?, ?> someMap = new HashMap<Object, Object>();
|
private Map<?, ?> someMap = new HashMap<Object, Object>();
|
||||||
|
|
||||||
private List<?> someList = new ArrayList<Object>();
|
private List<?> someList = new ArrayList<Object>();
|
||||||
|
|
||||||
private Properties someProperties = new Properties();
|
private Properties someProperties = new Properties();
|
||||||
|
|
||||||
private INestedTestBean doctor = new NestedTestBean();
|
private INestedTestBean doctor = new NestedTestBean();
|
||||||
|
|
||||||
private INestedTestBean lawyer = new NestedTestBean();
|
private INestedTestBean lawyer = new NestedTestBean();
|
||||||
|
|
||||||
private IndexedTestBean nestedIndexedBean;
|
private IndexedTestBean nestedIndexedBean;
|
||||||
|
|
||||||
private boolean destroyed;
|
private boolean destroyed;
|
||||||
|
|
||||||
private Number someNumber;
|
private Number someNumber;
|
||||||
|
|
||||||
private Colour favouriteColour;
|
private Colour favouriteColour;
|
||||||
|
|
||||||
private Boolean someBoolean;
|
private Boolean someBoolean;
|
||||||
|
|
||||||
private List<?> otherColours;
|
private List<?> otherColours;
|
||||||
|
|
||||||
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;
|
||||||
this.age = age;
|
this.age = age;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestBean(ITestBean spouse, Properties someProperties) {
|
public TestBean(ITestBean spouse, Properties someProperties) {
|
||||||
this.spouse = spouse;
|
this.spouse = spouse;
|
||||||
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) {
|
||||||
throw new Exception("Can't contain a .");
|
throw new Exception("Can't contain a .");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (touchy.indexOf(',') != -1) {
|
if (touchy.indexOf(',') != -1) {
|
||||||
throw new NumberFormatException("Number format exception: contains a ,");
|
throw new NumberFormatException("Number format exception: contains a ,");
|
||||||
}
|
}
|
||||||
|
|
||||||
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 Map<?, ?> getSomeMap() { return someMap; }
|
public void setSomeSet(Set<?> someSet) {
|
||||||
|
this.someSet = someSet;
|
||||||
public void setSomeMap(Map<?, ?> someMap) { this.someMap = someMap; }
|
}
|
||||||
|
|
||||||
public List<?> getSomeList() { return someList; }
|
public Map<?, ?> getSomeMap() {
|
||||||
|
return someMap;
|
||||||
public void setSomeList(List<?> someList) { this.someList = someList; }
|
}
|
||||||
|
|
||||||
public Properties getSomeProperties() { return someProperties; }
|
public void setSomeMap(Map<?, ?> someMap) {
|
||||||
|
this.someMap = someMap;
|
||||||
public void setSomeProperties(Properties someProperties) { this.someProperties = someProperties; }
|
}
|
||||||
|
|
||||||
public INestedTestBean getDoctor() { return doctor; }
|
public List<?> getSomeList() {
|
||||||
|
return someList;
|
||||||
public INestedTestBean getLawyer() { return lawyer; }
|
}
|
||||||
|
|
||||||
public void setDoctor(INestedTestBean bean) { doctor = bean; }
|
public void setSomeList(List<?> someList) {
|
||||||
|
this.someList = someList;
|
||||||
public void setLawyer(INestedTestBean bean) { lawyer = bean; }
|
}
|
||||||
|
|
||||||
public Number getSomeNumber() { return someNumber; }
|
public Properties getSomeProperties() {
|
||||||
|
return someProperties;
|
||||||
public void setSomeNumber(Number someNumber) { this.someNumber = someNumber; }
|
}
|
||||||
|
|
||||||
public Colour getFavouriteColour() { return favouriteColour; }
|
public void setSomeProperties(Properties someProperties) {
|
||||||
|
this.someProperties = someProperties;
|
||||||
public void setFavouriteColour(Colour favouriteColour) { this.favouriteColour = favouriteColour; }
|
}
|
||||||
|
|
||||||
public Boolean getSomeBoolean() { return someBoolean; }
|
public INestedTestBean getDoctor() {
|
||||||
|
return doctor;
|
||||||
public void setSomeBoolean(Boolean someBoolean) { this.someBoolean = someBoolean; }
|
}
|
||||||
|
|
||||||
public IndexedTestBean getNestedIndexedBean() { return nestedIndexedBean; }
|
public INestedTestBean getLawyer() {
|
||||||
|
return lawyer;
|
||||||
public void setNestedIndexedBean(IndexedTestBean nestedIndexedBean) { this.nestedIndexedBean = nestedIndexedBean; }
|
}
|
||||||
|
|
||||||
public List<?> getOtherColours() { return otherColours; }
|
public void setDoctor(INestedTestBean bean) {
|
||||||
|
doctor = bean;
|
||||||
public void setOtherColours(List<?> otherColours) { this.otherColours = otherColours; }
|
}
|
||||||
|
|
||||||
public List<?> getPets() { return pets; }
|
public void setLawyer(INestedTestBean bean) {
|
||||||
|
lawyer = bean;
|
||||||
public void setPets(List<?> pets) { this.pets = pets; }
|
}
|
||||||
|
|
||||||
/**
|
public Number getSomeNumber() {
|
||||||
* @see ITestBean#exceptional(Throwable)
|
return someNumber;
|
||||||
*/
|
}
|
||||||
public void exceptional(Throwable t) throws Throwable {
|
|
||||||
if (t != null) {
|
public void setSomeNumber(Number someNumber) {
|
||||||
throw t;
|
this.someNumber = someNumber;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public Colour getFavouriteColour() {
|
||||||
public void unreliableFileOperation() throws IOException { throw new IOException(); }
|
return favouriteColour;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @see ITestBean#returnsThis()
|
public void setFavouriteColour(Colour favouriteColour) {
|
||||||
*/
|
this.favouriteColour = favouriteColour;
|
||||||
public Object returnsThis() { return this; }
|
}
|
||||||
|
|
||||||
/**
|
public Boolean getSomeBoolean() {
|
||||||
* @see IOther#absquatulate()
|
return someBoolean;
|
||||||
*/
|
}
|
||||||
public void absquatulate() { }
|
|
||||||
|
public void setSomeBoolean(Boolean someBoolean) {
|
||||||
public int haveBirthday() { return age++; }
|
this.someBoolean = someBoolean;
|
||||||
|
}
|
||||||
public void destroy() { this.destroyed = true; }
|
|
||||||
|
public IndexedTestBean getNestedIndexedBean() {
|
||||||
public boolean wasDestroyed() { return destroyed; }
|
return nestedIndexedBean;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
public void setNestedIndexedBean(IndexedTestBean nestedIndexedBean) {
|
||||||
if (this == other) {
|
this.nestedIndexedBean = nestedIndexedBean;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
public List<?> getOtherColours() {
|
||||||
if ((other == null) || !(other instanceof TestBean)) {
|
return otherColours;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
public void setOtherColours(List<?> otherColours) {
|
||||||
TestBean tb2 = (TestBean) other;
|
this.otherColours = otherColours;
|
||||||
return (ObjectUtils.nullSafeEquals(this.name, tb2.name) && (this.age == tb2.age));
|
}
|
||||||
}
|
|
||||||
|
public List<?> getPets() {
|
||||||
@Override
|
return pets;
|
||||||
public int hashCode() { return this.age; }
|
}
|
||||||
|
|
||||||
public int compareTo(Object other) {
|
public void setPets(List<?> pets) {
|
||||||
if ((this.name != null) && (other instanceof TestBean)) {
|
this.pets = pets;
|
||||||
return this.name.compareTo(((TestBean) other).getName());
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
return 1;
|
* @see ITestBean#exceptional(Throwable)
|
||||||
}
|
*/
|
||||||
|
public void exceptional(Throwable t) throws Throwable {
|
||||||
@Override
|
if (t != null) {
|
||||||
public String toString() {
|
throw t;
|
||||||
String s = "name=" + name + "; age=" + age + "; touchy=" + touchy;
|
}
|
||||||
s += "; spouse={" + ((spouse != null) ? spouse.getName() : null) + "}";
|
}
|
||||||
return s;
|
|
||||||
}
|
public void unreliableFileOperation() throws IOException {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ITestBean#returnsThis()
|
||||||
|
*/
|
||||||
|
public Object returnsThis() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see IOther#absquatulate()
|
||||||
|
*/
|
||||||
|
public void absquatulate() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int haveBirthday() {
|
||||||
|
return age++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
this.destroyed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wasDestroyed() {
|
||||||
|
return destroyed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((other == null) || !(other instanceof TestBean)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBean tb2 = (TestBean) other;
|
||||||
|
return (ObjectUtils.nullSafeEquals(this.name, tb2.name) && (this.age == tb2.age));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(Object other) {
|
||||||
|
if ((this.name != null) && (other instanceof TestBean)) {
|
||||||
|
return this.name.compareTo(((TestBean) other).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String s = "name=" + name + "; age=" + age + "; touchy=" + touchy;
|
||||||
|
s += "; spouse={" + ((spouse != null) ? spouse.getName() : null) + "}";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue