+ Moving .config.java module -> .context
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="var" path="CLOVER_RUNTIME"/>
|
||||
<classpathentry including="**/*.java" kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
|
||||
@@ -10,11 +10,21 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.cenqua.clover.core.prejavabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.cenqua.clover.core.postjavabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<arguments>
|
||||
@@ -32,5 +42,6 @@
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
<nature>com.cenqua.clover.core.clovernature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that a method produces a bean to be managed by the Spring container. The
|
||||
* names and semantics of the attributes to this annotation are intentionally similar
|
||||
* to those of the {@literal <bean/>} element in the Spring XML schema. Deviations are
|
||||
* as follows:
|
||||
*
|
||||
* <p>The Bean annotation does not provide attributes for scope, primary or lazy. Rather,
|
||||
* it should be used in conjunction with {@link Scope}, {@link Primary} and {@link Lazy}
|
||||
* annotations to acheive the same semantics.
|
||||
*
|
||||
* <p>While a {@link #name()} attribute is available, the default strategy for determining
|
||||
* the name of a bean is to use the name of the Bean method. This is convenient and
|
||||
* intuitive, but if explicit naming is desired, the {@link #name()} attribute may be used.
|
||||
* Also note that {@link #name()} accepts an array of strings. This is in order to allow
|
||||
* for specifying multiple names (aka aliases) for a single bean.
|
||||
*
|
||||
* <h3>Constraints</h3>
|
||||
* <ul>
|
||||
* <li>Bean methods are valid only when declared within a {@link Configuration}-annotated class
|
||||
* <li>Bean methods must be non-void, non-final, non-private
|
||||
* <li>Bean methods may not accept any arguments
|
||||
* <li>Bean methods may throw any exception, which will be caught and handled
|
||||
* by the Spring container on processing of the declaring {@link Configuration} class.
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Usage</h3>
|
||||
* <p>Bean methods may reference other Bean methods by calling them directly. This ensures
|
||||
* that references between beans are strongly typed and navigable. So called 'inter-bean
|
||||
* references' are guaranteed to respect scoping and AOP semantics.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see Configuration
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Bean {
|
||||
|
||||
/**
|
||||
* The name of this bean, or if plural, aliases for this bean. If left unspecified
|
||||
* the name of the bean is the name of the annotated method. If specified, the method
|
||||
* name is ignored.
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* The optional name of a method to call on the bean instance during initialization.
|
||||
* Not commonly used, given that the method may be called programmatically directly
|
||||
* within the Bean method.
|
||||
*/
|
||||
String initMethod() default "";
|
||||
|
||||
/**
|
||||
* The optional name of a method to call on the bean instance during upon closing
|
||||
* the application context, for example a {@literal close()}
|
||||
* method on a {@literal DataSource}.
|
||||
*/
|
||||
String destroyMethod() default "";
|
||||
|
||||
/**
|
||||
* Beans on which the current bean depends. Any beans specified are guaranteed to be
|
||||
* created by the container before this bean. Used infrequently in cases where a bean
|
||||
* does not explicitly depend on another through properties or constructor arguments,
|
||||
* but rather depends on the side effects of another bean's initialization.
|
||||
*/
|
||||
String[] dependsOn() default {};
|
||||
|
||||
}
|
||||
|
||||
// TODO: test @Lazy @Bean
|
||||
// TODO: test @Primary @Bean
|
||||
// TODO: test Bean alias scenarios
|
||||
// TODO: test init/destroy method scenarios
|
||||
// TODO: test dependsOn
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that a class declares one or more {@link Bean} methods and may be processed
|
||||
* by the Spring container to generate bean definitions and service requests for those beans
|
||||
* at runtime.
|
||||
*
|
||||
* <p>Configuration is itself annotated as a {@link Component}, therefore Configuration
|
||||
* classes are candidates for component-scanning and may also take advantage of
|
||||
* {@link Autowire} at the field and method and constructor level.
|
||||
*
|
||||
* <p>May be used in conjunction with the {@link Lazy} annotation to indicate that all Bean
|
||||
* methods declared within this class are by default lazily initialized.
|
||||
*
|
||||
* <h3>Constraints</h3>
|
||||
* <ul>
|
||||
* <li>Configuration classes must be non-final
|
||||
* <li>Configuration classes must be non-local (may not be declared within a method)
|
||||
* <li>Configuration classes must have a default/no-arg constructor or an
|
||||
* {@link Autowired} constructor
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see ConfigurationClassPostProcessor
|
||||
* @see Bean
|
||||
* @see Lazy
|
||||
*/
|
||||
@Component
|
||||
@Target( { ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Configuration {
|
||||
|
||||
}
|
||||
|
||||
// TODO: test constructor autowiring<br>
|
||||
// TODO: test private Configuration classes<br>
|
||||
// TODO: test @Lazy @Configuration<br>
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* Annotation that allows one {@link Configuration} class to import another Configuration,
|
||||
* and thereby all its {@link Bean} definitions.
|
||||
*
|
||||
* <p>Provides functionality equivalent to the {@literal <import/>} element in Spring XML.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see Configuration
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Import {
|
||||
|
||||
/**
|
||||
* The {@link Configuration} class or classes to import.
|
||||
*/
|
||||
Class<?>[] value();
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
|
||||
/**
|
||||
* Enumerates the names of the scopes supported out of the box in Spring.
|
||||
*
|
||||
* <p>Not modeled as an actual java enum because annotations that accept a scope attribute
|
||||
* must allow for user-defined scope names. Given that java enums are not extensible, these
|
||||
* must remain simple string constants.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StandardScopes {
|
||||
|
||||
private StandardScopes() { }
|
||||
|
||||
public static final String SINGLETON = "singleton";
|
||||
|
||||
public static final String PROTOTYPE = "prototype";
|
||||
|
||||
public static final String REQUEST = "request";
|
||||
|
||||
public static final String SESSION = "session";
|
||||
|
||||
}
|
||||
|
||||
// TODO: move StandardScopes to appropriate package
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract superclass for processing {@link Configuration}-annotated classes and registering
|
||||
* bean definitions based on {@link Bean}-annotated methods within those classes.
|
||||
*
|
||||
* <p>Provides template method {@link #processConfigBeanDefinitions()} that orchestrates calling each
|
||||
* of several abstract methods to be overriden by concrete implementations that allow for
|
||||
* customizing how {@link Configuration} classes are found ({@link #getConfigurationBeanDefinitions}),
|
||||
* customizing the creation of a {@link ConfigurationParser} ({@link #createConfigurationParser}),
|
||||
* and customizing {@link ConfigurationModel} validation logic ({@link #validateModel}).
|
||||
*
|
||||
* <p>This class was expressly designed with tooling in mind. Spring IDE will maintain it's
|
||||
* own implementation of this class but still take advantage of the generic parsing algorithm
|
||||
* defined here by {@link #processConfigBeanDefinitions()}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see Configuration
|
||||
* @see ConfigurationClassPostProcessor
|
||||
*/
|
||||
public abstract class AbstractConfigurationClassProcessor {
|
||||
|
||||
/**
|
||||
* Used to register any problems detected with {@link Configuration} or {@link Bean}
|
||||
* declarations. For instance, a Bean method marked as {@literal final} is illegal
|
||||
* and would be reported as a problem. Defaults to {@link FailFastProblemReporter},
|
||||
* but is overridable with {@link #setProblemReporter}
|
||||
*/
|
||||
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
||||
|
||||
/**
|
||||
* Populate and return a registry containing all {@link Configuration} bean definitions
|
||||
* to be processed.
|
||||
*
|
||||
* @param includeAbstractBeanDefs whether abstract Configuration bean definitions should
|
||||
* be included in the resulting BeanDefinitionRegistry. Usually false, but called as true
|
||||
* during the enhancement phase.
|
||||
* @see #processConfigBeanDefinitions()
|
||||
*/
|
||||
protected abstract BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs);
|
||||
|
||||
/**
|
||||
* Create and return a new {@link ConfigurationParser}, allowing for customization of
|
||||
* type (ASM/JDT/Reflection) as well as providing specialized ClassLoader during
|
||||
* construction.
|
||||
* @see #processConfigBeanDefinitions()
|
||||
*/
|
||||
protected abstract ConfigurationParser createConfigurationParser();
|
||||
|
||||
/**
|
||||
* Override the default {@link ProblemReporter}.
|
||||
* @param problemReporter custom problem reporter
|
||||
*/
|
||||
protected final void setProblemReporter(ProblemReporter problemReporter) {
|
||||
this.problemReporter = problemReporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently registered {@link ProblemReporter}.
|
||||
*/
|
||||
protected final ProblemReporter getProblemReporter() {
|
||||
return problemReporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and validate a {@link ConfigurationModel} based on the registry of
|
||||
* {@link Configuration} classes provided by {@link #getConfigurationBeanDefinitions},
|
||||
* then, based on the content of that model, create and register bean definitions
|
||||
* against a new {@link BeanDefinitionRegistry}, then return the registry.
|
||||
*
|
||||
* @return registry containing one bean definition per {@link Bean} method declared
|
||||
* within the Configuration classes
|
||||
*/
|
||||
protected final BeanDefinitionRegistry processConfigBeanDefinitions() {
|
||||
BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(false);
|
||||
|
||||
// return an empty registry immediately if no @Configuration classes were found
|
||||
if(configBeanDefs.getBeanDefinitionCount() == 0)
|
||||
return configBeanDefs;
|
||||
|
||||
// populate a new ConfigurationModel by parsing each @Configuration classes
|
||||
ConfigurationParser parser = createConfigurationParser();
|
||||
|
||||
for(String beanName : configBeanDefs.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = configBeanDefs.getBeanDefinition(beanName);
|
||||
String className = beanDef.getBeanClassName();
|
||||
|
||||
parser.parse(className, beanName);
|
||||
}
|
||||
|
||||
ConfigurationModel configModel = parser.getConfigurationModel();
|
||||
|
||||
configModel.validate(problemReporter);
|
||||
|
||||
// read the model and create bean definitions based on its content
|
||||
return new ConfigurationModelBeanDefinitionReader().loadBeanDefinitions(configModel);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import net.sf.cglib.asm.Constants;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
import org.springframework.asm.ClassAdapter;
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.FieldVisitor;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
|
||||
|
||||
/**
|
||||
* Transforms a class by adding bytecode for a class-level annotation. Checks to ensure that
|
||||
* the desired annotation is not already present before adding. Used by
|
||||
* {@link ConfigurationEnhancer} to dynamically add an {@link org.aspectj.lang.Aspect}
|
||||
* annotation to an enhanced Configuration subclass.
|
||||
*
|
||||
* <p>This class was originally adapted from examples the ASM 3.0 documentation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
class AddAnnotationAdapter extends ClassAdapter {
|
||||
private String annotationDesc;
|
||||
private boolean isAnnotationPresent;
|
||||
|
||||
/**
|
||||
* Creates a new AddAnnotationAdapter instance.
|
||||
*
|
||||
* @param cv the ClassVisitor delegate
|
||||
* @param annotationDesc name of the annotation to be added (in type descriptor format)
|
||||
*/
|
||||
public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) {
|
||||
super(cv);
|
||||
this.annotationDesc = annotationDesc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the version of the resulting class is Java 5 or better.
|
||||
*/
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature,
|
||||
String superName, String[] interfaces) {
|
||||
int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version;
|
||||
cv.visit(v, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to ensure that the desired annotation is not already present.
|
||||
*/
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
if (visible && desc.equals(annotationDesc)) {
|
||||
isAnnotationPresent = true;
|
||||
}
|
||||
return cv.visitAnnotation(desc, visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
addAnnotation();
|
||||
cv.visitInnerClass(name, outerName, innerName, access);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
addAnnotation();
|
||||
return cv.visitField(access, name, desc, signature, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
addAnnotation();
|
||||
return cv.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks off the process of actually adding the desired annotation.
|
||||
*
|
||||
* @see #addAnnotation()
|
||||
*/
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
addAnnotation();
|
||||
cv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually adds the desired annotation.
|
||||
*/
|
||||
private void addAnnotation() {
|
||||
if (!isAnnotationPresent) {
|
||||
AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true);
|
||||
if (av != null) {
|
||||
av.visitEnd();
|
||||
}
|
||||
isAnnotationPresent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
|
||||
|
||||
/**
|
||||
* An empty {@link AnnotationVisitor} that delegates to another AnnotationVisitor. This
|
||||
* class can be used as a super class to quickly implement useful annotation adapter
|
||||
* classes, just by overriding the necessary methods. Note that for some reason, ASM
|
||||
* doesn't provide this class (it does provide MethodAdapter and ClassAdapter), thus
|
||||
* we're following the general pattern and adding our own here.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
class AnnotationAdapter implements AnnotationVisitor {
|
||||
|
||||
private AnnotationVisitor delegate;
|
||||
|
||||
/**
|
||||
* Creates a new AnnotationAdapter instance that will delegate all its calls to
|
||||
* <var>delegate</var>.
|
||||
*
|
||||
* @param delegate In most cases, the delegate will simply be
|
||||
* {@link AsmUtils#EMPTY_VISITOR}
|
||||
*/
|
||||
public AnnotationAdapter(AnnotationVisitor delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public void visit(String arg0, Object arg1) {
|
||||
delegate.visit(arg0, arg1);
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
|
||||
return delegate.visitAnnotation(arg0, arg1);
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitArray(String arg0) {
|
||||
return delegate.visitArray(arg0);
|
||||
}
|
||||
|
||||
public void visitEnum(String arg0, String arg1, String arg2) {
|
||||
delegate.visitEnum(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
delegate.visitEnd();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.commons.EmptyVisitor;
|
||||
|
||||
|
||||
/**
|
||||
* Various utility methods commonly used when interacting with ASM.
|
||||
*/
|
||||
class AsmUtils {
|
||||
|
||||
public static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor();
|
||||
|
||||
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("[["))
|
||||
internalName = typeDescriptor.substring(3);
|
||||
else if (typeDescriptor.startsWith("["))
|
||||
internalName = typeDescriptor.substring(2);
|
||||
else
|
||||
internalName = typeDescriptor.substring(1);
|
||||
|
||||
// convert slashes to dots
|
||||
String className = internalName.replace('/', '.');
|
||||
|
||||
// and strip trailing semicolon (if present)
|
||||
if (className.endsWith(";"))
|
||||
className = className.substring(0, internalName.length() - 1);
|
||||
|
||||
return className;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param methodDescriptor see ASM guide section 2.1.4
|
||||
*/
|
||||
public static String getReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1);
|
||||
return convertTypeDescriptorToClassName(returnTypeDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class' to
|
||||
* pathToClass before attempting to load.
|
||||
*
|
||||
* @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 ClassReader newClassReader(String pathToClass, ClassLoader classLoader) {
|
||||
InputStream is = Util.getClassAsStream(pathToClass, classLoader);
|
||||
return newClassReader(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link #newClassReader(InputStream)}.
|
||||
*
|
||||
* @param bytes byte array that will be provided as input to the new ClassReader
|
||||
* instance.
|
||||
*/
|
||||
public static ClassReader newClassReader(byte[] bytes) {
|
||||
return new ClassReader(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that creates and returns a new ASM {@link ClassReader} for the
|
||||
* given 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 any IOException thrown when closing the
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.context.annotation.StandardScopes.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see ConfigurationClass
|
||||
* @see ConfigurationModel
|
||||
* @see ConfigurationParser
|
||||
* @see ConfigurationModelBeanDefinitionReader
|
||||
*/
|
||||
final class BeanMethod implements BeanMetadataElement {
|
||||
|
||||
private final String name;
|
||||
private final int modifiers;
|
||||
private final ModelClass returnType;
|
||||
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
||||
|
||||
private transient ConfigurationClass declaringClass;
|
||||
private transient Object source;
|
||||
|
||||
public BeanMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
|
||||
Assert.hasText(name);
|
||||
this.name = name;
|
||||
|
||||
Assert.notNull(annotations);
|
||||
for (Annotation annotation : annotations)
|
||||
this.annotations.add(annotation);
|
||||
|
||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers);
|
||||
this.modifiers = modifiers;
|
||||
|
||||
Assert.notNull(returnType);
|
||||
this.returnType = returnType;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ModelClass getReturnType() {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.reflect.Modifier
|
||||
*/
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the annotation on this method matching <var>annoType</var> or
|
||||
* {@literal null} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the annotation on this method matching <var>annoType</var>
|
||||
* @throws {@link IllegalStateException} if not present
|
||||
* @see #getAnnotation(Class)
|
||||
*/
|
||||
public <T extends Annotation> T getRequiredAnnotation(Class<T> annoType) {
|
||||
T anno = getAnnotation(annoType);
|
||||
|
||||
Assert.notNull(anno, format("annotation %s not found on %s", annoType.getSimpleName(), this));
|
||||
|
||||
return anno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a bi-directional relationship between this method and its declaring class.
|
||||
*
|
||||
* @see ConfigurationClass#addBeanMethod(BeanMethod)
|
||||
*/
|
||||
public void setDeclaringClass(ConfigurationClass declaringClass) {
|
||||
this.declaringClass = declaringClass;
|
||||
}
|
||||
|
||||
public ConfigurationClass getDeclaringClass() {
|
||||
return declaringClass;
|
||||
}
|
||||
|
||||
public void setSource(Object source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
if (declaringClass == null)
|
||||
throw new IllegalStateException(
|
||||
"declaringClass property is null. Call setDeclaringClass() before calling getLocation()");
|
||||
return new Location(declaringClass.getLocation().getResource(), getSource());
|
||||
}
|
||||
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
|
||||
if (Modifier.isPrivate(getModifiers()))
|
||||
problemReporter.error(new PrivateMethodError());
|
||||
|
||||
if (Modifier.isFinal(getModifiers()))
|
||||
problemReporter.error(new FinalMethodError());
|
||||
|
||||
Scope scope = this.getAnnotation(Scope.class);
|
||||
if(scope != null
|
||||
&& scope.proxyMode() != ScopedProxyMode.NO
|
||||
&& (scope.value().equals(SINGLETON) || scope.value().equals(PROTOTYPE)))
|
||||
problemReporter.error(new InvalidScopedProxyDeclarationError(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String returnTypeName = returnType == null ? "<unknown>" : returnType.getSimpleName();
|
||||
return 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;
|
||||
BeanMethod other = (BeanMethod) 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;
|
||||
}
|
||||
|
||||
/** {@link Bean} methods must be non-private in order to accommodate CGLIB. */
|
||||
class PrivateMethodError extends Problem {
|
||||
PrivateMethodError() {
|
||||
super(format("Method '%s' may not be private; increase the method's visibility to continue", getName()),
|
||||
BeanMethod.this.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
/** {@link Bean} methods must be non-final in order to accommodate CGLIB. */
|
||||
class FinalMethodError extends Problem {
|
||||
FinalMethodError() {
|
||||
super(format("Method '%s' may not be final; remove the final modifier to continue", getName()),
|
||||
BeanMethod.this.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidScopedProxyDeclarationError extends Problem {
|
||||
InvalidScopedProxyDeclarationError(BeanMethod method) {
|
||||
super(format("Method %s contains an invalid annotation declaration: scoped proxies "
|
||||
+ "cannot be created for singleton/prototype beans", method.getName()),
|
||||
BeanMethod.this.getLocation());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper
|
||||
* handling of bean semantics such as scoping and AOP proxying.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see Bean
|
||||
* @see ConfigurationEnhancer
|
||||
*/
|
||||
class BeanMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
private static final Log log = LogFactory.getLog(BeanMethodInterceptor.class);
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory;
|
||||
|
||||
public BeanMethodInterceptor(DefaultListableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances a {@link Bean @Bean} method to check the supplied BeanFactory for the
|
||||
* existence of this bean object.
|
||||
*/
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
|
||||
// by default the bean name is the name of the @Bean-annotated method
|
||||
String beanName = method.getName();
|
||||
|
||||
// check to see if the user has explicitly set the bean name
|
||||
Bean bean = method.getAnnotation(Bean.class);
|
||||
if(bean != null && bean.name().length > 0)
|
||||
beanName = bean.name()[0];
|
||||
|
||||
// determine whether this bean is a scoped-proxy
|
||||
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
||||
boolean isScopedProxy = (scope != null && scope.proxyMode() != ScopedProxyMode.NO);
|
||||
String scopedBeanName = ConfigurationModelBeanDefinitionReader.resolveHiddenScopedProxyBeanName(beanName);
|
||||
if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
|
||||
beanName = scopedBeanName;
|
||||
|
||||
// to handle the case of an inter-bean method reference, we must explicitly check the
|
||||
// container for already cached instances
|
||||
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 cachedBean;
|
||||
}
|
||||
|
||||
// actually create and return the bean
|
||||
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 create it for the first time by executing the logic in the corresponding
|
||||
* Bean method.
|
||||
* <p>
|
||||
* Said another way, this check repurposes
|
||||
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
|
||||
* the container is calling this method or the user is calling this method.
|
||||
*
|
||||
* @param beanName name of bean to check for
|
||||
*
|
||||
* @return true if <var>beanName</var> already exists in beanFactory
|
||||
*/
|
||||
private boolean factoryContainsBean(String beanName) {
|
||||
return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a user-defined {@link Configuration @Configuration} class.
|
||||
* Includes a set of {@link Bean} methods, including all such methods defined in the
|
||||
* ancestry of the class, in a 'flattened-out' manner. Note that each {@link BeanMethod}
|
||||
* representation contains source information about where it was originally detected
|
||||
* (for the purpose of tooling with Spring IDE).
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see ConfigurationModel
|
||||
* @see BeanMethod
|
||||
* @see ConfigurationParser
|
||||
*/
|
||||
final class ConfigurationClass extends ModelClass {
|
||||
|
||||
private String beanName;
|
||||
private int modifiers;
|
||||
private Configuration configurationAnnotation;
|
||||
private HashSet<BeanMethod> methods = new HashSet<BeanMethod>();
|
||||
private ConfigurationClass declaringClass;
|
||||
|
||||
public String getBeanName() {
|
||||
return beanName == null ? getName() : beanName;
|
||||
}
|
||||
|
||||
public void setBeanName(String id) {
|
||||
this.beanName = id;
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public void setModifiers(int modifiers) {
|
||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
public Configuration getConfigurationAnnotation() {
|
||||
return this.configurationAnnotation;
|
||||
}
|
||||
|
||||
public void setConfigurationAnnotation(Configuration configAnno) {
|
||||
Assert.notNull(configAnno, "configuration annotation must be non-null");
|
||||
this.configurationAnnotation = configAnno;
|
||||
}
|
||||
|
||||
public Set<BeanMethod> getBeanMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public ConfigurationClass addBeanMethod(BeanMethod method) {
|
||||
method.setDeclaringClass(this);
|
||||
methods.add(method);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigurationClass getDeclaringClass() {
|
||||
return declaringClass;
|
||||
}
|
||||
|
||||
public void setDeclaringClass(ConfigurationClass configurationClass) {
|
||||
this.declaringClass = configurationClass;
|
||||
}
|
||||
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
// configuration classes must be annotated with @Configuration
|
||||
if (configurationAnnotation == null)
|
||||
problemReporter.error(new NonAnnotatedConfigurationProblem());
|
||||
|
||||
// a configuration class may not be final (CGLIB limitation)
|
||||
if (Modifier.isFinal(modifiers))
|
||||
problemReporter.error(new FinalConfigurationProblem());
|
||||
|
||||
for (BeanMethod method : methods)
|
||||
method.validate(problemReporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format("%s; modifiers=%d; methods=%s", super.toString(), modifiers, methods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
|
||||
result = prime * result + ((beanName == null) ? 0 : beanName.hashCode());
|
||||
result = prime * result + ((configurationAnnotation == null) ? 0 : configurationAnnotation.hashCode());
|
||||
result = prime * result + ((methods == null) ? 0 : methods.hashCode());
|
||||
result = prime * result + modifiers;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!super.equals(obj))
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ConfigurationClass other = (ConfigurationClass) obj;
|
||||
if (declaringClass == null) {
|
||||
if (other.declaringClass != null)
|
||||
return false;
|
||||
} else if (!declaringClass.equals(other.declaringClass))
|
||||
return false;
|
||||
if (beanName == null) {
|
||||
if (other.beanName != null)
|
||||
return false;
|
||||
} else if (!beanName.equals(other.beanName))
|
||||
return false;
|
||||
if (configurationAnnotation == null) {
|
||||
if (other.configurationAnnotation != null)
|
||||
return false;
|
||||
} else if (!configurationAnnotation.equals(other.configurationAnnotation))
|
||||
return false;
|
||||
if (methods == null) {
|
||||
if (other.methods != null)
|
||||
return false;
|
||||
} else if (!methods.equals(other.methods))
|
||||
return false;
|
||||
if (modifiers != other.modifiers)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Configuration classes must be annotated with {@link Configuration @Configuration}. */
|
||||
class NonAnnotatedConfigurationProblem extends Problem {
|
||||
|
||||
NonAnnotatedConfigurationProblem() {
|
||||
super(format("%s was specified as a @Configuration class but was not actually annotated " +
|
||||
"with @Configuration. Annotate the class or do not attempt to process it.",
|
||||
getSimpleName()),
|
||||
ConfigurationClass.this.getLocation());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Configuration classes must be non-final to accommodate CGLIB subclassing. */
|
||||
class FinalConfigurationProblem extends Problem {
|
||||
|
||||
FinalConfigurationProblem() {
|
||||
super(format("@Configuration class [%s] may not be final. Remove the final modifier to continue.",
|
||||
getSimpleName()),
|
||||
ConfigurationClass.this.getLocation());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.springframework.context.annotation.support.AsmUtils.*;
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.context.annotation.support.Util.*;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
import org.springframework.asm.ClassAdapter;
|
||||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodAdapter;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
/**
|
||||
* ASM {@link MethodVisitor} that visits a single method declared in a given
|
||||
* {@link Configuration} class. Determines whether the method is a {@link Bean}
|
||||
* method and if so, adds it to the {@link ConfigurationClass}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
class ConfigurationClassMethodVisitor extends MethodAdapter {
|
||||
|
||||
private final ConfigurationClass configClass;
|
||||
private final String methodName;
|
||||
private final int modifiers;
|
||||
private final ModelClass returnType;
|
||||
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private int lineNumber;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConfigurationClassMethodVisitor} instance.
|
||||
*
|
||||
* @param configClass model object to which this method will be added
|
||||
* @param methodName name of the method declared in the {@link Configuration} class
|
||||
* @param methodDescriptor ASM representation of the method signature
|
||||
* @param modifiers modifiers for this method
|
||||
*/
|
||||
public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName,
|
||||
String methodDescriptor, int modifiers, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
|
||||
this.configClass = configClass;
|
||||
this.methodName = methodName;
|
||||
this.classLoader = classLoader;
|
||||
this.modifiers = modifiers;
|
||||
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a single annotation on this method. Will be called once for each annotation
|
||||
* present (regardless of its RetentionPolicy).
|
||||
*/
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||
String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
|
||||
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName, classLoader);
|
||||
|
||||
if (annoClass == null)
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
|
||||
Annotation annotation = createMutableAnnotation(annoClass);
|
||||
|
||||
annotations.add(annotation);
|
||||
|
||||
return new MutableAnnotationVisitor(annotation, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Bean} method and if so adds it to the enclosing {@link #configClass}.
|
||||
*/
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
for (Annotation anno : annotations) {
|
||||
if (Bean.class.equals(anno.annotationType())) {
|
||||
// this method is annotated with @Bean -> add it to the ConfigurationClass model
|
||||
Annotation[] annoArray = annotations.toArray(new Annotation[] {});
|
||||
BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray);
|
||||
method.setSource(lineNumber);
|
||||
configClass.addBeanMethod(method);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines return type from ASM <var>methodDescriptor</var> and determines whether
|
||||
* that type is an interface.
|
||||
*/
|
||||
private ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||
final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor));
|
||||
|
||||
// detect whether the return type is an interface
|
||||
newClassReader(convertClassNameToResourcePath(returnType.getName()), classLoader).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;
|
||||
}
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.ClassMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
||||
* {@link Configuration @Configuration} classes.
|
||||
* <p>
|
||||
* Registered by default when using {@literal <context:annotation-config/>} or
|
||||
* {@literal <context:component-scan/>}. Otherwise, may be declared manually as
|
||||
* with any other BeanFactoryPostProcessor.
|
||||
* <p>
|
||||
* This post processor is {@link Ordered#HIGHEST_PRECEDENCE} as it's important
|
||||
* that any {@link Bean} methods declared in Configuration classes have their
|
||||
* respective bean definitions registered before any other BeanFactoryPostProcessor
|
||||
* executes.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConfigurationClassPostProcessor extends AbstractConfigurationClassProcessor
|
||||
implements Ordered, BeanFactoryPostProcessor {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ConfigurationClassPostProcessor.class);
|
||||
|
||||
/**
|
||||
* A well-known class in the CGLIB API used when testing to see if CGLIB
|
||||
* is present on the classpath. Package-private visibility allows for
|
||||
* manipulation by tests.
|
||||
* @see #assertCglibIsPresent(BeanDefinitionRegistry)
|
||||
*/
|
||||
static String CGLIB_TEST_CLASS = "net.sf.cglib.proxy.Callback";
|
||||
|
||||
/**
|
||||
* Holder for the calling BeanFactory
|
||||
* @see #postProcessBeanFactory(ConfigurableListableBeanFactory)
|
||||
*/
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* @return {@link Ordered#HIGHEST_PRECEDENCE}.
|
||||
*/
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds {@link Configuration} bean definitions within <var>clBeanFactory</var>
|
||||
* and processes them in order to register bean definitions for each Bean method
|
||||
* found within; also prepares the the Configuration classes for servicing
|
||||
* bean requests at runtime by replacing them with CGLIB-enhanced subclasses.
|
||||
*/
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(DefaultListableBeanFactory.class, clBeanFactory);
|
||||
beanFactory = (DefaultListableBeanFactory) clBeanFactory;
|
||||
|
||||
BeanDefinitionRegistry factoryBeanDefs = processConfigBeanDefinitions();
|
||||
|
||||
for(String beanName : factoryBeanDefs.getBeanDefinitionNames())
|
||||
beanFactory.registerBeanDefinition(beanName, factoryBeanDefs.getBeanDefinition(beanName));
|
||||
|
||||
enhanceConfigurationClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a ConfigurationParser that uses the enclosing BeanFactory's
|
||||
* ClassLoader to load all Configuration class artifacts.
|
||||
*/
|
||||
@Override
|
||||
protected ConfigurationParser createConfigurationParser() {
|
||||
return new ConfigurationParser(this.getProblemReporter(), beanFactory.getBeanClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return map of all non-abstract {@link BeanDefinition}s in the
|
||||
* enclosing {@link #beanFactory}
|
||||
*/
|
||||
@Override
|
||||
protected BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs) {
|
||||
|
||||
BeanDefinitionRegistry configBeanDefs = new DefaultListableBeanFactory();
|
||||
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
|
||||
if (beanDef.isAbstract() && !includeAbstractBeanDefs)
|
||||
continue;
|
||||
|
||||
if (isConfigurationClassBeanDefinition(beanDef))
|
||||
configBeanDefs.registerBeanDefinition(beanName, beanDef);
|
||||
}
|
||||
|
||||
return configBeanDefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @see ConfigurationEnhancer
|
||||
* @see BeanFactoryPostProcessor
|
||||
*/
|
||||
private void enhanceConfigurationClasses() {
|
||||
|
||||
BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(true);
|
||||
|
||||
assertCglibIsPresent(configBeanDefs);
|
||||
|
||||
ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory);
|
||||
|
||||
for(String beanName : configBeanDefs.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
String configClassName = beanDef.getBeanClassName();
|
||||
String enhancedClassName = enhancer.enhance(configClassName);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug(format("Replacing bean definition '%s' existing class name '%s' "
|
||||
+ "with enhanced class name '%s'", beanName, configClassName, enhancedClassName));
|
||||
|
||||
beanDef.setBeanClassName(enhancedClassName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for the presence of CGLIB on the classpath by trying to
|
||||
* classload {@link #CGLIB_TEST_CLASS}.
|
||||
* @throws IllegalStateException if CGLIB is not present.
|
||||
*/
|
||||
private void assertCglibIsPresent(BeanDefinitionRegistry configBeanDefs) {
|
||||
try {
|
||||
Class.forName(CGLIB_TEST_CLASS);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("CGLIB is required to process @Configuration classes. " +
|
||||
"Either add CGLIB v2.2.3 to the classpath or remove the following " +
|
||||
"@Configuration bean definitions: ["
|
||||
+ StringUtils.arrayToCommaDelimitedString(configBeanDefs.getBeanDefinitionNames()) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the BeanDefinition's beanClass (or its ancestry) is
|
||||
* {@link Configuration}-annotated, false if no beanClass is specified.
|
||||
*/
|
||||
private static boolean isConfigurationClassBeanDefinition(BeanDefinition beanDef) {
|
||||
|
||||
String className = beanDef.getBeanClassName();
|
||||
|
||||
while (className != null && !(className.equals(Object.class.getName()))) {
|
||||
try {
|
||||
MetadataReader metadataReader =
|
||||
new SimpleMetadataReaderFactory().getMetadataReader(className);
|
||||
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
|
||||
ClassMetadata classMetadata = metadataReader.getClassMetadata();
|
||||
|
||||
if (annotationMetadata.hasAnnotation(Configuration.class.getName()))
|
||||
return true;
|
||||
|
||||
className = classMetadata.getSuperClassName();
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
import org.springframework.asm.ClassAdapter;
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
|
||||
/**
|
||||
* ASM {@link ClassVisitor} that visits a {@link Configuration} class, populating a
|
||||
* {@link ConfigurationClass} instance with information gleaned along the way.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see ConfigurationParser
|
||||
* @see ConfigurationClass
|
||||
*/
|
||||
class ConfigurationClassVisitor extends ClassAdapter {
|
||||
|
||||
private static final String OBJECT_DESC = convertClassNameToResourcePath(Object.class.getName());
|
||||
|
||||
private final ConfigurationClass configClass;
|
||||
private final ConfigurationModel model;
|
||||
private final ProblemReporter problemReporter;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
||||
|
||||
private boolean processInnerClasses = true;
|
||||
|
||||
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model,
|
||||
ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
this.configClass = configClass;
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setProcessInnerClasses(boolean processInnerClasses) {
|
||||
this.processInnerClasses = processInnerClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(String sourceFile, String debug) {
|
||||
String resourcePath =
|
||||
convertClassNameToResourcePath(configClass.getName())
|
||||
.substring(0, configClass.getName().lastIndexOf('.') + 1).concat(sourceFile);
|
||||
|
||||
configClass.setSource(resourcePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3,
|
||||
String superTypeDesc, String[] arg5) {
|
||||
visitSuperType(superTypeDesc);
|
||||
|
||||
configClass.setName(convertResourcePathToClassName(classTypeDesc));
|
||||
|
||||
// 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
|
||||
// eliminated in order to comply with java.lang.reflect.Modifier values.
|
||||
configClass.setModifiers(modifiers - Opcodes.ACC_SUPER);
|
||||
}
|
||||
|
||||
private void visitSuperType(String superTypeDesc) {
|
||||
// traverse up the type hierarchy unless the next ancestor is java.lang.Object
|
||||
if (OBJECT_DESC.equals(superTypeDesc))
|
||||
return;
|
||||
|
||||
ConfigurationClassVisitor visitor =
|
||||
new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader);
|
||||
|
||||
ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader);
|
||||
reader.accept(visitor, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a class level annotation on a {@link Configuration @Configuration} class.
|
||||
* Accounts for all possible class-level annotations that are respected by JavaConfig
|
||||
* including AspectJ's {@code @Aspect} annotation.
|
||||
* <p>
|
||||
* Upon encountering such an annotation, update the {@link #configClass} model object
|
||||
* appropriately, and then return an {@link AnnotationVisitor} implementation that can
|
||||
* populate the annotation appropriately with data.
|
||||
*
|
||||
* @see MutableAnnotation
|
||||
*/
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
|
||||
if (Configuration.class.getName().equals(annoTypeName)) {
|
||||
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class);
|
||||
configClass.setConfigurationAnnotation(mutableConfiguration);
|
||||
return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
|
||||
}
|
||||
|
||||
if (Import.class.getName().equals(annoTypeName)) {
|
||||
ImportStack importStack = ImportStackHolder.getImportStack();
|
||||
|
||||
if (!importStack.contains(configClass)) {
|
||||
importStack.push(configClass);
|
||||
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
|
||||
}
|
||||
|
||||
problemReporter.error(new CircularImportProblem(configClass, importStack));
|
||||
}
|
||||
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
String innerClassName = convertResourcePathToClassName(name);
|
||||
String configClassName = configClass.getName();
|
||||
|
||||
// if the innerClassName is equal to configClassName, we just
|
||||
// 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();
|
||||
|
||||
ConfigurationClassVisitor ccVisitor =
|
||||
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), problemReporter, classLoader);
|
||||
ccVisitor.setProcessInnerClasses(false);
|
||||
|
||||
ClassReader reader = AsmUtils.newClassReader(name, classLoader);
|
||||
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.getConfigurationAnnotation() != null)
|
||||
innerClasses.put(name, innerConfigClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link Problem} registered upon detection of a circular {@link Import}.
|
||||
*
|
||||
* @see Import
|
||||
* @see ImportStack
|
||||
* @see ImportStackHolder
|
||||
*/
|
||||
class CircularImportProblem extends Problem {
|
||||
|
||||
CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> importStack) {
|
||||
super(format("A circular @Import has been detected: " +
|
||||
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
|
||||
"already present in the current import stack [%s]",
|
||||
importStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
|
||||
attemptedImport.getSimpleName(), importStack),
|
||||
new Location(new ClassPathResource(convertClassNameToResourcePath(importStack.peek().getName())),
|
||||
importStack.peek().getSource())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.context.annotation.support.Util.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.sf.cglib.core.DefaultGeneratorStrategy;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.NoOp;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.asm.ClassAdapter;
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.ClassWriter;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Enhances {@link Configuration} classes by generating a CGLIB subclass capable of
|
||||
* interacting with the Spring container to respect bean semantics.
|
||||
*
|
||||
* @see #enhance(String)
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see ConfigurationClassPostProcessor
|
||||
*/
|
||||
class ConfigurationEnhancer {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ConfigurationEnhancer.class);
|
||||
|
||||
private final ArrayList<Callback> callbackInstances = new ArrayList<Callback>();
|
||||
private final ArrayList<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
|
||||
private final CallbackFilter callbackFilter;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConfigurationEnhancer} instance.
|
||||
*/
|
||||
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "beanFactory must be non-null");
|
||||
|
||||
callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
||||
callbackInstances.add(NoOp.INSTANCE);
|
||||
|
||||
for (Callback callback : callbackInstances)
|
||||
callbackTypes.add(callback.getClass());
|
||||
|
||||
// set up the callback filter to return the index of the BeanMethodInterceptor when
|
||||
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
||||
callbackFilter = new CallbackFilter() {
|
||||
public int accept(Method candidateMethod) {
|
||||
return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null) ? 0 : 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
// TODO: try to implement with modifications to AbstractAspectJAdvisorFactory#isAspect
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Represents the set of all user-defined {@link Configuration} classes. Once this model
|
||||
* is populated using a {@link ConfigurationParser}, it can be rendered out to a set of
|
||||
* {@link BeanDefinition} objects. This model provides an 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.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see ConfigurationClass
|
||||
* @see ConfigurationParser
|
||||
* @see ConfigurationModelBeanDefinitionReader
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
final class ConfigurationModel extends LinkedHashSet<ConfigurationClass> {
|
||||
|
||||
/**
|
||||
* Recurses through the model validating each {@link ConfigurationClass}.
|
||||
*
|
||||
* @param problemReporter {@link ProblemReporter} against which any validation errors
|
||||
* will be registered
|
||||
* @see ConfigurationClass#validate
|
||||
*/
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
for (ConfigurationClass configClass : this)
|
||||
configClass.validate(problemReporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format("%s containing @Configuration classes: %s", getClass().getSimpleName(), super.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
|
||||
import org.springframework.aop.scope.ScopedProxyFactoryBean;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Reads a given fully-populated {@link ConfigurationModel}, registering bean definitions
|
||||
* with the given {@link BeanDefinitionRegistry} based on its contents.
|
||||
* <p>
|
||||
* This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does not
|
||||
* implement/extend any of its artifacts as {@link ConfigurationModel} is not a
|
||||
* {@link Resource}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see ConfigurationModel
|
||||
* @see AbstractConfigurationClassProcessor#processConfigBeanDefinitions()
|
||||
*/
|
||||
class ConfigurationModelBeanDefinitionReader {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class);
|
||||
|
||||
private BeanDefinitionRegistry registry;
|
||||
|
||||
|
||||
/**
|
||||
* Reads {@code model}, registering bean definitions with {@link #registry} based on
|
||||
* its contents.
|
||||
*
|
||||
* @return number of bean definitions generated
|
||||
*/
|
||||
public BeanDefinitionRegistry loadBeanDefinitions(ConfigurationModel configurationModel) {
|
||||
registry = new SimpleBeanDefinitionRegistry();
|
||||
|
||||
for (ConfigurationClass configClass : configurationModel)
|
||||
loadBeanDefinitionsForConfigurationClass(configClass);
|
||||
|
||||
return registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a particular {@link ConfigurationClass}, registering bean definitions for the
|
||||
* class itself, all its {@link Bean} methods
|
||||
*/
|
||||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
||||
doLoadBeanDefinitionForConfigurationClass(configClass);
|
||||
|
||||
for (BeanMethod method : configClass.getBeanMethods())
|
||||
loadBeanDefinitionsForModelMethod(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the {@link Configuration} class itself as a bean definition.
|
||||
* @param beanDefs
|
||||
*/
|
||||
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
|
||||
|
||||
GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
|
||||
configBeanDef.setBeanClassName(configClass.getName());
|
||||
|
||||
String configBeanName = configClass.getBeanName();
|
||||
|
||||
// consider the case where it's already been defined (probably in XML)
|
||||
// and potentially has PropertyValues and ConstructorArgs)
|
||||
if (registry.containsBeanDefinition(configBeanName)) {
|
||||
if (log.isInfoEnabled())
|
||||
log.info(format("Copying property and constructor arg values from existing bean definition for "
|
||||
+ "@Configuration class %s to new bean definition", configBeanName));
|
||||
AbstractBeanDefinition existing = (AbstractBeanDefinition) registry.getBeanDefinition(configBeanName);
|
||||
configBeanDef.setPropertyValues(existing.getPropertyValues());
|
||||
configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues());
|
||||
configBeanDef.setResource(existing.getResource());
|
||||
}
|
||||
|
||||
if (log.isInfoEnabled())
|
||||
log.info(format("Registering bean definition for @Configuration class %s", configBeanName));
|
||||
|
||||
registry.registerBeanDefinition(configBeanName, configBeanDef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a particular {@link BeanMethod}, registering bean definitions with
|
||||
* {@link #registry} based on its contents.
|
||||
*/
|
||||
private void loadBeanDefinitionsForModelMethod(BeanMethod method) {
|
||||
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
|
||||
|
||||
ConfigurationClass configClass = method.getDeclaringClass();
|
||||
|
||||
beanDef.setFactoryBeanName(configClass.getBeanName());
|
||||
beanDef.setFactoryMethodName(method.getName());
|
||||
|
||||
Bean bean = method.getRequiredAnnotation(Bean.class);
|
||||
|
||||
// TODO: prune defaults
|
||||
//Configuration defaults = configClass.getMetadata();
|
||||
|
||||
// consider scoping
|
||||
Scope scope = method.getAnnotation(Scope.class);
|
||||
if(scope != null)
|
||||
beanDef.setScope(scope.value());
|
||||
|
||||
// consider name and any aliases
|
||||
ArrayList<String> names = new ArrayList<String>(Arrays.asList(bean.name()));
|
||||
String beanName = (names.size() > 0) ? names.remove(0) : method.getName();
|
||||
for (String alias : bean.name())
|
||||
registry.registerAlias(beanName, alias);
|
||||
|
||||
// has this already been overriden (i.e.: via XML)?
|
||||
if (containsBeanDefinitionIncludingAncestry(beanName, registry)) {
|
||||
BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry);
|
||||
|
||||
// is the existing bean definition one that was created by JavaConfig?
|
||||
if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
|
||||
// no -> then it's an external override, probably XML
|
||||
|
||||
// overriding is legal, return immediately
|
||||
log.info(format("Skipping loading bean definition for %s: a definition for bean "
|
||||
+ "'%s' already exists. This is likely due to an override in XML.", method, beanName));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: re-enable for Lazy support
|
||||
// // is this bean marked as primary for disambiguation?
|
||||
// if (bean.primary() == Primary.TRUE)
|
||||
// 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?
|
||||
String initMethodName = bean.initMethod();
|
||||
if (hasText(initMethodName))
|
||||
beanDef.setInitMethodName(initMethodName);
|
||||
|
||||
// does this bean have a custom destroy-method specified?
|
||||
String destroyMethodName = bean.destroyMethod();
|
||||
if (hasText(destroyMethodName))
|
||||
beanDef.setDestroyMethodName(destroyMethodName);
|
||||
|
||||
// is this method annotated with @Scope(scopedProxy=...)?
|
||||
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
|
||||
RootBeanDefinition targetDef = beanDef;
|
||||
|
||||
// Create a scoped proxy definition for the original bean name,
|
||||
// "hiding" the target bean in an internal target definition.
|
||||
String targetBeanName = resolveHiddenScopedProxyBeanName(beanName);
|
||||
RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
|
||||
scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
|
||||
|
||||
if (scope.proxyMode() == ScopedProxyMode.TARGET_CLASS)
|
||||
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;
|
||||
}
|
||||
|
||||
if (bean.dependsOn().length > 0)
|
||||
beanDef.setDependsOn(bean.dependsOn());
|
||||
|
||||
log.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) {
|
||||
if(!(registry instanceof ConfigurableListableBeanFactory))
|
||||
return registry.getBeanDefinition(beanName);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <i>hidden</i> name based on a scoped proxy bean name.
|
||||
*
|
||||
* @param originalBeanName the scope proxy bean name as declared in the
|
||||
* Configuration-annotated class
|
||||
*
|
||||
* @return the internally-used <i>hidden</i> bean name
|
||||
*/
|
||||
public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
|
||||
Assert.hasText(originalBeanName);
|
||||
return TARGET_NAME_PREFIX.concat(originalBeanName);
|
||||
}
|
||||
|
||||
/** Prefix used when registering the target object for a scoped proxy. */
|
||||
private static final String TARGET_NAME_PREFIX = "scopedTarget.";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition created
|
||||
* by JavaConfig as opposed to any other configuration source. Used in bean overriding cases
|
||||
* where it's necessary to determine whether the bean definition was created externally
|
||||
* (e.g. via XML).
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class ConfigurationClassBeanDefinition extends RootBeanDefinition {
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a {@link Configuration} class definition, populating a {@link ConfigurationModel}.
|
||||
* This ASM-based implementation avoids reflection and eager classloading in order to
|
||||
* interoperate effectively with tooling (Spring IDE) and OSGi environments.
|
||||
* <p>
|
||||
* This class helps separate the concern of parsing the structure of a Configuration class
|
||||
* from the concern of registering {@link BeanDefinition} objects based on the content of
|
||||
* that model.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see ConfigurationModel
|
||||
* @see ConfigurationModelBeanDefinitionReader
|
||||
*/
|
||||
public class ConfigurationParser {
|
||||
|
||||
/**
|
||||
* Model to be populated during calls to {@link #parse(Object, String)}
|
||||
*/
|
||||
private final ConfigurationModel model;
|
||||
private final ProblemReporter problemReporter;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConfigurationParser} instance that will be used to populate a
|
||||
* {@link ConfigurationModel}.
|
||||
*
|
||||
* @param model model to be populated by each successive call to {@link #parse}
|
||||
* @see #getConfigurationModel()
|
||||
*/
|
||||
public ConfigurationParser(ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
this.model = new ConfigurationModel();
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the {@link Configuration @Configuration} class encapsulated by
|
||||
* <var>configurationSource</var>.
|
||||
*
|
||||
* @param configurationSource reader for Configuration class being parsed
|
||||
* @param configurationId may be null, but if populated represents the bean id (assumes
|
||||
* that this configuration class was configured via XML)
|
||||
*/
|
||||
public void parse(String className, String configurationId) {
|
||||
|
||||
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
|
||||
|
||||
ClassReader configClassReader = AsmUtils.newClassReader(Util.getClassAsStream(resourcePath, classLoader));
|
||||
|
||||
ConfigurationClass configClass = new ConfigurationClass();
|
||||
configClass.setBeanName(configurationId);
|
||||
|
||||
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
||||
model.add(configClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current {@link ConfigurationModel}, to be called after {@link #parse}.
|
||||
*/
|
||||
public ConfigurationModel getConfigurationModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.context.annotation.support.AsmUtils.*;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* ASM {@link AnnotationVisitor} implementation that reads an {@link Import} annotation
|
||||
* for all its specified classes and then one by one processes each class with a new
|
||||
* {@link ConfigurationClassVisitor}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see Import
|
||||
* @see ImportStack
|
||||
* @see ImportStackHolder
|
||||
* @see ConfigurationClassVisitor
|
||||
*/
|
||||
class ImportAnnotationVisitor extends AnnotationAdapter {
|
||||
private final ArrayList<String> classesToImport = new ArrayList<String>();
|
||||
private final ConfigurationModel model;
|
||||
private final ProblemReporter problemReporter;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public ImportAnnotationVisitor(ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String attribName) {
|
||||
Assert.isTrue("value".equals(attribName),
|
||||
format("expected 'value' attribute, got unknown '%s' attribute", attribName));
|
||||
|
||||
return new AnnotationAdapter(AsmUtils.EMPTY_VISITOR) {
|
||||
@Override
|
||||
public void visit(String na, Object type) {
|
||||
Assert.isInstanceOf(Type.class, type);
|
||||
classesToImport.add(((Type) type).getClassName());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
for (String classToImport : classesToImport)
|
||||
processClassToImport(classToImport);
|
||||
|
||||
ImportStackHolder.getImportStack().pop();
|
||||
}
|
||||
|
||||
private void processClassToImport(String classToImport) {
|
||||
ConfigurationClass configClass = new ConfigurationClass();
|
||||
|
||||
ClassReader reader = newClassReader(convertClassNameToResourcePath(classToImport), classLoader);
|
||||
|
||||
reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
||||
|
||||
model.add(configClass);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* {@link Stack} used for detecting circular use of the {@link Import} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see Import
|
||||
* @see ImportStackHolder
|
||||
* @see CircularImportException
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class ImportStack extends Stack<ConfigurationClass> {
|
||||
|
||||
/**
|
||||
* Simplified contains() implementation that tests to see if any {@link ConfigurationClass}
|
||||
* exists within this stack that has the same name as <var>elem</var>. Elem must be of
|
||||
* type ConfigurationClass.
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(Object elem) {
|
||||
Assert.isInstanceOf(ConfigurationClass.class, elem);
|
||||
|
||||
ConfigurationClass configClass = (ConfigurationClass) elem;
|
||||
|
||||
Comparator<ConfigurationClass> comparator = new Comparator<ConfigurationClass>() {
|
||||
public int compare(ConfigurationClass first, ConfigurationClass second) {
|
||||
return first.getName().equals(second.getName()) ? 0 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
int index = Collections.binarySearch(this, configClass, comparator);
|
||||
|
||||
return index >= 0 ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a stack containing (in order)
|
||||
* <ol>
|
||||
* <li>com.acme.Foo</li>
|
||||
* <li>com.acme.Bar</li>
|
||||
* <li>com.acme.Baz</li>
|
||||
* </ol>
|
||||
* Returns "Foo->Bar->Baz". In the case of an empty stack, returns empty string.
|
||||
*/
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
Iterator<ConfigurationClass> iterator = this.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
builder.append(iterator.next().getSimpleName());
|
||||
if (iterator.hasNext())
|
||||
builder.append("->");
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
|
||||
/**
|
||||
* Holder class to expose a thread-bound {@link ImportStack}, used while detecting circular
|
||||
* declarations of the {@link Import} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see Import
|
||||
* @see ImportStack
|
||||
* @see CircularImportException
|
||||
*/
|
||||
class ImportStackHolder {
|
||||
|
||||
private static ThreadLocal<ImportStack> stackHolder = new ThreadLocal<ImportStack>() {
|
||||
@Override
|
||||
protected ImportStack initialValue() {
|
||||
return new ImportStack();
|
||||
}
|
||||
};
|
||||
|
||||
public static ImportStack getImportStack() {
|
||||
return stackHolder.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Holder for circular @Import detection stack";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a class, free from java reflection,
|
||||
* populated by {@link ConfigurationParser}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see ConfigurationModel
|
||||
* @see ConfigurationClass
|
||||
* @see ConfigurationParser
|
||||
*/
|
||||
class ModelClass implements BeanMetadataElement {
|
||||
|
||||
private String name;
|
||||
private boolean isInterface;
|
||||
private transient Object source;
|
||||
|
||||
/**
|
||||
* Creates a new and empty ModelClass instance.
|
||||
*/
|
||||
public ModelClass() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ModelClass instance
|
||||
*
|
||||
* @param name fully-qualified name of the class being represented
|
||||
*/
|
||||
public ModelClass(String name) {
|
||||
this(name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ModelClass instance
|
||||
*
|
||||
* @param name fully-qualified name of the class being represented
|
||||
* @param isInterface whether the represented type is an interface
|
||||
*/
|
||||
public ModelClass(String name, boolean isInterface) {
|
||||
this.name = name;
|
||||
this.isInterface = isInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully-qualified name of this class.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fully-qualified name of this class.
|
||||
*/
|
||||
public void setName(String className) {
|
||||
this.name = className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'.
|
||||
*/
|
||||
public String getSimpleName() {
|
||||
return name == null ? null : ClassUtils.getShortName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the class represented by this ModelClass instance is an interface.
|
||||
*/
|
||||
public boolean isInterface() {
|
||||
return isInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signifies that this class is (true) or is not (false) an interface.
|
||||
*/
|
||||
public void setInterface(boolean isInterface) {
|
||||
this.isInterface = isInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource path-formatted representation of the .java file that declares this
|
||||
* class
|
||||
*/
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void setSource(Object source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
if(getName() == null)
|
||||
throw new IllegalStateException("'name' property is null. Call setName() before calling getLocation()");
|
||||
return new Location(new ClassPathResource(convertClassNameToResourcePath(getName())), getSource());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a ModelClass instance representing a class com.acme.Foo, this method will
|
||||
* return
|
||||
*
|
||||
* <pre>
|
||||
* ModelClass: name=Foo
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s: name=%s", getClass().getSimpleName(), getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + (isInterface ? 1231 : 1237);
|
||||
result = (prime * result) + ((name == null) ? 0 : name.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;
|
||||
|
||||
ModelClass other = (ModelClass) obj;
|
||||
if (isInterface != other.isInterface)
|
||||
return false;
|
||||
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
/**
|
||||
* Note: the visibility of this interface would be reduced to package-private save for an
|
||||
* obscure restriction of JDK dynamic proxies.
|
||||
* {@link MutableAnnotationUtils#createMutableAnnotation(Class)} creates a proxy based on
|
||||
* two interfaces: this one, and whatever annotation is currently being parsed. The
|
||||
* restriction is that both interfaces may not be package-private if they are in separate
|
||||
* packages. In order to avoid unnecessarily restricting the visibility options for
|
||||
* user-defined annotations, this interface becomes public. Developers should take caution
|
||||
* not to use this annotation outside this package.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public interface MutableAnnotation {
|
||||
|
||||
void setAttributeValue(String attribName, Object attribValue);
|
||||
|
||||
Class<?> getAttributeType(String attributeName);
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.context.annotation.support.Util.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
|
||||
|
||||
/**
|
||||
* ASM {@link AnnotationVisitor} that visits any annotation array values while populating
|
||||
* a new {@link MutableAnnotation} instance.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see MutableAnnotation
|
||||
* @see MutableAnnotationUtils
|
||||
*/
|
||||
class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
||||
|
||||
private final ArrayList<Object> values = new ArrayList<Object>();
|
||||
private final MutableAnnotation mutableAnno;
|
||||
private final String attribName;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
|
||||
this.mutableAnno = mutableAnno;
|
||||
this.attribName = attribName;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String na, Object value) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
|
||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
|
||||
|
||||
if (annoType == null)
|
||||
return super.visitAnnotation(na, annoTypeDesc);
|
||||
|
||||
Annotation anno = createMutableAnnotation(annoType);
|
||||
values.add(anno);
|
||||
return new MutableAnnotationVisitor(anno, classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
Class<?> arrayType = mutableAnno.getAttributeType(attribName);
|
||||
Object[] array = (Object[]) Array.newInstance(arrayType.getComponentType(), 0);
|
||||
mutableAnno.setAttributeValue(attribName, values.toArray(array));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Handles calls to {@link MutableAnnotation} attribute methods at runtime. Essentially
|
||||
* emulates what JDK annotation dynamic proxies do.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see MutableAnnotation
|
||||
* @see MutableAnnotationUtils
|
||||
*/
|
||||
final class MutableAnnotationInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Class<? extends Annotation> annoType;
|
||||
private final HashMap<String, Object> attributes = new HashMap<String, Object>();
|
||||
private final HashMap<String, Class<?>> attributeTypes = new HashMap<String, Class<?>>();
|
||||
|
||||
public MutableAnnotationInvocationHandler(Class<? extends Annotation> annoType) {
|
||||
// pre-populate the attributes hash will all the names
|
||||
// and default values of the attributes defined in 'annoType'
|
||||
Method[] attribs = annoType.getDeclaredMethods();
|
||||
for (Method attrib : attribs) {
|
||||
this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName()));
|
||||
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName()));
|
||||
}
|
||||
|
||||
this.annoType = annoType;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
Assert.isInstanceOf(Annotation.class, proxy);
|
||||
|
||||
String methodName = method.getName();
|
||||
|
||||
// first -> check to see if this method is an attribute on our annotation
|
||||
if (attributes.containsKey(methodName))
|
||||
return attributes.get(methodName);
|
||||
|
||||
|
||||
// second -> is it a method from java.lang.annotation.Annotation?
|
||||
if (methodName.equals("annotationType"))
|
||||
return annoType;
|
||||
|
||||
|
||||
// third -> is it a method from java.lang.Object?
|
||||
if (methodName.equals("toString"))
|
||||
return format("@%s(%s)", annoType.getName(), getAttribs());
|
||||
|
||||
if (methodName.equals("equals"))
|
||||
return isEqualTo(proxy, args[0]);
|
||||
|
||||
if (methodName.equals("hashCode"))
|
||||
return calculateHashCode(proxy);
|
||||
|
||||
|
||||
// finally -> is it a method specified by MutableAnno?
|
||||
if (methodName.equals("setAttributeValue")) {
|
||||
attributes.put((String) args[0], args[1]);
|
||||
return null; // setAttributeValue has a 'void' return type
|
||||
}
|
||||
|
||||
if (methodName.equals("getAttributeType"))
|
||||
return attributeTypes.get(args[0]);
|
||||
|
||||
throw new UnsupportedOperationException("this proxy does not support method: " + methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conforms to the hashCode() specification for Annotation.
|
||||
*
|
||||
* @see Annotation#hashCode()
|
||||
*/
|
||||
private Object calculateHashCode(Object proxy) {
|
||||
int sum = 0;
|
||||
|
||||
for (String attribName : attributes.keySet()) {
|
||||
Object attribValue = attributes.get(attribName);
|
||||
|
||||
final int attribNameHashCode = attribName.hashCode();
|
||||
final int attribValueHashCode;
|
||||
|
||||
if (attribValue == null)
|
||||
// memberValue may be null when a mutable annotation is being added to a
|
||||
// collection
|
||||
// and before it has actually been visited (and populated) by
|
||||
// MutableAnnotationVisitor
|
||||
attribValueHashCode = 0;
|
||||
else if (attribValue.getClass().isArray())
|
||||
attribValueHashCode = Arrays.hashCode((Object[]) attribValue);
|
||||
else
|
||||
attribValueHashCode = attribValue.hashCode();
|
||||
|
||||
sum += (127 * attribNameHashCode) ^ attribValueHashCode;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares <var>proxy</var> object and <var>other</var> object by comparing the return
|
||||
* values of the methods specified by their common {@link Annotation} ancestry.
|
||||
* <p/>
|
||||
* <var>other</var> must be the same type as or a subtype of <var>proxy</var>. Will
|
||||
* return false otherwise.
|
||||
* <p/>
|
||||
* Eagerly returns true if {@code proxy} == <var>other</var>
|
||||
* </p>
|
||||
* <p/>
|
||||
* Conforms strictly to the equals() specification for Annotation
|
||||
* </p>
|
||||
*
|
||||
* @see Annotation#equals(Object)
|
||||
*/
|
||||
private Object isEqualTo(Object proxy, Object other) {
|
||||
if (proxy == other)
|
||||
return true;
|
||||
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
if (!annoType.isAssignableFrom(other.getClass()))
|
||||
return false;
|
||||
|
||||
for (String attribName : attributes.keySet()) {
|
||||
Object thisVal;
|
||||
Object thatVal;
|
||||
|
||||
try {
|
||||
thisVal = attributes.get(attribName);
|
||||
thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
if ((thisVal == null) && (thatVal != null))
|
||||
return false;
|
||||
|
||||
if ((thatVal == null) && (thisVal != null))
|
||||
return false;
|
||||
|
||||
if (thatVal.getClass().isArray()) {
|
||||
if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) {
|
||||
return false;
|
||||
}
|
||||
} else if (thisVal instanceof Double) {
|
||||
if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal)))
|
||||
return false;
|
||||
} else if (thisVal instanceof Float) {
|
||||
if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal)))
|
||||
return false;
|
||||
} else if (!thisVal.equals(thatVal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getAttribs() {
|
||||
ArrayList<String> attribs = new ArrayList<String>();
|
||||
|
||||
for (String attribName : attributes.keySet())
|
||||
attribs.add(format("%s=%s", attribName, attributes.get(attribName)));
|
||||
|
||||
return StringUtils.collectionToDelimitedString(attribs, ", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the type of the given annotation attribute.
|
||||
*/
|
||||
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) {
|
||||
Method method = null;
|
||||
|
||||
try {
|
||||
method = annotationType.getDeclaredMethod(attributeName);
|
||||
} catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
|
||||
return method.getReturnType();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
|
||||
class MutableAnnotationUtils {
|
||||
|
||||
/**
|
||||
* Creates a {@link MutableAnnotation} for {@code annoType}. JDK dynamic proxies are
|
||||
* used, and the returned proxy implements both {@link MutableAnnotation} and annotation
|
||||
* type {@code A}
|
||||
*
|
||||
* @param <A> annotation type that must be supplied and returned
|
||||
* @param annoType type of annotation to create
|
||||
*/
|
||||
public static <A extends Annotation> A createMutableAnnotation(Class<A> annoType) {
|
||||
MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType);
|
||||
ClassLoader classLoader = MutableAnnotationUtils.class.getClassLoader();
|
||||
Class<?>[] interfaces = new Class<?>[] { annoType, MutableAnnotation.class };
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
||||
return mutableAnno;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.context.annotation.support.Util.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* ASM {@link AnnotationVisitor} that populates a given {@link MutableAnnotation} instance
|
||||
* with its attributes.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see MutableAnnotation
|
||||
* @see MutableAnnotationInvocationHandler
|
||||
* @see MutableAnnotationUtils
|
||||
*/
|
||||
class MutableAnnotationVisitor implements AnnotationVisitor {
|
||||
|
||||
protected final MutableAnnotation mutableAnno;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* 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, ClassLoader classLoader) {
|
||||
Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable");
|
||||
this.mutableAnno = (MutableAnnotation) mutableAnno;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitArray(final String attribName) {
|
||||
return new MutableAnnotationArrayVisitor(mutableAnno, attribName, classLoader);
|
||||
}
|
||||
|
||||
public void visit(String attribName, Object attribValue) {
|
||||
Class<?> attribReturnType = mutableAnno.getAttributeType(attribName);
|
||||
|
||||
if (attribReturnType.equals(Class.class)) {
|
||||
// the attribute type is Class -> load it and set it.
|
||||
String fqClassName = ((Type) attribValue).getClassName();
|
||||
|
||||
Class<?> classVal = loadToolingSafeClass(fqClassName, classLoader);
|
||||
|
||||
if (classVal == null)
|
||||
return;
|
||||
|
||||
mutableAnno.setAttributeValue(attribName, classVal);
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, assume the value can be set literally
|
||||
mutableAnno.setAttributeValue(attribName, attribValue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
|
||||
String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor);
|
||||
|
||||
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName, classLoader);
|
||||
|
||||
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, classLoader);
|
||||
|
||||
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, classLoader);
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import sun.security.x509.Extension;
|
||||
|
||||
|
||||
/**
|
||||
* Misc utils
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
class Util {
|
||||
|
||||
private static final Log log = LogFactory.getLog(Util.class);
|
||||
|
||||
private Util() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of type T by invoking its default or no-arg constructor.
|
||||
* <p>
|
||||
* Any reflection-related issues are re-thrown as unchecked.
|
||||
*/
|
||||
public static <T> T getInstance(Class<? extends T> clazz) {
|
||||
try {
|
||||
Constructor<? extends T> noArgCtor = clazz.getDeclaredConstructor();
|
||||
ReflectionUtils.makeAccessible(noArgCtor);
|
||||
return noArgCtor.newInstance();
|
||||
} catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
throw new IllegalStateException(format("Unexpected reflection exception - %s: %s", ex.getClass()
|
||||
.getName(), ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified class using the default class loader, gracefully handling any
|
||||
* {@link ClassNotFoundException} that may be thrown. This functionality is specifically
|
||||
* 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
|
||||
*
|
||||
* @return newly loaded class instance, null if class could not be found
|
||||
*
|
||||
* @see #loadRequiredClass(String)
|
||||
* @see #loadToolingSafeClass(String)
|
||||
* @see ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> loadClass(String fqClassName) {
|
||||
try {
|
||||
return (Class<? extends T>) ClassUtils.getDefaultClassLoader().loadClass(fqClassName);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified class using the default class loader, rethrowing any
|
||||
* {@link ClassNotFoundException} as an unchecked exception.
|
||||
*
|
||||
* @param <T> type of class to be returned
|
||||
* @param fqClassName fully-qualified class name
|
||||
*
|
||||
* @return newly loaded class instance
|
||||
*
|
||||
* @throws IllegalArgumentException if configClassName cannot be loaded.
|
||||
*
|
||||
* @see #loadClass(String)
|
||||
* @see #loadToolingSafeClass(String)
|
||||
* @see ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> loadRequiredClass(String fqClassName) {
|
||||
try {
|
||||
return (Class<? extends T>) getDefaultClassLoader().loadClass(fqClassName);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new IllegalArgumentException(format(
|
||||
"Class [%s] could not be loaded, check your CLASSPATH.", fqClassName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 and return null. This functionality is specifically implemented to
|
||||
* accomodate tooling (Spring IDE) concerns, where user-defined types will not be
|
||||
* available to the tooling.
|
||||
* <p>
|
||||
* ASM class reading is used throughout JavaConfig, but there are certain cases where
|
||||
* classloading cannot be avoided - specifically in cases where users define their own
|
||||
* {@link Extension} annotations. This method should therefore be
|
||||
* used sparingly but consistently where required.
|
||||
* <p>
|
||||
* Because {@link ClassNotFoundException} is compensated for by returning null, callers
|
||||
* must take care to handle the null case appropriately.
|
||||
* <p>
|
||||
* In cases where the WARN logging statement is not desired, use the
|
||||
* {@link #loadClass(String)} method, which returns null but issues no logging
|
||||
* statements.
|
||||
* <p>
|
||||
* This method should only ever return null in the case of a user-defined type be
|
||||
* 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
|
||||
* respecting them at actual runtime.
|
||||
*
|
||||
* @param <T> type of class to be returned
|
||||
* @param fqClassName fully-qualified class name
|
||||
*
|
||||
* @return newly loaded class, null if class could not be found.
|
||||
*
|
||||
* @see #loadClass(String)
|
||||
* @see #loadRequiredClass(String)
|
||||
* @see ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName, ClassLoader classLoader) {
|
||||
try {
|
||||
return (Class<? extends T>) classLoader.loadClass(fqClassName);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
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' to
|
||||
* pathToClass before attempting to load.
|
||||
*
|
||||
* @param pathToClass resource path to class, not including .class suffix. e.g.:
|
||||
* com/acme/MyClass
|
||||
*
|
||||
* @return inputStream for <var>pathToClass</var>
|
||||
*
|
||||
* @throws RuntimeException if <var>pathToClass</var> does not exist
|
||||
*/
|
||||
public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) {
|
||||
String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX;
|
||||
|
||||
InputStream is = classLoader.getResourceAsStream(classFileName);
|
||||
|
||||
if (is == null)
|
||||
throw new RuntimeException(
|
||||
new FileNotFoundException("Class file [" + classFileName + "] not found"));
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
|
||||
|
||||
/**
|
||||
* Covers the somewhat unlilely case of a {@link Configuration} class being declared
|
||||
* as an abstract {@link BeanDefinition}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see BeanDefinition#isAbstract()
|
||||
*/
|
||||
public class AbstractBeanDefinitionConfigurationClassTests {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void abstractConfigurationClassBeanDefinitionsAreIgnored() {
|
||||
@Configuration class Abstract { @Bean Object foo1() { return null; } }
|
||||
@Configuration class Concrete { @Bean Object foo2() { return null; } }
|
||||
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
factory.registerBeanDefinition("abstract",
|
||||
rootBeanDefinition(Abstract.class).setAbstract(true).getBeanDefinition());
|
||||
factory.registerBeanDefinition("concrete",
|
||||
rootBeanDefinition(Concrete.class).setAbstract(false).getBeanDefinition());
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
|
||||
|
||||
assertTrue("abstract configuration should be CGLIB-enhanced",
|
||||
AopUtils.isCglibProxyClassName(factory.getBeanDefinition("abstract").getBeanClassName()));
|
||||
assertTrue("concrete configuration should be CGLIB-enhanced",
|
||||
AopUtils.isCglibProxyClassName(factory.getBeanDefinition("concrete").getBeanClassName()));
|
||||
|
||||
assertFalse("abstract configuration's @Bean method should not be registered",
|
||||
factory.containsBeanDefinition("foo1"));
|
||||
assertTrue("concrete configuration's @Bean method should be registered",
|
||||
factory.containsBeanDefinition("foo2"));
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
|
||||
public abstract class AbstractJavaConfigTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
}
|
||||
|
||||
// protected
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.XmlBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
|
||||
public class AspectTests {
|
||||
private void assertAdviceWasApplied(Class<?> configClass) {
|
||||
GenericApplicationContext ctx = new GenericApplicationContext(
|
||||
new XmlBeanFactory(new ClassPathResource("aspectj-autoproxy-config.xml", AspectTests.class)));
|
||||
ctx.addBeanFactoryPostProcessor(new ConfigurationClassPostProcessor());
|
||||
ctx.registerBeanDefinition("config", new RootBeanDefinition(configClass));
|
||||
ctx.refresh();
|
||||
|
||||
TestBean testBean = ctx.getBean("testBean", TestBean.class);
|
||||
assertThat(testBean.getName(), equalTo("name"));
|
||||
testBean.absquatulate();
|
||||
assertThat(testBean.getName(), equalTo("advisedName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aspectAnnotatedConfiguration() {
|
||||
assertAdviceWasApplied(AspectConfig.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationIncludesAspect() {
|
||||
assertAdviceWasApplied(ConfigurationWithAspect.class);
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
@Configuration
|
||||
static class AspectConfig {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean("name");
|
||||
}
|
||||
|
||||
@Before("execution(* test.beans.TestBean.absquatulate(..)) && target(testBean)")
|
||||
public void touchBean(TestBean testBean) {
|
||||
testBean.setName("advisedName");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigurationWithAspect {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean("name");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NameChangingAspect nameChangingAspect() {
|
||||
return new NameChangingAspect();
|
||||
}
|
||||
}
|
||||
|
||||
@Aspect
|
||||
static class NameChangingAspect {
|
||||
@Before("execution(* test.beans.TestBean.absquatulate(..)) && target(testBean)")
|
||||
public void touchBean(TestBean testBean) {
|
||||
testBean.setName("advisedName");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import test.beans.Colour;
|
||||
import test.beans.TestBean;
|
||||
|
||||
|
||||
public class AutowiredConfigurationTests {
|
||||
public @Test
|
||||
void test() {
|
||||
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext(
|
||||
AutowiredConfigurationTests.class.getSimpleName() + ".xml", AutowiredConfigurationTests.class);
|
||||
|
||||
assertThat(factory.getBean("colour", Colour.class), equalTo(Colour.RED));
|
||||
assertThat(factory.getBean("testBean", TestBean.class).getName(), equalTo(Colour.RED.toString()));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AutowiredConfig {
|
||||
private @Autowired
|
||||
Colour colour;
|
||||
|
||||
public @Bean
|
||||
TestBean testBean() {
|
||||
return new TestBean(colour.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ColorConfig {
|
||||
public @Bean
|
||||
Colour colour() {
|
||||
return Colour.RED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public @Test
|
||||
void testValueInjection() {
|
||||
System.setProperty("myProp", "foo");
|
||||
|
||||
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext(
|
||||
"ValueInjectionTests.xml", AutowiredConfigurationTests.class);
|
||||
|
||||
TestBean testBean = factory.getBean("testBean", TestBean.class);
|
||||
assertThat(testBean.getName(), equalTo("foo"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ValueConfig {
|
||||
|
||||
@Value("#{systemProperties.myProp}")
|
||||
private String name = "default";
|
||||
|
||||
public @Bean
|
||||
TestBean testBean() {
|
||||
return new TestBean(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
|
||||
xmlns:context="http://www.springframework.org/schema/context">
|
||||
|
||||
<context:annotation-config/>
|
||||
|
||||
<bean class="org.springframework.context.annotation.support.ConfigurationClassPostProcessor"/>
|
||||
|
||||
<bean class="org.springframework.context.annotation.configuration.AutowiredConfigurationTests$AutowiredConfig"/>
|
||||
<bean class="org.springframework.context.annotation.configuration.AutowiredConfigurationTests$ColorConfig"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,159 +0,0 @@
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.StandardScopes;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
|
||||
public class BasicTests {
|
||||
|
||||
/**
|
||||
* 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 the factory using JavaConfig's {@link ConfigurationClassPostProcessor}.
|
||||
* When complete, the factory is ready to service requests for any {@link Bean} methods
|
||||
* declared by <var>configClasses</var>.
|
||||
*
|
||||
* @param configClasses the {@link Configuration} classes under test. may be an empty
|
||||
* list.
|
||||
*
|
||||
* @return fully initialized and post-processed {@link BeanFactory}
|
||||
*/
|
||||
private static BeanFactory initBeanFactory(Class<?>... configClasses) {
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
|
||||
for (Class<?> configClass : configClasses) {
|
||||
String configBeanName = configClass.getName();
|
||||
factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass).getBeanDefinition());
|
||||
}
|
||||
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
|
||||
|
||||
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customBeanNameIsRespected() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithBeanWithCustomName.class);
|
||||
assertSame(factory.getBean("customName"), ConfigWithBeanWithCustomName.testBean);
|
||||
|
||||
// method name should not be registered
|
||||
try {
|
||||
factory.getBean("methodName");
|
||||
fail("bean should not have been registered with 'methodName'");
|
||||
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithBeanWithCustomName {
|
||||
static TestBean testBean = new TestBean();
|
||||
@Bean(name="customName")
|
||||
public TestBean methodName() {
|
||||
return testBean;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aliasesAreRespected() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithBeanWithAliases.class);
|
||||
assertSame(factory.getBean("name1"), ConfigWithBeanWithAliases.testBean);
|
||||
String[] aliases = factory.getAliases("name1");
|
||||
for(String alias : aliases)
|
||||
assertSame(factory.getBean(alias), ConfigWithBeanWithAliases.testBean);
|
||||
|
||||
// method name should not be registered
|
||||
try {
|
||||
factory.getBean("methodName");
|
||||
fail("bean should not have been registered with 'methodName'");
|
||||
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithBeanWithAliases {
|
||||
static TestBean testBean = new TestBean();
|
||||
@Bean(name={"name1", "alias1", "alias2", "alias3"})
|
||||
public TestBean methodName() {
|
||||
return testBean;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
public void testFinalBeanMethod() {
|
||||
initBeanFactory(ConfigWithFinalBean.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithFinalBean {
|
||||
public final @Bean TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simplestPossibleConfiguration() {
|
||||
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
|
||||
|
||||
String stringBean = factory.getBean("stringBean", String.class);
|
||||
|
||||
assertThat(stringBean, equalTo("foo"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SimplestPossibleConfig {
|
||||
public @Bean String stringBean() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void configurationWithPrototypeScopedBeans() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
|
||||
|
||||
TestBean foo = factory.getBean("foo", TestBean.class);
|
||||
ITestBean bar = factory.getBean("bar", ITestBean.class);
|
||||
ITestBean baz = factory.getBean("baz", ITestBean.class);
|
||||
|
||||
assertThat(foo.getSpouse(), sameInstance(bar));
|
||||
assertThat(bar.getSpouse(), not(sameInstance(baz)));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithPrototypeBean {
|
||||
public @Bean TestBean foo() {
|
||||
TestBean foo = new TestBean("foo");
|
||||
foo.setSpouse(bar());
|
||||
return foo;
|
||||
}
|
||||
|
||||
public @Bean TestBean bar() {
|
||||
TestBean bar = new TestBean("bar");
|
||||
bar.setSpouse(baz());
|
||||
return bar;
|
||||
}
|
||||
|
||||
@Bean @Scope(StandardScopes.PROTOTYPE)
|
||||
public TestBean baz() {
|
||||
return new TestBean("bar");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
|
||||
/**
|
||||
* System tests for {@link Import} annotation support.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ImportTests {
|
||||
|
||||
private DefaultListableBeanFactory processConfigurationClasses(Class<?>... classes) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
for (Class<?> clazz : classes)
|
||||
beanFactory.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
private void assertBeanDefinitionCount(int expectedCount, Class<?>... classes) {
|
||||
DefaultListableBeanFactory beanFactory = processConfigurationClasses(classes);
|
||||
assertThat(beanFactory.getBeanDefinitionCount(), equalTo(expectedCount));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessImports() {
|
||||
int configClasses = 2;
|
||||
int beansInClasses = 2;
|
||||
|
||||
assertBeanDefinitionCount((configClasses + beansInClasses), ConfigurationWithImportAnnotation.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(OtherConfiguration.class)
|
||||
static class ConfigurationWithImportAnnotation {
|
||||
@Bean
|
||||
public ITestBean one() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OtherConfiguration {
|
||||
@Bean
|
||||
public ITestBean two() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void testImportAnnotationWithTwoLevelRecursion() {
|
||||
int configClasses = 2;
|
||||
int beansInClasses = 3;
|
||||
|
||||
assertBeanDefinitionCount((configClasses + beansInClasses), AppConfig.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(DataSourceConfig.class)
|
||||
static class AppConfig {
|
||||
|
||||
@Bean
|
||||
public ITestBean transferService() {
|
||||
return new TestBean(accountRepository());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean accountRepository() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DataSourceConfig {
|
||||
@Bean
|
||||
public ITestBean dataSourceA() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void testImportAnnotationWithThreeLevelRecursion() {
|
||||
int configClasses = 3;
|
||||
int beansInClasses = 5;
|
||||
|
||||
assertBeanDefinitionCount((configClasses + beansInClasses), FirstLevel.class);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void testImportAnnotationWithMultipleArguments() {
|
||||
int configClasses = 3;
|
||||
int beansInClasses = 3;
|
||||
|
||||
assertBeanDefinitionCount((configClasses + beansInClasses),
|
||||
WithMultipleArgumentsToImportAnnotation.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testImportAnnotationWithMultipleArgumentsResultingInOverriddenBeanDefinition() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(
|
||||
WithMultipleArgumentsThatWillCauseDuplication.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
assertThat(beanFactory.getBeanDefinitionCount(), equalTo(4));
|
||||
assertThat(beanFactory.getBean("foo", ITestBean.class).getName(), equalTo("foo2"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import( { Foo1.class, Foo2.class })
|
||||
static class WithMultipleArgumentsThatWillCauseDuplication {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Foo1 {
|
||||
@Bean
|
||||
public ITestBean foo() {
|
||||
return new TestBean("foo1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Foo2 {
|
||||
@Bean
|
||||
public ITestBean foo() {
|
||||
return new TestBean("foo2");
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void testImportAnnotationOnInnerClasses() {
|
||||
int configClasses = 2;
|
||||
int beansInClasses = 2;
|
||||
|
||||
assertBeanDefinitionCount((configClasses + beansInClasses), OuterConfig.InnerConfig.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OuterConfig {
|
||||
@Bean
|
||||
String whatev() {
|
||||
return "whatev";
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(ExternalConfig.class)
|
||||
static class InnerConfig {
|
||||
@Bean
|
||||
public ITestBean innerBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ExternalConfig {
|
||||
@Bean
|
||||
public ITestBean extBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Configuration
|
||||
@Import(SecondLevel.class)
|
||||
static class FirstLevel {
|
||||
@Bean
|
||||
public TestBean m() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(ThirdLevel.class)
|
||||
static class SecondLevel {
|
||||
@Bean
|
||||
public TestBean n() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ThirdLevel {
|
||||
@Bean
|
||||
public ITestBean thirdLevelA() {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean thirdLevelB() {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean thirdLevelC() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import( { LeftConfig.class, RightConfig.class })
|
||||
static class WithMultipleArgumentsToImportAnnotation {
|
||||
@Bean
|
||||
public TestBean m() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class LeftConfig {
|
||||
@Bean
|
||||
public ITestBean left() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RightConfig {
|
||||
@Bean
|
||||
public ITestBean right() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
public void testImportNonConfigurationAnnotationClassCausesError() {
|
||||
processConfigurationClasses(ConfigAnnotated.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(NonConfigAnnotated.class)
|
||||
static class ConfigAnnotated { }
|
||||
|
||||
static class NonConfigAnnotated { }
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import java.lang.annotation.Inherited;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests that polymorphic Configuration classes need not explicitly redeclare the
|
||||
* {@link Configuration} annotation. This respects the {@link Inherited} nature
|
||||
* of the Configuration annotation, even though it's being detected via ASM.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class PolymorphicConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void subclassNeedNotDeclareConfigurationAnnotation() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
|
||||
beanFactory.getBean("testBean", TestBean.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SuperConfig {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
static class Config extends SuperConfig { }
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.scope.ScopedObject;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.context.annotation.StandardScopes;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
import test.beans.CustomScope;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tests that scopes are properly supported by using a custom Scope implementations
|
||||
* and scoped proxy {@link Bean} declarations.
|
||||
*
|
||||
* @see ScopeIntegrationTests
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ScopingTests {
|
||||
|
||||
public static String flag = "1";
|
||||
|
||||
private static final String SCOPE = "my scope";
|
||||
private CustomScope customScope;
|
||||
private GenericApplicationContext ctx;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
customScope = new CustomScope();
|
||||
ctx = createContext(customScope, ScopedConfigurationClass.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
ctx.close();
|
||||
ctx = null;
|
||||
customScope = null;
|
||||
}
|
||||
|
||||
private GenericApplicationContext createContext(org.springframework.beans.factory.config.Scope customScope, Class<?> configClass) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
if(customScope != null)
|
||||
beanFactory.registerScope(SCOPE, customScope);
|
||||
beanFactory.registerBeanDefinition("config",
|
||||
rootBeanDefinition(configClass).getBeanDefinition());
|
||||
GenericApplicationContext ctx = new GenericApplicationContext(beanFactory);
|
||||
ctx.addBeanFactoryPostProcessor(new ConfigurationClassPostProcessor());
|
||||
ctx.refresh();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScopeOnClasses() throws Exception {
|
||||
genericTestScope("scopedClass");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScopeOnInterfaces() throws Exception {
|
||||
genericTestScope("scopedInterface");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSameScopeOnDifferentBeans() throws Exception {
|
||||
Object beanAInScope = ctx.getBean("scopedClass");
|
||||
Object beanBInScope = ctx.getBean("scopedInterface");
|
||||
|
||||
assertNotSame(beanAInScope, beanBInScope);
|
||||
|
||||
customScope.createNewScope = true;
|
||||
|
||||
Object newBeanAInScope = ctx.getBean("scopedClass");
|
||||
Object newBeanBInScope = ctx.getBean("scopedInterface");
|
||||
|
||||
assertNotSame(newBeanAInScope, newBeanBInScope);
|
||||
assertNotSame(newBeanAInScope, beanAInScope);
|
||||
assertNotSame(newBeanBInScope, beanBInScope);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScopedProxyOnSingletonBeanMethod() throws Exception {
|
||||
// should throw - scoped proxies should not be applied on singleton/prototype beans
|
||||
try {
|
||||
createContext(null, InvalidProxyOnPredefinedScopesConfiguration.class);
|
||||
fail("exception expected");
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue(ex.getMessage().contains("scoped proxies cannot be created for singleton/prototype beans"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRawScopes() throws Exception {
|
||||
|
||||
String beanName = "scopedProxyInterface";
|
||||
|
||||
// get hidden bean
|
||||
Object bean = ctx.getBean("scopedTarget." + beanName);
|
||||
|
||||
assertFalse(bean instanceof ScopedObject);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScopedProxyConfiguration() throws Exception {
|
||||
|
||||
TestBean singleton = (TestBean) ctx.getBean("singletonWithScopedInterfaceDep");
|
||||
ITestBean spouse = singleton.getSpouse();
|
||||
assertTrue("scoped bean is not wrapped by the scoped-proxy", spouse instanceof ScopedObject);
|
||||
|
||||
String beanName = "scopedProxyInterface";
|
||||
|
||||
String scopedBeanName = "scopedTarget." + beanName;
|
||||
|
||||
// get hidden bean
|
||||
assertEquals(flag, spouse.getName());
|
||||
|
||||
ITestBean spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName);
|
||||
assertEquals(spouse.getName(), spouseFromBF.getName());
|
||||
// the scope proxy has kicked in
|
||||
assertNotSame(spouse, spouseFromBF);
|
||||
|
||||
// create a new bean
|
||||
customScope.createNewScope = true;
|
||||
|
||||
// get the bean again from the BF
|
||||
spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName);
|
||||
// make sure the name has been updated
|
||||
assertSame(spouse.getName(), spouseFromBF.getName());
|
||||
assertNotSame(spouse, spouseFromBF);
|
||||
|
||||
// get the bean again
|
||||
spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName);
|
||||
assertSame(spouse.getName(), spouseFromBF.getName());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScopedProxyConfigurationWithClasses() throws Exception {
|
||||
|
||||
TestBean singleton = (TestBean) ctx.getBean("singletonWithScopedClassDep");
|
||||
ITestBean spouse = singleton.getSpouse();
|
||||
assertTrue("scoped bean is not wrapped by the scoped-proxy", spouse instanceof ScopedObject);
|
||||
|
||||
String beanName = "scopedProxyClass";
|
||||
|
||||
String scopedBeanName = "scopedTarget." + beanName;
|
||||
|
||||
// get hidden bean
|
||||
assertEquals(flag, spouse.getName());
|
||||
|
||||
TestBean spouseFromBF = (TestBean) ctx.getBean(scopedBeanName);
|
||||
assertEquals(spouse.getName(), spouseFromBF.getName());
|
||||
// the scope proxy has kicked in
|
||||
assertNotSame(spouse, spouseFromBF);
|
||||
|
||||
// create a new bean
|
||||
customScope.createNewScope = true;
|
||||
flag = "boo";
|
||||
|
||||
// get the bean again from the BF
|
||||
spouseFromBF = (TestBean) ctx.getBean(scopedBeanName);
|
||||
// make sure the name has been updated
|
||||
assertSame(spouse.getName(), spouseFromBF.getName());
|
||||
assertNotSame(spouse, spouseFromBF);
|
||||
|
||||
// get the bean again
|
||||
spouseFromBF = (TestBean) ctx.getBean(scopedBeanName);
|
||||
assertSame(spouse.getName(), spouseFromBF.getName());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScopedConfigurationBeanDefinitionCount() throws Exception {
|
||||
|
||||
// count the beans
|
||||
// 6 @Beans + 1 Configuration + 2 scoped proxy
|
||||
assertThat(ctx.getBeanDefinitionCount(), equalTo(9));
|
||||
}
|
||||
|
||||
// /**
|
||||
// * SJC-254 caught a regression in handling scoped proxies starting in 1.0 m4.
|
||||
// * The ScopedProxyFactoryBean object was having its scope set to that of its delegate
|
||||
// * whereas it should have remained singleton.
|
||||
// */
|
||||
// @Test
|
||||
// public void sjc254() {
|
||||
// JavaConfigWebApplicationContext ctx = new JavaConfigWebApplicationContext();
|
||||
// ctx.setConfigLocations(new String[] { ScopeTestConfiguration.class.getName() });
|
||||
// ctx.refresh();
|
||||
//
|
||||
// // should be fine
|
||||
// ctx.getBean(Bar.class);
|
||||
//
|
||||
// boolean threw = false;
|
||||
// try {
|
||||
// ctx.getBean(Foo.class);
|
||||
// } catch (BeanCreationException ex) {
|
||||
// if(ex.getCause() instanceof IllegalStateException) {
|
||||
// threw = true;
|
||||
// }
|
||||
// }
|
||||
// assertTrue(threw);
|
||||
// }
|
||||
|
||||
@Configuration
|
||||
static class ScopeTestConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope(value=StandardScopes.SESSION, proxyMode=ScopedProxyMode.INTERFACES)
|
||||
public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
|
||||
static class Foo {
|
||||
public Foo() {
|
||||
//System.out.println("created foo: " + this.getClass().getName());
|
||||
}
|
||||
|
||||
public void doSomething() {
|
||||
//System.out.println("interesting: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
static class Bar {
|
||||
|
||||
private final Foo foo;
|
||||
|
||||
public Bar(Foo foo) {
|
||||
this.foo = foo;
|
||||
//System.out.println("created bar: " + this);
|
||||
}
|
||||
|
||||
public Foo getFoo() {
|
||||
return foo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void genericTestScope(String beanName) throws Exception {
|
||||
String message = "scope is ignored";
|
||||
Object bean1 = ctx.getBean(beanName);
|
||||
Object bean2 = ctx.getBean(beanName);
|
||||
|
||||
assertSame(message, bean1, bean2);
|
||||
|
||||
Object bean3 = ctx.getBean(beanName);
|
||||
|
||||
assertSame(message, bean1, bean3);
|
||||
|
||||
// make the scope create a new object
|
||||
customScope.createNewScope = true;
|
||||
|
||||
Object newBean1 = ctx.getBean(beanName);
|
||||
assertNotSame(message, bean1, newBean1);
|
||||
|
||||
Object sameBean1 = ctx.getBean(beanName);
|
||||
|
||||
assertSame(message, newBean1, sameBean1);
|
||||
|
||||
// make the scope create a new object
|
||||
customScope.createNewScope = true;
|
||||
|
||||
Object newBean2 = ctx.getBean(beanName);
|
||||
assertNotSame(message, newBean1, newBean2);
|
||||
|
||||
// make the scope create a new object .. again
|
||||
customScope.createNewScope = true;
|
||||
|
||||
Object newBean3 = ctx.getBean(beanName);
|
||||
assertNotSame(message, newBean2, newBean3);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class InvalidProxyOnPredefinedScopesConfiguration {
|
||||
@Bean @Scope(proxyMode=ScopedProxyMode.INTERFACES)
|
||||
public Object invalidProxyOnPredefinedScopes() { return new Object(); }
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ScopedConfigurationClass {
|
||||
@Bean
|
||||
@Scope(SCOPE)
|
||||
public TestBean scopedClass() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName(flag);
|
||||
return tb;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope(SCOPE)
|
||||
public ITestBean scopedInterface() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName(flag);
|
||||
return tb;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope(value=SCOPE, proxyMode=ScopedProxyMode.TARGET_CLASS)
|
||||
public ITestBean scopedProxyInterface() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName(flag);
|
||||
return tb;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope(value=SCOPE, proxyMode=ScopedProxyMode.TARGET_CLASS)
|
||||
public TestBean scopedProxyClass() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName(flag);
|
||||
return tb;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestBean singletonWithScopedClassDep() {
|
||||
TestBean singleton = new TestBean();
|
||||
singleton.setSpouse(scopedProxyClass());
|
||||
return singleton;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestBean singletonWithScopedInterfaceDep() {
|
||||
TestBean singleton = new TestBean();
|
||||
singleton.setSpouse(scopedProxyInterface());
|
||||
return singleton;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
|
||||
xmlns:context="http://www.springframework.org/schema/context">
|
||||
|
||||
<context:annotation-config/>
|
||||
|
||||
<bean class="org.springframework.context.annotation.support.ConfigurationClassPostProcessor"/>
|
||||
|
||||
<bean class="org.springframework.context.annotation.configuration.AutowiredConfigurationTests$ValueConfig"/>
|
||||
|
||||
<!--
|
||||
<context:component-scan base-package="test.basic.value"/>
|
||||
-->
|
||||
</beans>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
|
||||
xmlns:context="http://www.springframework.org/schema/context">
|
||||
|
||||
<aop:aspectj-autoproxy proxy-target-class="true"/>
|
||||
</beans>
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Integration tests for {@link org.springframework.context.annotation.Configuration}
|
||||
* class processing.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.context.annotation.support.ConfigurationClassPostProcessor
|
||||
*/
|
||||
package org.springframework.context.annotation.configuration;
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.support.ConfigurationParser;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
|
||||
/**
|
||||
* TCK-style unit tests for handling circular use of the {@link Import} annotation. Explore
|
||||
* subclass hierarchy for specific concrete implementations.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public abstract class AbstractCircularImportDetectionTests {
|
||||
|
||||
protected abstract ConfigurationParser newParser();
|
||||
|
||||
protected abstract String loadAsConfigurationSource(Class<?> clazz) throws Exception;
|
||||
|
||||
@Test
|
||||
public void simpleCircularImportIsDetected() throws Exception {
|
||||
boolean threw = false;
|
||||
try {
|
||||
newParser().parse(loadAsConfigurationSource(A.class), null);
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
||||
ex.getMessage().contains(
|
||||
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.B' " +
|
||||
"to import class 'AbstractCircularImportDetectionTests.A'"));
|
||||
threw = true;
|
||||
}
|
||||
|
||||
assertTrue(threw);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void complexCircularImportIsDetected() throws Exception {
|
||||
boolean threw = false;
|
||||
try {
|
||||
newParser().parse(loadAsConfigurationSource(X.class), null);
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
||||
ex.getMessage().contains(
|
||||
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.Z2' " +
|
||||
"to import class 'AbstractCircularImportDetectionTests.Z'"));
|
||||
threw = true;
|
||||
}
|
||||
|
||||
assertTrue(threw);
|
||||
}
|
||||
|
||||
@Import(B.class)
|
||||
static class A {
|
||||
@Bean
|
||||
TestBean b1() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Import(A.class)
|
||||
static class B {
|
||||
@Bean
|
||||
TestBean b2() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Import( { Y.class, Z.class })
|
||||
class X {
|
||||
@Bean
|
||||
TestBean x() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
class Y {
|
||||
@Bean
|
||||
TestBean y() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Import( { Z1.class, Z2.class })
|
||||
class Z {
|
||||
@Bean
|
||||
TestBean z() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
class Z1 {
|
||||
@Bean
|
||||
TestBean z1() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Import(Z.class)
|
||||
class Z2 {
|
||||
@Bean
|
||||
TestBean z2() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.support.ConfigurationParser;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test proving that ASM-based {@link ConfigurationParser} correctly detects circular use of
|
||||
* the {@link Import @Import} annotation.
|
||||
*
|
||||
* <p>While this test is the only subclass of {@link AbstractCircularImportDetectionTests}, the
|
||||
* hierarchy remains in place in case a JDT-based ConfigurationParser implementation needs to be
|
||||
* developed.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class AsmCircularImportDetectionTests extends AbstractCircularImportDetectionTests {
|
||||
@Override
|
||||
protected ConfigurationParser newParser() {
|
||||
return new ConfigurationParser(new FailFastProblemReporter(), ClassUtils.getDefaultClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String loadAsConfigurationSource(Class<?> clazz) throws Exception {
|
||||
return clazz.getName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.context.annotation.ScopedProxyMode.*;
|
||||
import static org.springframework.context.annotation.StandardScopes.*;
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.support.BeanMethod;
|
||||
import org.springframework.context.annotation.support.ConfigurationClass;
|
||||
import org.springframework.context.annotation.support.ModelClass;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for {@link BeanMethod}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class BeanMethodTests {
|
||||
|
||||
private ProblemReporter problemReporter = new FailFastProblemReporter();
|
||||
private String beanName = "foo";
|
||||
private Bean beanAnno = createMutableAnnotation(Bean.class);
|
||||
private ModelClass returnType = new ModelClass("FooType");
|
||||
private ConfigurationClass declaringClass = new ConfigurationClass();
|
||||
{ declaringClass.setName("test.Config"); }
|
||||
|
||||
@Test
|
||||
public void testWellFormedMethod() {
|
||||
BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno);
|
||||
|
||||
assertThat(beanMethod.getName(), sameInstance(beanName));
|
||||
assertThat(beanMethod.getModifiers(), equalTo(0));
|
||||
assertThat(beanMethod.getReturnType(), sameInstance(returnType));
|
||||
assertThat(beanMethod.getAnnotation(Bean.class), sameInstance(beanAnno));
|
||||
assertThat(beanMethod.getAnnotation(Override.class), nullValue());
|
||||
assertThat(beanMethod.getRequiredAnnotation(Bean.class), sameInstance(beanAnno));
|
||||
try {
|
||||
beanMethod.getRequiredAnnotation(Override.class);
|
||||
fail("expected IllegalArgumentException ex");
|
||||
} catch (IllegalArgumentException ex) { /* expected */ }
|
||||
|
||||
// must call setDeclaringClass() before calling getLocation()
|
||||
try {
|
||||
beanMethod.getLocation();
|
||||
fail("expected IllegalStateException ex");
|
||||
} catch (IllegalStateException ex) { /* expected */ }
|
||||
|
||||
|
||||
beanMethod.setDeclaringClass(declaringClass);
|
||||
assertThat(beanMethod.getDeclaringClass(), sameInstance(declaringClass));
|
||||
|
||||
beanMethod.setSource(12); // indicating a line number
|
||||
assertEquals(beanMethod.getSource(), 12);
|
||||
|
||||
Location location = beanMethod.getLocation();
|
||||
assertEquals(location.getResource(), new ClassPathResource("test/Config"));
|
||||
assertEquals(location.getSource(), 12);
|
||||
|
||||
// should validate without throwing as this is a well-formed method
|
||||
beanMethod.validate(problemReporter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void finalMethodsAreIllegal() {
|
||||
BeanMethod beanMethod = new BeanMethod(beanName, Modifier.FINAL, returnType, beanAnno);
|
||||
beanMethod.setDeclaringClass(declaringClass);
|
||||
try {
|
||||
beanMethod.validate(problemReporter);
|
||||
fail("should have failed due to final bean method");
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue(ex.getMessage().contains("remove the final modifier"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void privateMethodsAreIllegal() {
|
||||
BeanMethod beanMethod = new BeanMethod(beanName, Modifier.PRIVATE, returnType, beanAnno);
|
||||
beanMethod.setDeclaringClass(declaringClass);
|
||||
try {
|
||||
beanMethod.validate(problemReporter);
|
||||
fail("should have failed due to private bean method");
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue(ex.getMessage().contains("increase the method's visibility"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singletonInterfaceScopedProxiesAreIllegal() {
|
||||
Scope scope = SingletonInterfaceProxy.class.getAnnotation(Scope.class);
|
||||
BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
|
||||
beanMethod.setDeclaringClass(declaringClass);
|
||||
try {
|
||||
beanMethod.validate(problemReporter);
|
||||
fail("should have failed due to singleton with scoped proxy");
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex.getMessage().contains("cannot be created for singleton/prototype beans"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singletonTargetClassScopedProxiesAreIllegal() {
|
||||
Scope scope = SingletonTargetClassProxy.class.getAnnotation(Scope.class);
|
||||
BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
|
||||
beanMethod.setDeclaringClass(declaringClass);
|
||||
try {
|
||||
beanMethod.validate(problemReporter);
|
||||
fail("should have failed due to singleton with scoped proxy");
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex.getMessage().contains("cannot be created for singleton/prototype beans"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singletonsSansProxyAreLegal() {
|
||||
Scope scope = SingletonNoProxy.class.getAnnotation(Scope.class);
|
||||
BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
|
||||
beanMethod.setDeclaringClass(declaringClass);
|
||||
beanMethod.validate(problemReporter); // should validate without problems - it's legal
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prototypeInterfaceScopedProxiesAreIllegal() {
|
||||
Scope scope = PrototypeInterfaceProxy.class.getAnnotation(Scope.class);
|
||||
BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
|
||||
beanMethod.setDeclaringClass(declaringClass);
|
||||
try {
|
||||
beanMethod.validate(problemReporter);
|
||||
fail("should have failed due to prototype with scoped proxy");
|
||||
} catch (Exception ex) {
|
||||
assertTrue(ex.getMessage().contains("cannot be created for singleton/prototype beans"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionInterfaceScopedProxiesAreLegal() {
|
||||
Scope scope = SessionInterfaceProxy.class.getAnnotation(Scope.class);
|
||||
BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
|
||||
beanMethod.setDeclaringClass(declaringClass);
|
||||
beanMethod.validate(problemReporter); // should validate without problems - it's legal
|
||||
}
|
||||
|
||||
@Scope(value=SINGLETON, proxyMode=INTERFACES)
|
||||
private class SingletonInterfaceProxy { }
|
||||
|
||||
@Scope(value=SINGLETON, proxyMode=TARGET_CLASS)
|
||||
private class SingletonTargetClassProxy { }
|
||||
|
||||
@Scope(value=SINGLETON, proxyMode=NO)
|
||||
private class SingletonNoProxy { }
|
||||
|
||||
@Scope(value=PROTOTYPE, proxyMode=INTERFACES)
|
||||
private class PrototypeInterfaceProxy { }
|
||||
|
||||
@Scope(value=SESSION, proxyMode=INTERFACES)
|
||||
private class SessionInterfaceProxy { }
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.support.ConfigurationClass;
|
||||
import org.springframework.context.annotation.support.ConfigurationModel;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ConfigurationModel}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ConfigurationModelTests {
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
ConfigurationModel model = new ConfigurationModel();
|
||||
assertThat(model.toString(), equalTo(
|
||||
"ConfigurationModel containing @Configuration classes: []"));
|
||||
|
||||
ConfigurationClass config1 = new ConfigurationClass();
|
||||
config1.setName("test.Config1");
|
||||
model.add(config1);
|
||||
|
||||
assertThat(model.toString(), equalTo(format(
|
||||
"ConfigurationModel containing @Configuration classes: [%s]", config1)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ConfigurationClassPostProcessor}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ConfigurationPostProcessorTests {
|
||||
|
||||
private static final String ORIG_CGLIB_TEST_CLASS = ConfigurationClassPostProcessor.CGLIB_TEST_CLASS;
|
||||
private static final String BOGUS_CGLIB_TEST_CLASS = "a.bogus.class";
|
||||
|
||||
/**
|
||||
* CGLIB is an optional dependency for Spring. If users attempt
|
||||
* to use {@link Configuration} classes, they'll need it on the classpath;
|
||||
* if Configuration classes are present in the bean factory and CGLIB
|
||||
* is not present, an instructive exception should be thrown.
|
||||
*/
|
||||
@Test
|
||||
public void testFailFastIfCglibNotPresent() {
|
||||
@Configuration class Config { }
|
||||
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
factory.registerBeanDefinition("config", rootBeanDefinition(Config.class).getBeanDefinition());
|
||||
ConfigurationClassPostProcessor cpp = new ConfigurationClassPostProcessor();
|
||||
|
||||
// temporarily set the cglib test class to something bogus
|
||||
ConfigurationClassPostProcessor.CGLIB_TEST_CLASS = BOGUS_CGLIB_TEST_CLASS;
|
||||
|
||||
try {
|
||||
cpp.postProcessBeanFactory(factory);
|
||||
} catch (RuntimeException ex) {
|
||||
assertTrue(ex.getMessage().contains("CGLIB is required to process @Configuration classes"));
|
||||
} finally {
|
||||
ConfigurationClassPostProcessor.CGLIB_TEST_CLASS = ORIG_CGLIB_TEST_CLASS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to keep Spring's footprint as small as possible, CGLIB must
|
||||
* not be required on the classpath unless the user is taking advantage
|
||||
* of {@link Configuration} classes.
|
||||
*
|
||||
* This test will fail if any CGLIB classes are classloaded before the call
|
||||
* to {@link ConfigurationClassPostProcessor#enhanceConfigurationClasses}
|
||||
*/
|
||||
@Test
|
||||
public void testCglibClassesAreLoadedJustInTimeForEnhancement() throws Exception {
|
||||
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
|
||||
Field classesField = ClassLoader.class.getDeclaredField("classes");
|
||||
classesField.setAccessible(true);
|
||||
|
||||
// first, remove any CGLIB classes that may have been loaded by other tests
|
||||
@SuppressWarnings("unchecked")
|
||||
Vector<Class<?>> classes = (Vector<Class<?>>) classesField.get(classLoader);
|
||||
|
||||
Vector<Class<?>> cglibClassesAlreadyLoaded = new Vector<Class<?>>();
|
||||
for(Class<?> loadedClass : classes)
|
||||
if(loadedClass.getName().startsWith("net.sf.cglib"))
|
||||
cglibClassesAlreadyLoaded.add(loadedClass);
|
||||
|
||||
for(Class<?> cglibClass : cglibClassesAlreadyLoaded)
|
||||
classes.remove(cglibClass);
|
||||
|
||||
// now, execute a scenario where everything except enhancement occurs
|
||||
// -- no CGLIB classes should get loaded!
|
||||
testFailFastIfCglibNotPresent();
|
||||
|
||||
// test to ensure that indeed no CGLIB classes have been loaded
|
||||
for(Class<?> loadedClass : classes)
|
||||
if(loadedClass.getName().startsWith("net.sf.cglib"))
|
||||
fail("CGLIB class should not have been eagerly loaded: " + loadedClass.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced {@link Configuration} classes are only necessary for respecting
|
||||
* certain bean semantics, like singleton-scoping, scoped proxies, etc.
|
||||
*
|
||||
* Technically, {@link ConfigurationClassPostProcessor} could fail to enhance the
|
||||
* registered Configuration classes and many use cases would still work.
|
||||
* Certain cases, however, like inter-bean singleton references would not.
|
||||
* We test for such a case below, and in doing so prove that enhancement is
|
||||
* working.
|
||||
*/
|
||||
@Test
|
||||
public void testEnhancementIsPresentBecauseSingletonSemanticsAreRespected() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config",
|
||||
rootBeanDefinition(SingletonBeanConfig.class).getBeanDefinition());
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
Foo foo = (Foo) beanFactory.getBean("foo");
|
||||
Bar bar = (Bar) beanFactory.getBean("bar");
|
||||
assertThat(foo, sameInstance(bar.foo));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SingletonBeanConfig {
|
||||
public @Bean Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
public @Bean Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
|
||||
static class Foo { }
|
||||
static class Bar {
|
||||
final Foo foo;
|
||||
public Bar(Foo foo) { this.foo = foo; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.support.ConfigurationClassPostProcessor;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests covering cases where a user defines an invalid Configuration
|
||||
* class, e.g.: forgets to annotate with {@link Configuration} or declares
|
||||
* a Configuration class as final.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class InvalidConfigurationClassDefinitionTests {
|
||||
|
||||
@Test
|
||||
public void configurationClassesMayNotBeFinal() {
|
||||
@Configuration
|
||||
final class Config { }
|
||||
|
||||
BeanDefinition configBeanDef = rootBeanDefinition(Config.class).getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", configBeanDef);
|
||||
|
||||
try {
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
fail("expected exception");
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue(ex.getMessage(), ex.getMessage().contains("Remove the final modifier"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Unit tests for support classes related to
|
||||
* {@link org.springframework.context.annotation.Configuration} class processing.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
package org.springframework.context.annotation.support;
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
import org.springframework.core.enums.ShortCodedLabeledEnum;
|
||||
|
||||
|
||||
/**
|
||||
* TODO: JAVADOC
|
||||
*
|
||||
* @author Rob Harrop
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class Colour extends ShortCodedLabeledEnum {
|
||||
|
||||
public static final Colour RED = new Colour(0, "RED");
|
||||
|
||||
public static final Colour BLUE = new Colour(1, "BLUE");
|
||||
|
||||
public static final Colour GREEN = new Colour(2, "GREEN");
|
||||
|
||||
public static final Colour PURPLE = new Colour(3, "PURPLE");
|
||||
|
||||
private Colour(int code, String label) {
|
||||
super(code, label);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
|
||||
|
||||
/**
|
||||
* Simple scope implementation which creates object based on a flag.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class CustomScope implements Scope {
|
||||
|
||||
public boolean createNewScope = true;
|
||||
|
||||
private Map<String, Object> beans = new HashMap<String, Object>();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.Scope#get(java.lang.String,
|
||||
* org.springframework.beans.factory.ObjectFactory)
|
||||
*/
|
||||
public Object get(String name, ObjectFactory<?> objectFactory) {
|
||||
if (createNewScope) {
|
||||
beans.clear();
|
||||
// reset the flag back
|
||||
createNewScope = false;
|
||||
}
|
||||
|
||||
Object bean = beans.get(name);
|
||||
// if a new object is requested or none exists under the current
|
||||
// name, create one
|
||||
if (bean == null) {
|
||||
beans.put(name, objectFactory.getObject());
|
||||
}
|
||||
|
||||
return beans.get(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.Scope#getConversationId()
|
||||
*/
|
||||
public String getConversationId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String,
|
||||
* java.lang.Runnable)
|
||||
*/
|
||||
public void registerDestructionCallback(String name, Runnable callback) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
|
||||
*/
|
||||
public Object remove(String name) {
|
||||
return beans.remove(name);
|
||||
}
|
||||
|
||||
public Object resolveContextualObject(String key) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
|
||||
/** TODO: JAVADOC */
|
||||
public class DependsOnTestBean {
|
||||
public TestBean tb;
|
||||
|
||||
private int state;
|
||||
|
||||
public void setTestBean(TestBean tb) {
|
||||
this.tb = tb;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return tb;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
|
||||
/** TODO: JAVADOC */
|
||||
public interface INestedTestBean {
|
||||
|
||||
String getCompany();
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
|
||||
/** TODO: JAVADOC */
|
||||
public interface IOther {
|
||||
|
||||
void absquatulate();
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Interface used for test beans. Two methods are the same as on Person, but if this extends
|
||||
* person it breaks quite a few tests
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface ITestBean {
|
||||
|
||||
int getAge();
|
||||
|
||||
void setAge(int age);
|
||||
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
|
||||
ITestBean getSpouse();
|
||||
|
||||
void setSpouse(ITestBean spouse);
|
||||
|
||||
/**
|
||||
* t null no error.
|
||||
*/
|
||||
void exceptional(Throwable t) throws Throwable;
|
||||
|
||||
Object returnsThis();
|
||||
|
||||
INestedTestBean getDoctor();
|
||||
|
||||
INestedTestBean getLawyer();
|
||||
|
||||
IndexedTestBean getNestedIndexedBean();
|
||||
|
||||
/**
|
||||
* Increment the age by one.
|
||||
*
|
||||
* @return the previous age
|
||||
*/
|
||||
int haveBirthday();
|
||||
|
||||
void unreliableFileOperation() throws IOException;
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
||||
/**
|
||||
* TODO: JAVADOC
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 11.11.2003
|
||||
*/
|
||||
public class IndexedTestBean {
|
||||
|
||||
private TestBean[] array;
|
||||
|
||||
private Collection<TestBean> collection;
|
||||
|
||||
private List<TestBean> list;
|
||||
|
||||
private Set<TestBean> set;
|
||||
|
||||
private SortedSet<TestBean> sortedSet;
|
||||
|
||||
private Map<String, Object> map;
|
||||
|
||||
private SortedMap<String, TestBean> sortedMap;
|
||||
|
||||
public IndexedTestBean() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public IndexedTestBean(boolean populate) {
|
||||
if (populate) {
|
||||
populate();
|
||||
}
|
||||
}
|
||||
|
||||
public void populate() {
|
||||
TestBean tb0 = new TestBean("name0", 0);
|
||||
TestBean tb1 = new TestBean("name1", 0);
|
||||
TestBean tb2 = new TestBean("name2", 0);
|
||||
TestBean tb3 = new TestBean("name3", 0);
|
||||
TestBean tb4 = new TestBean("name4", 0);
|
||||
TestBean tb5 = new TestBean("name5", 0);
|
||||
TestBean tb6 = new TestBean("name6", 0);
|
||||
TestBean tb7 = new TestBean("name7", 0);
|
||||
TestBean tbX = new TestBean("nameX", 0);
|
||||
TestBean tbY = new TestBean("nameY", 0);
|
||||
this.array = new TestBean[] { tb0, tb1 };
|
||||
this.list = new ArrayList<TestBean>();
|
||||
this.list.add(tb2);
|
||||
this.list.add(tb3);
|
||||
this.set = new TreeSet<TestBean>();
|
||||
this.set.add(tb6);
|
||||
this.set.add(tb7);
|
||||
this.map = new HashMap<String, Object>();
|
||||
this.map.put("key1", tb4);
|
||||
this.map.put("key2", tb5);
|
||||
this.map.put("key.3", tb5);
|
||||
List<TestBean> list = new ArrayList<TestBean>();
|
||||
list.add(tbX);
|
||||
list.add(tbY);
|
||||
this.map.put("key4", list);
|
||||
}
|
||||
|
||||
public TestBean[] getArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
public void setArray(TestBean[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public Collection<?> getCollection() {
|
||||
return collection;
|
||||
}
|
||||
|
||||
public void setCollection(Collection<TestBean> collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
public List<TestBean> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<TestBean> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public Set<TestBean> getSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
public void setSet(Set<TestBean> set) {
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
public SortedSet<TestBean> getSortedSet() {
|
||||
return sortedSet;
|
||||
}
|
||||
|
||||
public void setSortedSet(SortedSet<TestBean> sortedSet) {
|
||||
this.sortedSet = sortedSet;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public void setMap(Map<String, Object> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public SortedMap<String, TestBean> getSortedMap() {
|
||||
return sortedMap;
|
||||
}
|
||||
|
||||
public void setSortedMap(SortedMap<String, TestBean> sortedMap) {
|
||||
this.sortedMap = sortedMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
/**
|
||||
* Simple nested test bean used for testing bean factories, AOP framework etc.
|
||||
*
|
||||
* @author Trevor D. Cook
|
||||
* @since 30.09.2003
|
||||
*/
|
||||
public class NestedTestBean implements INestedTestBean {
|
||||
|
||||
private String company = "";
|
||||
|
||||
public NestedTestBean() {
|
||||
}
|
||||
|
||||
public NestedTestBean(String company) {
|
||||
setCompany(company);
|
||||
}
|
||||
|
||||
public void setCompany(String company) {
|
||||
this.company = ((company != null) ? company : "");
|
||||
}
|
||||
|
||||
public String getCompany() {
|
||||
return company;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof NestedTestBean)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NestedTestBean ntb = (NestedTestBean) obj;
|
||||
return this.company.equals(ntb.company);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.company.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NestedTestBean: " + this.company;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package test.beans;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Simple test bean used for testing bean factories, AOP framework etc.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 15 April 2001
|
||||
*/
|
||||
public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOther, Comparable<Object> {
|
||||
|
||||
private String beanName;
|
||||
|
||||
private String country;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private boolean postProcessed;
|
||||
|
||||
private String name;
|
||||
|
||||
private String sex;
|
||||
|
||||
private int age;
|
||||
|
||||
private boolean jedi;
|
||||
|
||||
private ITestBean spouse;
|
||||
|
||||
private String touchy;
|
||||
|
||||
private String[] stringArray;
|
||||
|
||||
private Integer[] someIntegerArray;
|
||||
|
||||
private Date date = new Date();
|
||||
|
||||
private Float myFloat = new Float(0.0);
|
||||
|
||||
private Collection<?> friends = new LinkedList<Object>();
|
||||
|
||||
private Set<?> someSet = new HashSet<Object>();
|
||||
|
||||
private Map<?, ?> someMap = new HashMap<Object, Object>();
|
||||
|
||||
private List<?> someList = new ArrayList<Object>();
|
||||
|
||||
private Properties someProperties = new Properties();
|
||||
|
||||
private INestedTestBean doctor = new NestedTestBean();
|
||||
|
||||
private INestedTestBean lawyer = new NestedTestBean();
|
||||
|
||||
private IndexedTestBean nestedIndexedBean;
|
||||
|
||||
private boolean destroyed;
|
||||
|
||||
private Number someNumber;
|
||||
|
||||
private Colour favouriteColour;
|
||||
|
||||
private Boolean someBoolean;
|
||||
|
||||
private List<?> otherColours;
|
||||
|
||||
private List<?> pets;
|
||||
|
||||
public TestBean() {
|
||||
}
|
||||
|
||||
public TestBean(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public TestBean(ITestBean spouse) {
|
||||
this.spouse = spouse;
|
||||
}
|
||||
|
||||
public TestBean(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public TestBean(ITestBean spouse, Properties someProperties) {
|
||||
this.spouse = spouse;
|
||||
this.someProperties = someProperties;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public BeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
public void setPostProcessed(boolean postProcessed) {
|
||||
this.postProcessed = postProcessed;
|
||||
}
|
||||
|
||||
public boolean isPostProcessed() {
|
||||
return postProcessed;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public void setSex(String sex) {
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public boolean isJedi() {
|
||||
return jedi;
|
||||
}
|
||||
|
||||
public void setJedi(boolean jedi) {
|
||||
this.jedi = jedi;
|
||||
}
|
||||
|
||||
public ITestBean getSpouse() {
|
||||
return spouse;
|
||||
}
|
||||
|
||||
public void setSpouse(ITestBean spouse) {
|
||||
this.spouse = spouse;
|
||||
}
|
||||
|
||||
public String getTouchy() {
|
||||
return touchy;
|
||||
}
|
||||
|
||||
public void setTouchy(String touchy) throws Exception {
|
||||
if (touchy.indexOf('.') != -1) {
|
||||
throw new Exception("Can't contain a .");
|
||||
}
|
||||
|
||||
if (touchy.indexOf(',') != -1) {
|
||||
throw new NumberFormatException("Number format exception: contains a ,");
|
||||
}
|
||||
|
||||
this.touchy = touchy;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String[] getStringArray() {
|
||||
return stringArray;
|
||||
}
|
||||
|
||||
public void setStringArray(String[] stringArray) {
|
||||
this.stringArray = stringArray;
|
||||
}
|
||||
|
||||
public Integer[] getSomeIntegerArray() {
|
||||
return someIntegerArray;
|
||||
}
|
||||
|
||||
public void setSomeIntegerArray(Integer[] someIntegerArray) {
|
||||
this.someIntegerArray = someIntegerArray;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public Float getMyFloat() {
|
||||
return myFloat;
|
||||
}
|
||||
|
||||
public void setMyFloat(Float myFloat) {
|
||||
this.myFloat = myFloat;
|
||||
}
|
||||
|
||||
public Collection<?> getFriends() {
|
||||
return friends;
|
||||
}
|
||||
|
||||
public void setFriends(Collection<?> friends) {
|
||||
this.friends = friends;
|
||||
}
|
||||
|
||||
public Set<?> getSomeSet() {
|
||||
return someSet;
|
||||
}
|
||||
|
||||
public void setSomeSet(Set<?> someSet) {
|
||||
this.someSet = someSet;
|
||||
}
|
||||
|
||||
public Map<?, ?> getSomeMap() {
|
||||
return someMap;
|
||||
}
|
||||
|
||||
public void setSomeMap(Map<?, ?> someMap) {
|
||||
this.someMap = someMap;
|
||||
}
|
||||
|
||||
public List<?> getSomeList() {
|
||||
return someList;
|
||||
}
|
||||
|
||||
public void setSomeList(List<?> someList) {
|
||||
this.someList = someList;
|
||||
}
|
||||
|
||||
public Properties getSomeProperties() {
|
||||
return someProperties;
|
||||
}
|
||||
|
||||
public void setSomeProperties(Properties someProperties) {
|
||||
this.someProperties = someProperties;
|
||||
}
|
||||
|
||||
public INestedTestBean getDoctor() {
|
||||
return doctor;
|
||||
}
|
||||
|
||||
public INestedTestBean getLawyer() {
|
||||
return lawyer;
|
||||
}
|
||||
|
||||
public void setDoctor(INestedTestBean bean) {
|
||||
doctor = bean;
|
||||
}
|
||||
|
||||
public void setLawyer(INestedTestBean bean) {
|
||||
lawyer = bean;
|
||||
}
|
||||
|
||||
public Number getSomeNumber() {
|
||||
return someNumber;
|
||||
}
|
||||
|
||||
public void setSomeNumber(Number someNumber) {
|
||||
this.someNumber = someNumber;
|
||||
}
|
||||
|
||||
public Colour getFavouriteColour() {
|
||||
return favouriteColour;
|
||||
}
|
||||
|
||||
public void setFavouriteColour(Colour favouriteColour) {
|
||||
this.favouriteColour = favouriteColour;
|
||||
}
|
||||
|
||||
public Boolean getSomeBoolean() {
|
||||
return someBoolean;
|
||||
}
|
||||
|
||||
public void setSomeBoolean(Boolean someBoolean) {
|
||||
this.someBoolean = someBoolean;
|
||||
}
|
||||
|
||||
public IndexedTestBean getNestedIndexedBean() {
|
||||
return nestedIndexedBean;
|
||||
}
|
||||
|
||||
public void setNestedIndexedBean(IndexedTestBean nestedIndexedBean) {
|
||||
this.nestedIndexedBean = nestedIndexedBean;
|
||||
}
|
||||
|
||||
public List<?> getOtherColours() {
|
||||
return otherColours;
|
||||
}
|
||||
|
||||
public void setOtherColours(List<?> otherColours) {
|
||||
this.otherColours = otherColours;
|
||||
}
|
||||
|
||||
public List<?> getPets() {
|
||||
return pets;
|
||||
}
|
||||
|
||||
public void setPets(List<?> pets) {
|
||||
this.pets = pets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ITestBean#exceptional(Throwable)
|
||||
*/
|
||||
public void exceptional(Throwable t) throws Throwable {
|
||||
if (t != null) {
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user