diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index 8edb9f7d00d..244e534dccb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -220,8 +220,8 @@ class TypeConverterDelegate { else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) { if (firstAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) { try { - Constructor strCtor = requiredType.getConstructor(String.class); - return (T) BeanUtils.instantiateClass(strCtor, convertedValue); + Constructor strCtor = requiredType.getConstructor(String.class); + return BeanUtils.instantiateClass(strCtor, convertedValue); } catch (NoSuchMethodException ex) { // proceed with field lookup @@ -331,7 +331,7 @@ class TypeConverterDelegate { * @param requiredType the type to find an editor for * @return the corresponding editor, or {@code null} if none */ - private PropertyEditor findDefaultEditor(Class requiredType) { + private PropertyEditor findDefaultEditor(Class requiredType) { PropertyEditor editor = null; if (requiredType != null) { // No custom editor -> check BeanWrapperImpl's default editors. @@ -496,7 +496,7 @@ class TypeConverterDelegate { @SuppressWarnings("unchecked") private Collection convertToTypedCollection( - Collection original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) { + Collection original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) { if (!Collection.class.isAssignableFrom(requiredType)) { return original; @@ -578,7 +578,7 @@ class TypeConverterDelegate { @SuppressWarnings("unchecked") private Map convertToTypedMap( - Map original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) { + Map original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) { if (!Map.class.isAssignableFrom(requiredType)) { return original; @@ -674,7 +674,7 @@ class TypeConverterDelegate { null); } - private boolean canCreateCopy(Class requiredType) { + private boolean canCreateCopy(Class requiredType) { return (!requiredType.isInterface() && !Modifier.isAbstract(requiredType.getModifiers()) && Modifier.isPublic(requiredType.getModifiers()) && ClassUtils.hasConstructor(requiredType)); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java index 69353182130..ae394ca7c85 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java @@ -65,7 +65,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class); - private static Class closeableInterface; + private static Class closeableInterface; static { try { @@ -86,14 +86,14 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { private final boolean nonPublicAccessAllowed; + private final AccessControlContext acc; + private String destroyMethodName; private transient Method destroyMethod; private List beanPostProcessors; - private final AccessControlContext acc; - /** * Create a new DisposableBeanAdapter for the given bean. @@ -150,9 +150,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { this.beanName = beanName; this.invokeDisposableBean = invokeDisposableBean; this.nonPublicAccessAllowed = nonPublicAccessAllowed; + this.acc = null; this.destroyMethodName = destroyMethodName; this.beanPostProcessors = postProcessors; - this.acc = null; } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10744Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10744Tests.java new file mode 100644 index 00000000000..ae8a6ce00fe --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10744Tests.java @@ -0,0 +1,130 @@ +/* + * Copyright 2002-2014 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 org.junit.Test; + +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +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 static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +/** + * @author Phillip Webb + */ +public class Spr10744Tests { + + private static int createCount = 0; + + private static int scopeCount = 0; + + + @Test + public void testSpr10744() throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.getBeanFactory().registerScope("myTestScope", new MyTestScope()); + context.register(MyTestConfiguration.class); + context.refresh(); + + Foo bean1 = context.getBean("foo", Foo.class); + Foo bean2 = context.getBean("foo", Foo.class); + assertThat(bean1, sameInstance(bean2)); + + // Should have created a single instance for the proxy + assertThat(createCount, equalTo(1)); + assertThat(scopeCount, equalTo(0)); + + // Proxy mode should create new scoped object on each method call + bean1.getMessage(); + assertThat(createCount, equalTo(2)); + assertThat(scopeCount, equalTo(1)); + bean1.getMessage(); + assertThat(createCount, equalTo(3)); + assertThat(scopeCount, equalTo(2)); + + context.close(); + } + + + private static class MyTestScope implements org.springframework.beans.factory.config.Scope { + + @Override + public Object get(String name, ObjectFactory objectFactory) { + scopeCount++; + return objectFactory.getObject(); + } + + @Override + public Object remove(String name) { + return null; + } + + @Override + public void registerDestructionCallback(String name, Runnable callback) { + } + + @Override + public Object resolveContextualObject(String key) { + return null; + } + + @Override + public String getConversationId() { + return null; + } + } + + + static class Foo { + + public Foo() { + createCount++; + } + + public String getMessage() { + return "Hello"; + } + } + + + @Configuration + static class MyConfiguration { + + @Bean + public Foo foo() { + return new Foo(); + } + } + + + @Configuration + static class MyTestConfiguration extends MyConfiguration { + + @Bean + @Scope(value = "myTestScope", proxyMode = ScopedProxyMode.TARGET_CLASS) + @Override + public Foo foo() { + return new Foo(); + } + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 46847d08237..3b4def636d2 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -25,6 +25,7 @@ import java.util.WeakHashMap; import org.springframework.core.BridgeMethodResolver; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** * General utility methods for working with annotations, handling bridge methods (which the compiler @@ -60,8 +61,8 @@ public abstract class AnnotationUtils { * Method, Constructor or Field. Meta-annotations will be searched if the annotation * is not declared locally on the supplied element. * @param ae the Method, Constructor or Field from which to get the annotation - * @param annotationType the annotation class to look for, both locally and as a meta-annotation - * @return the matching annotation or {@code null} if not found + * @param annotationType the annotation type to look for, both locally and as a meta-annotation + * @return the matching annotation, or {@code null} if none found * @since 3.1 */ public static T getAnnotation(AnnotatedElement ae, Class annotationType) { @@ -92,7 +93,7 @@ public abstract class AnnotationUtils { * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method}. *

Correctly handles bridge {@link Method Methods} generated by the compiler. * @param method the method to look for annotations on - * @param annotationType the annotation class to look for + * @param annotationType the annotation type to look for * @return the annotations found * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) */ @@ -115,8 +116,8 @@ public abstract class AnnotationUtils { * traversing its super methods if no annotation can be found on the given method itself. *

Annotations on methods are not inherited by default, so we need to handle this explicitly. * @param method the method to look for annotations on - * @param annotationType the annotation class to look for - * @return the annotation found, or {@code null} if none found + * @param annotationType the annotation type to look for + * @return the annotation found, or {@code null} if none */ public static A findAnnotation(Method method, Class annotationType) { A annotation = getAnnotation(method, annotationType); @@ -192,7 +193,7 @@ public abstract class AnnotationUtils { * with the interfaces that the superclass declares. Recursing up through the entire superclass * hierarchy if no match is found. * @param clazz the class to look for annotations on - * @param annotationType the annotation class to look for + * @param annotationType the annotation type to look for * @return the annotation found, or {@code null} if none found */ public static A findAnnotation(Class clazz, Class annotationType) { @@ -215,11 +216,11 @@ public abstract class AnnotationUtils { } } } - Class superClass = clazz.getSuperclass(); - if (superClass == null || superClass.equals(Object.class)) { + Class superclass = clazz.getSuperclass(); + if (superclass == null || superclass.equals(Object.class)) { return null; } - return findAnnotation(superClass, annotationType); + return findAnnotation(superclass, annotationType); } /** @@ -232,9 +233,8 @@ public abstract class AnnotationUtils { *

The standard {@link Class} API does not provide a mechanism for determining which class * in an inheritance hierarchy actually declares an {@link Annotation}, so we need to handle * this explicitly. - * @param annotationType the Class object corresponding to the annotation type - * @param clazz the Class object corresponding to the class on which to check for the annotation, - * or {@code null} + * @param annotationType the annotation type to look for, both locally and as a meta-annotation + * @param clazz the class on which to check for the annotation (may be {@code null}) * @return the first {@link Class} in the inheritance hierarchy of the specified {@code clazz} * which declares an annotation for the specified {@code annotationType}, or {@code null} * if not found @@ -248,8 +248,10 @@ public abstract class AnnotationUtils { if (clazz == null || clazz.equals(Object.class)) { return null; } - return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz : findAnnotationDeclaringClass( - annotationType, clazz.getSuperclass()); + if (isAnnotationDeclaredLocally(annotationType, clazz)) { + return clazz; + } + return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass()); } /** @@ -413,14 +415,13 @@ public abstract class AnnotationUtils { } if (nestedAnnotationsAsMap && value instanceof Annotation) { attrs.put(method.getName(), - getAnnotationAttributes((Annotation) value, classValuesAsString, nestedAnnotationsAsMap)); + getAnnotationAttributes((Annotation) value, classValuesAsString, true)); } else if (nestedAnnotationsAsMap && value instanceof Annotation[]) { Annotation[] realAnnotations = (Annotation[]) value; AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; for (int i = 0; i < realAnnotations.length; i++) { - mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, - nestedAnnotationsAsMap); + mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true); } attrs.put(method.getName(), mappedAnnotations); } @@ -456,7 +457,8 @@ public abstract class AnnotationUtils { */ public static Object getValue(Annotation annotation, String attributeName) { try { - Method method = annotation.annotationType().getDeclaredMethod(attributeName, new Class[0]); + Method method = annotation.annotationType().getDeclaredMethod(attributeName); + ReflectionUtils.makeAccessible(method); return method.invoke(annotation); } catch (Exception ex) { @@ -506,8 +508,7 @@ public abstract class AnnotationUtils { */ public static Object getDefaultValue(Class annotationType, String attributeName) { try { - Method method = annotationType.getDeclaredMethod(attributeName, new Class[0]); - return method.getDefaultValue(); + return annotationType.getDeclaredMethod(attributeName).getDefaultValue(); } catch (Exception ex) { return null; diff --git a/spring-core/src/main/java/org/springframework/core/env/EnumerablePropertySource.java b/spring-core/src/main/java/org/springframework/core/env/EnumerablePropertySource.java index a8767da54e2..f5d3b6e9e6d 100644 --- a/spring-core/src/main/java/org/springframework/core/env/EnumerablePropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/env/EnumerablePropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2014 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. @@ -18,6 +18,7 @@ package org.springframework.core.env; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.util.Assert; /** @@ -53,21 +54,22 @@ public abstract class EnumerablePropertySource extends PropertySource { super(name, source); } + /** - * Return the names of all properties contained by the {@linkplain #getSource() - * source} object (never {@code null}). + * Return the names of all properties contained by the + * {@linkplain #getSource() source} object (never {@code null}). */ public abstract String[] getPropertyNames(); /** * Return whether this {@code PropertySource} contains a property with the given name. - *

This implementation checks for the presence of the given name within - * the {@link #getPropertyNames()} array. - * @param name the property to find + *

This implementation checks for the presence of the given name within the + * {@link #getPropertyNames()} array. + * @param name the name of the property to find */ public boolean containsProperty(String name) { - Assert.notNull(name, "property name must not be null"); - for (String candidate : this.getPropertyNames()) { + Assert.notNull(name, "Property name must not be null"); + for (String candidate : getPropertyNames()) { if (candidate.equals(name)) { if (logger.isDebugEnabled()) { logger.debug(String.format("PropertySource [%s] contains '%s'", getName(), name)); diff --git a/spring-core/src/main/java/org/springframework/core/env/PropertySource.java b/spring-core/src/main/java/org/springframework/core/env/PropertySource.java index aab278b895b..3fbdd045c79 100644 --- a/spring-core/src/main/java/org/springframework/core/env/PropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/env/PropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2014 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. @@ -18,7 +18,9 @@ package org.springframework.core.env; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Abstract base class representing a source of name/value property pairs. The underlying @@ -55,12 +57,13 @@ import org.springframework.util.Assert; */ public abstract class PropertySource { - protected final Log logger = LogFactory.getLog(this.getClass()); + protected final Log logger = LogFactory.getLog(getClass()); protected final String name; protected final T source; + /** * Create a new {@code PropertySource} with the given name and source object. */ @@ -74,15 +77,15 @@ public abstract class PropertySource { /** * Create a new {@code PropertySource} with the given name and with a new {@code Object} * instance as the underlying source. - *

Often useful in testing scenarios when creating - * anonymous implementations that never query an actual source, but rather return - * hard-coded values. + *

Often useful in testing scenarios when creating anonymous implementations that + * never query an actual source but rather return hard-coded values. */ @SuppressWarnings("unchecked") public PropertySource(String name) { this(name, (T) new Object()); } + /** * Return the name of this {@code PropertySource} */ @@ -94,91 +97,75 @@ public abstract class PropertySource { * Return the underlying source object for this {@code PropertySource}. */ public T getSource() { - return source; + return this.source; } /** * Return whether this {@code PropertySource} contains the given name. - *

This implementation simply checks for a null return value - * from {@link #getProperty(String)}. Subclasses may wish to - * implement a more efficient algorithm if possible. + *

This implementation simply checks for a {@code null} return value + * from {@link #getProperty(String)}. Subclasses may wish to implement + * a more efficient algorithm if possible. * @param name the property name to find */ public boolean containsProperty(String name) { - return this.getProperty(name) != null; + return (getProperty(name) != null); } /** - * Return the value associated with the given name, {@code null} if not found. + * Return the value associated with the given name, + * or {@code null} if not found. * @param name the property to find * @see PropertyResolver#getRequiredProperty(String) */ public abstract Object getProperty(String name); - /** - * Return a hashcode derived from the {@code name} property of this {@code PropertySource} - * object. - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); - return result; - } /** * This {@code PropertySource} object is equal to the given object if: *

- * - *

No properties other than {@code name} are evaluated. + *

No properties other than {@code name} are evaluated. */ @Override public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof PropertySource)) - return false; - PropertySource other = (PropertySource) obj; - if (this.name == null) { - if (other.name != null) - return false; - } else if (!this.name.equals(other.name)) - return false; - return true; + return (this == obj || (obj instanceof PropertySource && + ObjectUtils.nullSafeEquals(this.name, ((PropertySource) obj).name))); + } + + /** + * Return a hash code derived from the {@code name} property + * of this {@code PropertySource} object. + */ + @Override + public int hashCode() { + return ObjectUtils.nullSafeHashCode(this.name); } /** * Produce concise output (type and name) if the current log level does not include - * debug. If debug is enabled, produce verbose output including hashcode of the + * debug. If debug is enabled, produce verbose output including the hash code of the * PropertySource instance and every name/value property pair. - * - * This variable verbosity is useful as a property source such as system properties + *

This variable verbosity is useful as a property source such as system properties * or environment variables may contain an arbitrary number of property pairs, * potentially leading to difficult to read exception and log messages. - * * @see Log#isDebugEnabled() */ @Override public String toString() { if (logger.isDebugEnabled()) { return String.format("%s@%s [name='%s', properties=%s]", - this.getClass().getSimpleName(), System.identityHashCode(this), this.name, this.source); + getClass().getSimpleName(), System.identityHashCode(this), this.name, this.source); + } + else { + return String.format("%s [name='%s']", getClass().getSimpleName(), this.name); } - - return String.format("%s [name='%s']", - this.getClass().getSimpleName(), this.name); } /** * Return a {@code PropertySource} implementation intended for collection comparison purposes only. - * *

Primarily for internal use, but given a collection of {@code PropertySource} objects, may be * used as follows: *

@@ -189,11 +176,9 @@ public abstract class PropertySource {
 	 * assert sources.contains(PropertySource.named("sourceB"));
 	 * assert !sources.contains(PropertySource.named("sourceC"));
 	 * }
- * * The returned {@code PropertySource} will throw {@code UnsupportedOperationException} * if any methods other than {@code equals(Object)}, {@code hashCode()}, and {@code toString()} * are called. - * * @param name the name of the comparison {@code PropertySource} to be created and returned. */ public static PropertySource named(String name) { @@ -209,7 +194,6 @@ public abstract class PropertySource { * {@code ApplicationContext}. In such cases, a stub should be used to hold the * intended default position/order of the property source, then be replaced * during context refresh. - * * @see org.springframework.context.support.AbstractApplicationContext#initPropertySources() * @see org.springframework.web.context.support.StandardServletEnvironment * @see org.springframework.web.context.support.ServletContextPropertySource @@ -221,7 +205,7 @@ public abstract class PropertySource { } /** - * Always return {@code null}. + * Always returns {@code null}. */ @Override public String getProperty(String name) { @@ -236,8 +220,7 @@ public abstract class PropertySource { static class ComparisonPropertySource extends StubPropertySource { private static final String USAGE_ERROR = - "ComparisonPropertySource instances are for collection comparison " + - "use only"; + "ComparisonPropertySource instances are for use with collection comparison only"; public ComparisonPropertySource(String name) { super(name); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/OracleTableMetaDataProvider.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/OracleTableMetaDataProvider.java index c90743abe8e..7c827621768 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/OracleTableMetaDataProvider.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/OracleTableMetaDataProvider.java @@ -29,8 +29,8 @@ import org.springframework.util.ReflectionUtils; /** * Oracle-specific implementation of the {@link org.springframework.jdbc.core.metadata.TableMetaDataProvider}. - * Supports a feature for including synonyms in the metadata lookup. Also supports lookup of current schema using - * the sys_context. + * Supports a feature for including synonyms in the metadata lookup. Also supports lookup of current schema + * using the sys_context. * *

Thanks to Mike Youngstrom and Bruce Campbell for submitting the original suggestion for the Oracle * current schema lookup implementation. @@ -56,9 +56,10 @@ public class OracleTableMetaDataProvider extends GenericTableMetaDataProvider { lookupDefaultSchema(databaseMetaData); } + @Override protected String getDefaultSchema() { - if (defaultSchema != null) { + if (this.defaultSchema != null) { return defaultSchema; } return super.getDefaultSchema(); @@ -107,7 +108,7 @@ public class OracleTableMetaDataProvider extends GenericTableMetaDataProvider { ReflectionUtils.makeAccessible(getIncludeSynonyms); originalValueForIncludeSynonyms = (Boolean) getIncludeSynonyms.invoke(con); - setIncludeSynonyms = con.getClass().getMethod("setIncludeSynonyms", new Class[] {boolean.class}); + setIncludeSynonyms = con.getClass().getMethod("setIncludeSynonyms", boolean.class); ReflectionUtils.makeAccessible(setIncludeSynonyms); setIncludeSynonyms.invoke(con, Boolean.TRUE); } @@ -126,9 +127,7 @@ public class OracleTableMetaDataProvider extends GenericTableMetaDataProvider { } /* - * Oracle implementation for detecting current schema - * - * @param databaseMetaData + * Oracle-based implementation for detecting the current schema. */ private void lookupDefaultSchema(DatabaseMetaData databaseMetaData) { try { @@ -144,7 +143,9 @@ public class OracleTableMetaDataProvider extends GenericTableMetaDataProvider { cstmt.close(); } } - } catch (Exception ignore) {} + } + catch (Exception ignore) { + } } } diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java index c2daca30af4..60593bf5e89 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java @@ -28,27 +28,24 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; -import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; /** - *

- * {@code TestContextManager} is the main entry point into the - * Spring TestContext Framework, which provides support for loading and - * accessing {@link ApplicationContext application contexts}, dependency - * injection of test instances, - * {@link org.springframework.transaction.annotation.Transactional - * transactional} execution of test methods, etc. - *

- *

- * Specifically, a {@code TestContextManager} is responsible for managing a + * {@code TestContextManager} is the main entry point into the Spring + * TestContext Framework, which provides support for loading and accessing + * {@link org.springframework.context.ApplicationContext application contexts}, + * dependency injection of test instances, + * {@link org.springframework.transaction.annotation.Transactional transactional} + * execution of test methods, etc. + * + *

Specifically, a {@code TestContextManager} is responsible for managing a * single {@link TestContext} and signaling events to all registered * {@link TestExecutionListener TestExecutionListeners} at well defined test * execution points: - *

+ * *
    *
  • {@link #beforeTestClass() before test class execution}: prior to any * before class methods of a particular testing framework (e.g., JUnit @@ -85,9 +82,10 @@ public class TestContextManager { private static final Log logger = LogFactory.getLog(TestContextManager.class); /** - * Cache of Spring application contexts. This needs to be static, as tests - * may be destroyed and recreated between running individual test methods, - * for example with JUnit. + * Cache of Spring application contexts. + *

    This needs to be static, since test instances may be destroyed and + * recreated between invocations of individual test methods, as is the case + * with JUnit. */ static final ContextCache contextCache = new ContextCache(); @@ -97,7 +95,11 @@ public class TestContextManager { /** - * Delegates to {@link #TestContextManager(Class, String)} with a value of + * Construct a new {@code TestContextManager} for the specified {@linkplain Class test class} + * and automatically {@link #registerTestExecutionListeners register} the + * {@link TestExecutionListener TestExecutionListeners} configured for the test class + * via the {@link TestExecutionListeners @TestExecutionListeners} annotation. + *

    Delegates to {@link #TestContextManager(Class, String)} with a value of * {@code null} for the default {@code ContextLoader} class name. */ public TestContextManager(Class testClass) { @@ -105,23 +107,23 @@ public class TestContextManager { } /** - * Constructs a new {@code TestContextManager} for the specified {@linkplain Class - * test class} and automatically {@link #registerTestExecutionListeners registers} the + * Construct a new {@code TestContextManager} for the specified {@linkplain Class test class} + * and automatically {@link #registerTestExecutionListeners register} the * {@link TestExecutionListener TestExecutionListeners} configured for the test class * via the {@link TestExecutionListeners @TestExecutionListeners} annotation. * @param testClass the test class to be managed - * @param defaultContextLoaderClassName the name of the default {@code ContextLoader} - * class to use (may be {@code null}) - * @see #registerTestExecutionListeners(TestExecutionListener...) + * @param defaultContextLoaderClassName the name of the default {@code ContextLoader} class + * to use (may be {@code null}) + * @see #registerTestExecutionListeners */ public TestContextManager(Class testClass, String defaultContextLoaderClassName) { this.testContext = new TestContext(testClass, contextCache, defaultContextLoaderClassName); registerTestExecutionListeners(retrieveTestExecutionListeners(testClass)); } + /** - * Returns the {@link TestContext} managed by this - * {@code TestContextManager}. + * Get the {@link TestContext} managed by this {@code TestContextManager}. */ protected final TestContext getTestContext() { return this.testContext; @@ -183,7 +185,8 @@ public class TestContextManager { logger.debug("@TestExecutionListeners is not present for class [" + clazz + "]: using defaults."); } classesList.addAll(getDefaultTestExecutionListenerClasses()); - } else { + } + else { // Traverse the class hierarchy... while (declaringClass != null) { TestExecutionListeners testExecutionListeners = declaringClass.getAnnotation(annotationType); @@ -201,7 +204,8 @@ public class TestContextManager { ObjectUtils.nullSafeToString(listenerClasses)); logger.error(msg); throw new IllegalStateException(msg); - } else if (!ObjectUtils.isEmpty(valueListenerClasses)) { + } + else if (!ObjectUtils.isEmpty(valueListenerClasses)) { listenerClasses = valueListenerClasses; } @@ -264,7 +268,7 @@ public class TestContextManager { * @see #getTestExecutionListeners() */ public void beforeTestClass() throws Exception { - final Class testClass = getTestContext().getTestClass(); + Class testClass = getTestContext().getTestClass(); if (logger.isTraceEnabled()) { logger.trace("beforeTestClass(): class [" + testClass + "]"); } @@ -421,7 +425,7 @@ public class TestContextManager { * @see #getTestExecutionListeners() */ public void afterTestClass() throws Exception { - final Class testClass = getTestContext().getTestClass(); + Class testClass = getTestContext().getTestClass(); if (logger.isTraceEnabled()) { logger.trace("afterTestClass(): class [" + testClass + "]"); } diff --git a/spring-web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java b/spring-web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java index d427f7f3811..93e647e8231 100644 --- a/spring-web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2014 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. @@ -16,9 +16,8 @@ package org.springframework.web.context.support; -import static org.junit.Assert.assertNotNull; - import org.junit.Test; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.AnnotationBeanNameGenerator; @@ -26,18 +25,17 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.hamcrest.CoreMatchers.*; - import static org.junit.Assert.*; /** * @author Chris Beams + * @author Juergen Hoeller */ public class AnnotationConfigWebApplicationContextTests { @Test public void registerSingleClass() { - AnnotationConfigWebApplicationContext ctx = - new AnnotationConfigWebApplicationContext(); + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(Config.class); ctx.refresh(); @@ -47,8 +45,7 @@ public class AnnotationConfigWebApplicationContextTests { @Test public void configLocationWithSingleClass() { - AnnotationConfigWebApplicationContext ctx = - new AnnotationConfigWebApplicationContext(); + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.setConfigLocation(Config.class.getName()); ctx.refresh(); @@ -56,10 +53,19 @@ public class AnnotationConfigWebApplicationContextTests { assertNotNull(bean); } + @Test + public void configLocationWithBasePackage() { + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + ctx.setConfigLocation("org.springframework.web.context.support"); + ctx.refresh(); + + TestBean bean = ctx.getBean(TestBean.class); + assertNotNull(bean); + } + @Test public void withBeanNameGenerator() { - AnnotationConfigWebApplicationContext ctx = - new AnnotationConfigWebApplicationContext(); + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.setBeanNameGenerator(new AnnotationBeanNameGenerator() { @Override public String generateBeanName(BeanDefinition definition, @@ -75,14 +81,15 @@ public class AnnotationConfigWebApplicationContextTests { @Configuration("myConfig") static class Config { + @Bean public TestBean myTestBean() { return new TestBean(); } } - static class NotConfigurationAnnotated { } - static class TestBean { } + static class TestBean { + } }