summaryrefslogtreecommitdiff
path: root/spring-context
diff options
context:
space:
mode:
authorEmmanuel Bourg <ebourg@apache.org>2017-12-04 13:19:26 +0100
committerEmmanuel Bourg <ebourg@apache.org>2017-12-04 13:19:26 +0100
commit1cb0ad9c5dd218623094b3d6369be7a949094e8d (patch)
tree922bf9fc992eb711a1d11ae7d6cb256ab7eb45fd /spring-context
parent86ee2f0c49ee1002a7bcd624aa105caba0166617 (diff)
New upstream version 4.3.13
Diffstat (limited to 'spring-context')
-rw-r--r--spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java31
-rw-r--r--spring-context/src/main/java/org/springframework/context/annotation/Bean.java56
-rw-r--r--spring-context/src/main/java/org/springframework/context/annotation/Configuration.java2
-rw-r--r--spring-context/src/main/java/org/springframework/context/annotation/Primary.java8
-rw-r--r--spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java9
-rw-r--r--spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java14
-rw-r--r--spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java13
-rw-r--r--spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java30
-rw-r--r--spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java9
-rw-r--r--spring-context/src/main/java/org/springframework/stereotype/Component.java4
-rw-r--r--spring-context/src/main/java/org/springframework/stereotype/Controller.java4
-rw-r--r--spring-context/src/main/java/org/springframework/stereotype/Repository.java6
-rw-r--r--spring-context/src/main/java/org/springframework/stereotype/Service.java4
-rw-r--r--spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java4
-rw-r--r--spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java51
-rw-r--r--spring-context/src/test/java/org/springframework/context/annotation/Spr16179Tests.java98
-rw-r--r--spring-context/src/test/java/org/springframework/context/annotation/Spr16217Tests.java131
-rw-r--r--spring-context/src/test/java/org/springframework/context/annotation/Spr8954Tests.java (renamed from spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassSpr8954Tests.java)4
-rw-r--r--spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java35
-rw-r--r--spring-context/src/test/java/org/springframework/scripting/bsh/BshScriptFactoryTests.java43
-rw-r--r--spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java183
-rw-r--r--spring-context/src/test/resources/org/springframework/scripting/bsh/bsh-with-xsd.xml23
22 files changed, 650 insertions, 112 deletions
diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java b/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
index 0ec602b9..c05b44ce 100644
--- a/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
+++ b/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -148,15 +148,19 @@ import org.springframework.core.Ordered;
* can be useful if you do not need to customize everything. See {@link CachingConfigurer}
* Javadoc for further details.
*
- * <p>The {@link #mode()} attribute controls how advice is applied; if the mode is
- * {@link AdviceMode#PROXY} (the default), then the other attributes such as
- * {@link #proxyTargetClass()} control the behavior of the proxying.
+ * <p>The {@link #mode} attribute controls how advice is applied: If the mode is
+ * {@link AdviceMode#PROXY} (the default), then the other attributes control the behavior
+ * of the proxying. Please note that proxy mode allows for interception of calls through
+ * the proxy only; local calls within the same class cannot get intercepted that way.
*
- * <p>If the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the
- * {@link #proxyTargetClass()} attribute is obsolete. Note also that in this case the
- * {@code spring-aspects} module JAR must be present on the classpath.
+ * <p>Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the
+ * value of the {@link #proxyTargetClass} attribute will be ignored. Note also that in
+ * this case the {@code spring-aspects} module JAR must be present on the classpath, with
+ * compile-time weaving or load-time weaving applying the aspect to the affected classes.
+ * There is no proxy involved in such a scenario; local calls will be intercepted as well.
*
* @author Chris Beams
+ * @author Juergen Hoeller
* @since 3.1
* @see CachingConfigurer
* @see CachingConfigurationSelector
@@ -183,16 +187,21 @@ public @interface EnableCaching {
boolean proxyTargetClass() default false;
/**
- * Indicate how caching advice should be applied. The default is
- * {@link AdviceMode#PROXY}.
- * @see AdviceMode
+ * Indicate how caching advice should be applied.
+ * <p><b>The default is {@link AdviceMode#PROXY}.</b>
+ * Please note that proxy mode allows for interception of calls through the proxy
+ * only. Local calls within the same class cannot get intercepted that way;
+ * a caching annotation on such a method within a local call will be ignored
+ * since Spring's interceptor does not even kick in for such a runtime scenario.
+ * For a more advanced mode of interception, consider switching this to
+ * {@link AdviceMode#ASPECTJ}.
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* Indicate the ordering of the execution of the caching advisor
* when multiple advices are applied at a specific joinpoint.
- * The default is {@link Ordered#LOWEST_PRECEDENCE}.
+ * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
*/
int order() default Ordered.LOWEST_PRECEDENCE;
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java
index b15b7c8e..cc57a068 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -60,15 +60,16 @@ import org.springframework.core.annotation.AliasFor;
* }
* </pre>
*
- * <h3>Scope, DependsOn, Primary, and Lazy</h3>
+ * <h3>Profile, Scope, Lazy, DependsOn, Primary, Order</h3>
*
- * <p>Note that the {@code @Bean} annotation does not provide attributes for scope,
- * depends-on, primary, or lazy. Rather, it should be used in conjunction with
- * {@link Scope @Scope}, {@link DependsOn @DependsOn}, {@link Primary @Primary},
- * and {@link Lazy @Lazy} annotations to achieve those semantics. For example:
+ * <p>Note that the {@code @Bean} annotation does not provide attributes for profile,
+ * scope, lazy, depends-on or primary. Rather, it should be used in conjunction with
+ * {@link Scope @Scope}, {@link Lazy @Lazy}, {@link DependsOn @DependsOn} and
+ * {@link Primary @Primary} annotations to declare those semantics. For example:
*
* <pre class="code">
* &#064;Bean
+ * &#064;Profile("production")
* &#064;Scope("prototype")
* public MyBean myBean() {
* // instantiate and configure MyBean obj
@@ -76,6 +77,33 @@ import org.springframework.core.annotation.AliasFor;
* }
* </pre>
*
+ * The semantics of the above-mentioned annotations match their use at the component
+ * class level: {@code Profile} allows for selective inclusion of certain beans.
+ * {@code @Scope} changes the bean's scope from singleton to the specified scope.
+ * {@code @Lazy} only has an actual effect in case of the default singleton scope.
+ * {@code @DependsOn} enforces the creation of specific other beans before this
+ * bean will be created, in addition to any dependencies that the bean expressed
+ * through direct references, which is typically helpful for singleton startup.
+ * {@code @Primary} is a mechanism to resolve ambiguity at the injection point level
+ * if a single target component needs to be injected but several beans match by type.
+ *
+ * <p>Additionally, {@code @Bean} methods may also declare qualifier annotations
+ * and {@link org.springframework.core.annotation.Order @Order} values, to be
+ * taken into account during injection point resolution just like corresponding
+ * annotations on the corresponding component classes but potentially being very
+ * individual per bean definition (in case of multiple definitions with the same
+ * bean class). Qualifiers narrow the set of candidates after the initial type match;
+ * order values determine the order of resolved elements in case of collection
+ * injection points (with several target beans matching by type and qualifier).
+ *
+ * <p><b>NOTE:</b> {@code @Order} values may influence priorities at injection points
+ * but please be aware that they do not influence singleton startup order which is an
+ * orthogonal concern determined by dependency relationships and {@code @DependsOn}
+ * declarations as mentioned above. Also, {@link javax.annotation.Priority} is not
+ * available at this level since it cannot be declared on methods; its semantics can
+ * be modelled through {@code @Order} values in combination with {@code @Primary} on
+ * a single bean per type.
+ *
* <h3>{@code @Bean} Methods in {@code @Configuration} Classes</h3>
*
* <p>Typically, {@code @Bean} methods are declared within {@code @Configuration}
@@ -143,7 +171,7 @@ import org.springframework.core.annotation.AliasFor;
*
* <h3>Bootstrapping</h3>
*
- * <p>See @{@link Configuration} Javadoc for further details including how to bootstrap
+ * <p>See the @{@link Configuration} javadoc for further details including how to bootstrap
* the container using {@link AnnotationConfigApplicationContext} and friends.
*
* <h3>{@code BeanFactoryPostProcessor}-returning {@code @Bean} methods</h3>
@@ -157,8 +185,8 @@ import org.springframework.core.annotation.AliasFor;
*
* <pre class="code">
* &#064;Bean
- * public static PropertyPlaceholderConfigurer ppc() {
- * // instantiate, configure and return ppc ...
+ * public static PropertySourcesPlaceholderConfigurer pspc() {
+ * // instantiate, configure and return pspc ...
* }
* </pre>
*
@@ -228,6 +256,8 @@ public @interface Bean {
* Not commonly used, given that the method may be called programmatically directly
* within the body of a Bean-annotated method.
* <p>The default value is {@code ""}, indicating no init method to be called.
+ * @see org.springframework.beans.factory.InitializingBean
+ * @see org.springframework.context.ConfigurableApplicationContext#refresh()
*/
String initMethod() default "";
@@ -248,12 +278,14 @@ public @interface Bean {
* creation time).
* <p>To disable destroy method inference for a particular {@code @Bean}, specify an
* empty string as the value, e.g. {@code @Bean(destroyMethod="")}. Note that the
- * {@link org.springframework.beans.factory.DisposableBean} and the
- * {@link java.io.Closeable}/{@link java.lang.AutoCloseable} interfaces will
- * nevertheless get detected and the corresponding destroy/close method invoked.
+ * {@link org.springframework.beans.factory.DisposableBean} callback interface will
+ * nevertheless get detected and the corresponding destroy method invoked: In other
+ * words, {@code destroyMethod=""} only affects custom close/shutdown methods and
+ * {@link java.io.Closeable}/{@link java.lang.AutoCloseable} declared close methods.
* <p>Note: Only invoked on beans whose lifecycle is under the full control of the
* factory, which is always the case for singletons but not guaranteed for any
* other scope.
+ * @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.context.ConfigurableApplicationContext#close()
*/
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java b/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java
index 49019333..7d8e8a29 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java
@@ -412,7 +412,7 @@ public @interface Configuration {
* component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.
* If the Configuration class is registered as a traditional XML bean definition,
* the name/id of the bean element will take precedence.
- * @return the specified bean name, if any
+ * @return the suggested component name, if any (or empty String otherwise)
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
String value() default "";
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Primary.java b/spring-context/src/main/java/org/springframework/context/annotation/Primary.java
index 37b6480e..bac49c79 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/Primary.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/Primary.java
@@ -48,18 +48,18 @@ import java.lang.annotation.Target;
* }
*
* &#064;Component
- * public class JdbcFooRepository {
+ * public class JdbcFooRepository extends FooRepository {
*
- * public JdbcFooService(DataSource dataSource) {
+ * public JdbcFooRepository(DataSource dataSource) {
* // ...
* }
* }
*
* &#064;Primary
* &#064;Component
- * public class HibernateFooRepository {
+ * public class HibernateFooRepository extends FooRepository {
*
- * public HibernateFooService(SessionFactory sessionFactory) {
+ * public HibernateFooRepository(SessionFactory sessionFactory) {
* // ...
* }
* }
diff --git a/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java b/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java
index 278021f5..24b8cf97 100644
--- a/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java
+++ b/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java
@@ -31,7 +31,6 @@ import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
import org.springframework.aop.scope.ScopedObject;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.aop.support.AopUtils;
-import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext;
@@ -65,7 +64,7 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton,
@Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ public void setApplicationContext(ApplicationContext applicationContext) {
Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
"ApplicationContext does not implement ConfigurableApplicationContext");
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
@@ -119,9 +118,9 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton,
*/
protected List<EventListenerFactory> getEventListenerFactories() {
Map<String, EventListenerFactory> beans = this.applicationContext.getBeansOfType(EventListenerFactory.class);
- List<EventListenerFactory> allFactories = new ArrayList<EventListenerFactory>(beans.values());
- AnnotationAwareOrderComparator.sort(allFactories);
- return allFactories;
+ List<EventListenerFactory> factories = new ArrayList<EventListenerFactory>(beans.values());
+ AnnotationAwareOrderComparator.sort(factories);
+ return factories;
}
protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
index 6791f436..587e884c 100644
--- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -381,7 +381,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
else {
applicationEvent = new PayloadApplicationEvent<Object>(this, event);
if (eventType == null) {
- eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
+ eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
@@ -995,11 +995,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
// Stop all Lifecycle beans, to avoid delays during individual destruction.
- try {
- getLifecycleProcessor().onClose();
- }
- catch (Throwable ex) {
- logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
+ if (this.lifecycleProcessor != null) {
+ try {
+ this.lifecycleProcessor.onClose();
+ }
+ catch (Throwable ex) {
+ logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
+ }
}
// Destroy all cached singletons in the context's BeanFactory.
diff --git a/spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java b/spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java
index 89ed106f..f9b135c4 100644
--- a/spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java
+++ b/spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -94,9 +94,14 @@ class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor,
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) {
if (this.applicationContext != null && bean instanceof ApplicationListener) {
- ApplicationEventMulticaster multicaster = this.applicationContext.getApplicationEventMulticaster();
- multicaster.removeApplicationListener((ApplicationListener<?>) bean);
- multicaster.removeApplicationListenerBean(beanName);
+ try {
+ ApplicationEventMulticaster multicaster = this.applicationContext.getApplicationEventMulticaster();
+ multicaster.removeApplicationListener((ApplicationListener<?>) bean);
+ multicaster.removeApplicationListenerBean(beanName);
+ }
+ catch (IllegalStateException ex) {
+ // ApplicationEventMulticaster not initialized yet - no need to remove a listener
+ }
}
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
index 8b7eb4c0..a06ed0cb 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -58,14 +58,6 @@ import org.springframework.core.Ordered;
* }
* }</pre>
*
- * <p>The {@link #mode} attribute controls how advice is applied; if the mode is
- * {@link AdviceMode#PROXY} (the default), then the other attributes control the behavior
- * of the proxying.
- *
- * <p>Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the
- * value of the {@link #proxyTargetClass} attribute will be ignored. Note also that in
- * this case the {@code spring-aspects} module JAR must be present on the classpath.
- *
* <p>By default, Spring will be searching for an associated thread pool definition:
* either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context,
* or an {@link java.util.concurrent.Executor} bean named "taskExecutor" otherwise. If
@@ -140,6 +132,17 @@ import org.springframework.core.Ordered;
* demonstrates how the JavaConfig-based approach allows for maximum configurability
* through direct access to actual componentry.
*
+ * <p>The {@link #mode} attribute controls how advice is applied: If the mode is
+ * {@link AdviceMode#PROXY} (the default), then the other attributes control the behavior
+ * of the proxying. Please note that proxy mode allows for interception of calls through
+ * the proxy only; local calls within the same class cannot get intercepted that way.
+ *
+ * <p>Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the
+ * value of the {@link #proxyTargetClass} attribute will be ignored. Note also that in
+ * this case the {@code spring-aspects} module JAR must be present on the classpath, with
+ * compile-time weaving or load-time weaving applying the aspect to the affected classes.
+ * There is no proxy involved in such a scenario; local calls will be intercepted as well.
+ *
* @author Chris Beams
* @author Juergen Hoeller
* @author Stephane Nicoll
@@ -182,8 +185,13 @@ public @interface EnableAsync {
/**
* Indicate how async advice should be applied.
- * <p>The default is {@link AdviceMode#PROXY}.
- * @see AdviceMode
+ * <p><b>The default is {@link AdviceMode#PROXY}.</b>
+ * Please note that proxy mode allows for interception of calls through the proxy
+ * only. Local calls within the same class cannot get intercepted that way; an
+ * {@link Async} annotation on such a method within a local call will be ignored
+ * since Spring's interceptor does not even kick in for such a runtime scenario.
+ * For a more advanced mode of interception, consider switching this to
+ * {@link AdviceMode#ASPECTJ}.
*/
AdviceMode mode() default AdviceMode.PROXY;
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
index 77de2543..ad9cb8d1 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
@@ -17,10 +17,12 @@
package org.springframework.scheduling.annotation;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
@@ -53,6 +55,7 @@ import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.config.CronTask;
@@ -207,9 +210,11 @@ public class ScheduledAnnotationBeanPostProcessor
}
if (this.beanFactory instanceof ListableBeanFactory) {
- Map<String, SchedulingConfigurer> configurers =
+ Map<String, SchedulingConfigurer> beans =
((ListableBeanFactory) this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
- for (SchedulingConfigurer configurer : configurers.values()) {
+ List<SchedulingConfigurer> configurers = new ArrayList<SchedulingConfigurer>(beans.values());
+ AnnotationAwareOrderComparator.sort(configurers);
+ for (SchedulingConfigurer configurer : configurers) {
configurer.configureTasks(this.registrar);
}
}
diff --git a/spring-context/src/main/java/org/springframework/stereotype/Component.java b/spring-context/src/main/java/org/springframework/stereotype/Component.java
index 8b3c75df..43ca86d4 100644
--- a/spring-context/src/main/java/org/springframework/stereotype/Component.java
+++ b/spring-context/src/main/java/org/springframework/stereotype/Component.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -47,7 +47,7 @@ public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
- * @return the suggested component name, if any
+ * @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
diff --git a/spring-context/src/main/java/org/springframework/stereotype/Controller.java b/spring-context/src/main/java/org/springframework/stereotype/Controller.java
index 189776d4..95db8d43 100644
--- a/spring-context/src/main/java/org/springframework/stereotype/Controller.java
+++ b/spring-context/src/main/java/org/springframework/stereotype/Controller.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -46,7 +46,7 @@ public @interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
- * @return the suggested component name, if any
+ * @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
diff --git a/spring-context/src/main/java/org/springframework/stereotype/Repository.java b/spring-context/src/main/java/org/springframework/stereotype/Repository.java
index 0709b72b..2aa5289e 100644
--- a/spring-context/src/main/java/org/springframework/stereotype/Repository.java
+++ b/spring-context/src/main/java/org/springframework/stereotype/Repository.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -27,7 +27,7 @@ import java.lang.annotation.Target;
* Domain-Driven Design (Evans, 2003) as "a mechanism for encapsulating storage,
* retrieval, and search behavior which emulates a collection of objects".
*
- * <p>Teams implementing traditional J2EE patterns such as "Data Access Object"
+ * <p>Teams implementing traditional Java EE patterns such as "Data Access Object"
* may also apply this stereotype to DAO classes, though care should be taken to
* understand the distinction between Data Access Object and DDD-style repositories
* before doing so. This annotation is a general-purpose stereotype and individual teams
@@ -62,7 +62,7 @@ public @interface Repository {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
- * @return the suggested component name, if any
+ * @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
diff --git a/spring-context/src/main/java/org/springframework/stereotype/Service.java b/spring-context/src/main/java/org/springframework/stereotype/Service.java
index 09307383..f9dadc38 100644
--- a/spring-context/src/main/java/org/springframework/stereotype/Service.java
+++ b/spring-context/src/main/java/org/springframework/stereotype/Service.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -48,7 +48,7 @@ public @interface Service {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
- * @return the suggested component name, if any
+ * @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java
index 90ab4839..7d6b8f75 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java
@@ -263,8 +263,8 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
*/
protected Object getRejectedValue(String field, ConstraintViolation<Object> violation, BindingResult bindingResult) {
Object invalidValue = violation.getInvalidValue();
- if (!"".equals(field) && (invalidValue == violation.getLeafBean() ||
- (!field.contains("[]") && (field.contains("[") || field.contains("."))))) {
+ if (!"".equals(field) && !field.contains("[]") &&
+ (invalidValue == violation.getLeafBean() || field.contains("[") || field.contains("."))) {
// Possibly a bean constraint with property path: retrieve the actual property value.
// However, explicitly avoid this for "address[]" style paths that we can't handle.
invalidValue = bindingResult.getRawFieldValue(field);
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java b/spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java
index a9b1bf88..ff941127 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -17,10 +17,10 @@
package org.springframework.context.annotation;
import java.io.Closeable;
-import java.io.IOException;
import org.junit.Test;
+import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
@@ -36,8 +36,7 @@ public class DestroyMethodInferenceTests {
@Test
public void beanMethods() {
- ConfigurableApplicationContext ctx =
- new AnnotationConfigApplicationContext(Config.class);
+ ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
WithExplicitDestroyMethod c0 = ctx.getBean(WithExplicitDestroyMethod.class);
WithLocalCloseMethod c1 = ctx.getBean("c1", WithLocalCloseMethod.class);
WithLocalCloseMethod c2 = ctx.getBean("c2", WithLocalCloseMethod.class);
@@ -47,6 +46,7 @@ public class DestroyMethodInferenceTests {
WithNoCloseMethod c6 = ctx.getBean("c6", WithNoCloseMethod.class);
WithLocalShutdownMethod c7 = ctx.getBean("c7", WithLocalShutdownMethod.class);
WithInheritedCloseMethod c8 = ctx.getBean("c8", WithInheritedCloseMethod.class);
+ WithDisposableBean c9 = ctx.getBean("c9", WithDisposableBean.class);
assertThat(c0.closed, is(false));
assertThat(c1.closed, is(false));
@@ -57,6 +57,7 @@ public class DestroyMethodInferenceTests {
assertThat(c6.closed, is(false));
assertThat(c7.closed, is(false));
assertThat(c8.closed, is(false));
+ assertThat(c9.closed, is(false));
ctx.close();
assertThat("c0", c0.closed, is(true));
assertThat("c1", c1.closed, is(true));
@@ -67,6 +68,7 @@ public class DestroyMethodInferenceTests {
assertThat("c6", c6.closed, is(false));
assertThat("c7", c7.closed, is(true));
assertThat("c8", c8.closed, is(false));
+ assertThat("c9", c9.closed, is(true));
}
@Test
@@ -91,9 +93,11 @@ public class DestroyMethodInferenceTests {
assertThat(x8.closed, is(false));
}
+
@Configuration
static class Config {
- @Bean(destroyMethod="explicitClose")
+
+ @Bean(destroyMethod = "explicitClose")
public WithExplicitDestroyMethod c0() {
return new WithExplicitDestroyMethod();
}
@@ -118,12 +122,12 @@ public class DestroyMethodInferenceTests {
return new WithInheritedCloseMethod();
}
- @Bean(destroyMethod="other")
+ @Bean(destroyMethod = "other")
public WithInheritedCloseMethod c5() {
return new WithInheritedCloseMethod() {
@Override
- public void close() throws IOException {
- throw new RuntimeException("close() should not be called");
+ public void close() {
+ throw new IllegalStateException("close() should not be called");
}
@SuppressWarnings("unused")
public void other() {
@@ -146,37 +150,66 @@ public class DestroyMethodInferenceTests {
public WithInheritedCloseMethod c8() {
return new WithInheritedCloseMethod();
}
+
+ @Bean(destroyMethod = "")
+ public WithDisposableBean c9() {
+ return new WithDisposableBean();
+ }
}
static class WithExplicitDestroyMethod {
+
boolean closed = false;
+
public void explicitClose() {
closed = true;
}
}
+
static class WithLocalCloseMethod {
+
boolean closed = false;
+
public void close() {
closed = true;
}
}
+
static class WithInheritedCloseMethod implements Closeable {
+
+ boolean closed = false;
+
+ @Override
+ public void close() {
+ closed = true;
+ }
+ }
+
+
+ static class WithDisposableBean implements DisposableBean {
+
boolean closed = false;
+
@Override
- public void close() throws IOException {
+ public void destroy() {
closed = true;
}
}
+
static class WithNoCloseMethod {
+
boolean closed = false;
}
+
static class WithLocalShutdownMethod {
+
boolean closed = false;
+
public void shutdown() {
closed = true;
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/Spr16179Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/Spr16179Tests.java
new file mode 100644
index 00000000..1cb2e2e7
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/context/annotation/Spr16179Tests.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2002-2017 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 org.junit.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Juergen Hoeller
+ * @author Oliver Gierke
+ */
+public class Spr16179Tests {
+
+ @Test
+ public void repro() {
+ AnnotationConfigApplicationContext bf =
+ new AnnotationConfigApplicationContext(AssemblerConfig.class, AssemblerInjection.class);
+
+ assertSame(bf.getBean("someAssembler"), bf.getBean(AssemblerInjection.class).assembler0);
+ // assertNull(bf.getBean(AssemblerInjection.class).assembler1); TODO: accidental match
+ // assertNull(bf.getBean(AssemblerInjection.class).assembler2);
+ assertSame(bf.getBean("pageAssembler"), bf.getBean(AssemblerInjection.class).assembler3);
+ assertSame(bf.getBean("pageAssembler"), bf.getBean(AssemblerInjection.class).assembler4);
+ assertSame(bf.getBean("pageAssembler"), bf.getBean(AssemblerInjection.class).assembler5);
+ assertSame(bf.getBean("pageAssembler"), bf.getBean(AssemblerInjection.class).assembler6);
+ }
+
+
+ @Configuration
+ static class AssemblerConfig {
+
+ @Bean
+ PageAssemblerImpl<?> pageAssembler() {
+ return new PageAssemblerImpl<>();
+ }
+
+ @Bean
+ Assembler<SomeType> someAssembler() {
+ return new Assembler<SomeType>() {};
+ }
+ }
+
+
+ public static class AssemblerInjection {
+
+ @Autowired(required = false)
+ Assembler<SomeType> assembler0;
+
+ @Autowired(required = false)
+ Assembler<SomeOtherType> assembler1;
+
+ @Autowired(required = false)
+ Assembler<Page<String>> assembler2;
+
+ @Autowired(required = false)
+ Assembler<Page> assembler3;
+
+ @Autowired(required = false)
+ Assembler<Page<?>> assembler4;
+
+ @Autowired(required = false)
+ PageAssembler<?> assembler5;
+
+ @Autowired(required = false)
+ PageAssembler<String> assembler6;
+ }
+
+
+ interface Assembler<T> {}
+
+ interface PageAssembler<T> extends Assembler<Page<T>> {}
+
+ static class PageAssemblerImpl<T> implements PageAssembler<T> {}
+
+ interface Page<T> {}
+
+ interface SomeType {}
+
+ interface SomeOtherType {}
+
+}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/Spr16217Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/Spr16217Tests.java
new file mode 100644
index 00000000..9c0684b4
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/context/annotation/Spr16217Tests.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2002-2017 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 org.junit.Ignore;
+import org.junit.Test;
+
+import org.springframework.core.type.AnnotatedTypeMetadata;
+
+/**
+ * @author Andy Wilkinson
+ * @author Juergen Hoeller
+ */
+public class Spr16217Tests {
+
+ @Test
+ @Ignore("TODO")
+ public void baseConfigurationIsIncludedWhenFirstSuperclassReferenceIsSkippedInRegisterBeanPhase() {
+ try (AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext(RegisterBeanPhaseImportingConfiguration.class)) {
+ context.getBean("someBean");
+ }
+ }
+
+ @Test
+ public void baseConfigurationIsIncludedWhenFirstSuperclassReferenceIsSkippedInParseConfigurationPhase() {
+ try (AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext(ParseConfigurationPhaseImportingConfiguration.class)) {
+ context.getBean("someBean");
+ }
+ }
+
+ @Test
+ public void baseConfigurationIsIncludedOnceWhenBothConfigurationClassesAreActive() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.setAllowBeanDefinitionOverriding(false);
+ context.register(UnconditionalImportingConfiguration.class);
+ context.refresh();
+ try {
+ context.getBean("someBean");
+ }
+ finally {
+ context.close();
+ }
+ }
+
+
+ public static class RegisterBeanPhaseCondition implements ConfigurationCondition {
+
+ @Override
+ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
+ return false;
+ }
+
+ @Override
+ public ConfigurationPhase getConfigurationPhase() {
+ return ConfigurationPhase.REGISTER_BEAN;
+ }
+ }
+
+
+ public static class ParseConfigurationPhaseCondition implements ConfigurationCondition {
+
+ @Override
+ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
+ return false;
+ }
+
+ @Override
+ public ConfigurationPhase getConfigurationPhase() {
+ return ConfigurationPhase.PARSE_CONFIGURATION;
+ }
+ }
+
+
+ @Import({RegisterBeanPhaseConditionConfiguration.class, BarConfiguration.class})
+ public static class RegisterBeanPhaseImportingConfiguration {
+ }
+
+
+ @Import({ParseConfigurationPhaseConditionConfiguration.class, BarConfiguration.class})
+ public static class ParseConfigurationPhaseImportingConfiguration {
+ }
+
+
+ @Import({UnconditionalConfiguration.class, BarConfiguration.class})
+ public static class UnconditionalImportingConfiguration {
+ }
+
+
+ public static class BaseConfiguration {
+
+ @Bean
+ public String someBean() {
+ return "foo";
+ }
+ }
+
+
+ @Conditional(RegisterBeanPhaseCondition.class)
+ public static class RegisterBeanPhaseConditionConfiguration extends BaseConfiguration {
+ }
+
+
+ @Conditional(ParseConfigurationPhaseCondition.class)
+ public static class ParseConfigurationPhaseConditionConfiguration extends BaseConfiguration {
+ }
+
+
+ public static class UnconditionalConfiguration extends BaseConfiguration {
+ }
+
+
+ public static class BarConfiguration extends BaseConfiguration {
+ }
+
+}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassSpr8954Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/Spr8954Tests.java
index 114fea7c..c321cdbe 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassSpr8954Tests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/Spr8954Tests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -39,7 +39,7 @@ import static org.junit.Assert.*;
* @author Oliver Gierke
*/
@SuppressWarnings("resource")
-public class ConfigurationClassSpr8954Tests {
+public class Spr8954Tests {
@Test
public void repro() {
diff --git a/spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java b/spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java
index 5b864c3e..91310213 100644
--- a/spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java
+++ b/spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -211,6 +211,39 @@ public class ResourceBundleMessageSourceTests {
}
@Test
+ public void testDefaultApplicationContextMessageSourceWithParent() {
+ GenericApplicationContext ac = new GenericApplicationContext();
+ GenericApplicationContext parent = new GenericApplicationContext();
+ parent.refresh();
+ ac.setParent(parent);
+ ac.refresh();
+ assertEquals("default", ac.getMessage("code1", null, "default", Locale.ENGLISH));
+ assertEquals("default value", ac.getMessage("code1", new Object[] {"value"}, "default {0}", Locale.ENGLISH));
+ }
+
+ @Test
+ public void testStaticApplicationContextMessageSourceWithStaticParent() {
+ StaticApplicationContext ac = new StaticApplicationContext();
+ StaticApplicationContext parent = new StaticApplicationContext();
+ parent.refresh();
+ ac.setParent(parent);
+ ac.refresh();
+ assertEquals("default", ac.getMessage("code1", null, "default", Locale.ENGLISH));
+ assertEquals("default value", ac.getMessage("code1", new Object[] {"value"}, "default {0}", Locale.ENGLISH));
+ }
+
+ @Test
+ public void testStaticApplicationContextMessageSourceWithDefaultParent() {
+ StaticApplicationContext ac = new StaticApplicationContext();
+ GenericApplicationContext parent = new GenericApplicationContext();
+ parent.refresh();
+ ac.setParent(parent);
+ ac.refresh();
+ assertEquals("default", ac.getMessage("code1", null, "default", Locale.ENGLISH));
+ assertEquals("default value", ac.getMessage("code1", new Object[] {"value"}, "default {0}", Locale.ENGLISH));
+ }
+
+ @Test
public void testResourceBundleMessageSourceStandalone() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages");
diff --git a/spring-context/src/test/java/org/springframework/scripting/bsh/BshScriptFactoryTests.java b/spring-context/src/test/java/org/springframework/scripting/bsh/BshScriptFactoryTests.java
index 1d3ba17d..8b8c113a 100644
--- a/spring-context/src/test/java/org/springframework/scripting/bsh/BshScriptFactoryTests.java
+++ b/spring-context/src/test/java/org/springframework/scripting/bsh/BshScriptFactoryTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2017 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,6 +16,7 @@
package org.springframework.scripting.bsh;
+import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
@@ -47,7 +48,7 @@ import static org.mockito.BDDMockito.*;
public class BshScriptFactoryTests {
@Test
- public void staticScript() throws Exception {
+ public void staticScript() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bshContext.xml", getClass());
assertTrue(Arrays.asList(ctx.getBeanNamesForType(Calculator.class)).contains("calculator"));
@@ -75,7 +76,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void staticScriptWithNullReturnValue() throws Exception {
+ public void staticScriptWithNullReturnValue() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bshContext.xml", getClass());
assertTrue(Arrays.asList(ctx.getBeanNamesForType(Messenger.class)).contains("messengerWithConfig"));
@@ -86,7 +87,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void staticScriptWithTwoInterfacesSpecified() throws Exception {
+ public void staticScriptWithTwoInterfacesSpecified() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("bshContext.xml", getClass());
assertTrue(Arrays.asList(ctx.getBeanNamesForType(Messenger.class)).contains("messengerWithConfigExtra"));
@@ -100,7 +101,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void staticWithScriptReturningInstance() throws Exception {
+ public void staticWithScriptReturningInstance() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("bshContext.xml", getClass());
assertTrue(Arrays.asList(ctx.getBeanNamesForType(Messenger.class)).contains("messengerInstance"));
@@ -114,7 +115,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void staticScriptImplementingInterface() throws Exception {
+ public void staticScriptImplementingInterface() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("bshContext.xml", getClass());
assertTrue(Arrays.asList(ctx.getBeanNamesForType(Messenger.class)).contains("messengerImpl"));
@@ -128,7 +129,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void staticPrototypeScript() throws Exception {
+ public void staticPrototypeScript() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bshContext.xml", getClass());
ConfigurableMessenger messenger = (ConfigurableMessenger) ctx.getBean("messengerPrototype");
ConfigurableMessenger messenger2 = (ConfigurableMessenger) ctx.getBean("messengerPrototype");
@@ -147,7 +148,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void nonStaticScript() throws Exception {
+ public void nonStaticScript() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bshRefreshableContext.xml", getClass());
Messenger messenger = (Messenger) ctx.getBean("messenger");
@@ -165,7 +166,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void nonStaticPrototypeScript() throws Exception {
+ public void nonStaticPrototypeScript() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bshRefreshableContext.xml", getClass());
ConfigurableMessenger messenger = (ConfigurableMessenger) ctx.getBean("messengerPrototype");
ConfigurableMessenger messenger2 = (ConfigurableMessenger) ctx.getBean("messengerPrototype");
@@ -189,7 +190,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void scriptCompilationException() throws Exception {
+ public void scriptCompilationException() {
try {
new ClassPathXmlApplicationContext("org/springframework/scripting/bsh/bshBrokenContext.xml");
fail("Must throw exception for broken script file");
@@ -200,7 +201,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void scriptThatCompilesButIsJustPlainBad() throws Exception {
+ public void scriptThatCompilesButIsJustPlainBad() throws IOException {
ScriptSource script = mock(ScriptSource.class);
final String badScript = "String getMessage() { throw new IllegalArgumentException(); }";
given(script.getScriptAsString()).willReturn(badScript);
@@ -217,7 +218,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void ctorWithNullScriptSourceLocator() throws Exception {
+ public void ctorWithNullScriptSourceLocator() {
try {
new BshScriptFactory(null, Messenger.class);
fail("Must have thrown exception by this point.");
@@ -227,9 +228,9 @@ public class BshScriptFactoryTests {
}
@Test
- public void ctorWithEmptyScriptSourceLocator() throws Exception {
+ public void ctorWithEmptyScriptSourceLocator() {
try {
- new BshScriptFactory("", new Class<?>[] {Messenger.class});
+ new BshScriptFactory("", Messenger.class);
fail("Must have thrown exception by this point.");
}
catch (IllegalArgumentException expected) {
@@ -237,9 +238,9 @@ public class BshScriptFactoryTests {
}
@Test
- public void ctorWithWhitespacedScriptSourceLocator() throws Exception {
+ public void ctorWithWhitespacedScriptSourceLocator() {
try {
- new BshScriptFactory("\n ", new Class<?>[] {Messenger.class});
+ new BshScriptFactory("\n ", Messenger.class);
fail("Must have thrown exception by this point.");
}
catch (IllegalArgumentException expected) {
@@ -247,7 +248,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void resourceScriptFromTag() throws Exception {
+ public void resourceScriptFromTag() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("bsh-with-xsd.xml", getClass());
TestBean testBean = (TestBean) ctx.getBean("testBean");
@@ -286,7 +287,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void prototypeScriptFromTag() throws Exception {
+ public void prototypeScriptFromTag() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bsh-with-xsd.xml", getClass());
ConfigurableMessenger messenger = (ConfigurableMessenger) ctx.getBean("messengerPrototype");
ConfigurableMessenger messenger2 = (ConfigurableMessenger) ctx.getBean("messengerPrototype");
@@ -302,7 +303,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void inlineScriptFromTag() throws Exception {
+ public void inlineScriptFromTag() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bsh-with-xsd.xml", getClass());
Calculator calculator = (Calculator) ctx.getBean("calculator");
assertNotNull(calculator);
@@ -310,7 +311,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void refreshableFromTag() throws Exception {
+ public void refreshableFromTag() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bsh-with-xsd.xml", getClass());
Messenger messenger = (Messenger) ctx.getBean("refreshableMessenger");
assertEquals("Hello World!", messenger.getMessage());
@@ -318,7 +319,7 @@ public class BshScriptFactoryTests {
}
@Test
- public void applicationEventListener() throws Exception {
+ public void applicationEventListener() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bsh-with-xsd.xml", getClass());
Messenger eventListener = (Messenger) ctx.getBean("eventListener");
ctx.publishEvent(new MyEvent(ctx));
diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java
index 307acc59..b16b39c6 100644
--- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java
+++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java
@@ -21,13 +21,22 @@ import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
+import java.util.Set;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
+import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;
+import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@@ -138,6 +147,43 @@ public class SpringValidatorAdapterTests {
is("Email required"));
}
+ @Test // SPR-16177
+ public void testWithList() {
+ Parent parent = new Parent();
+ parent.setName("Parent whit list");
+ parent.getChildList().addAll(createChildren(parent));
+
+ BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent");
+ validatorAdapter.validate(parent, errors);
+
+ assertTrue(errors.getErrorCount() > 0);
+ }
+
+ @Test // SPR-16177
+ public void testWithSet() {
+ Parent parent = new Parent();
+ parent.setName("Parent whith set");
+ parent.getChildSet().addAll(createChildren(parent));
+
+ BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent");
+ validatorAdapter.validate(parent, errors);
+
+ assertTrue(errors.getErrorCount() > 0);
+ }
+
+ private List<Child> createChildren(Parent parent) {
+ Child child1 = new Child();
+ child1.setName("Child1");
+ child1.setAge(null);
+ child1.setParent(parent);
+
+ Child child2 = new Child();
+ child2.setName(null);
+ child2.setAge(17);
+ child2.setParent(parent);
+
+ return Arrays.asList(child1, child2);
+ }
@Same(field = "password", comparingField = "confirmPassword")
@Same(field = "email", comparingField = "confirmEmail")
@@ -255,4 +301,141 @@ public class SpringValidatorAdapterTests {
}
}
+
+ public static class Parent {
+
+ private Integer id;
+
+ @NotNull
+ private String name;
+
+ @Valid
+ private Set<Child> childSet = new LinkedHashSet<>();
+
+ @Valid
+ private List<Child> childList = new LinkedList<>();
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set<Child> getChildSet() {
+ return childSet;
+ }
+
+ public void setChildSet(Set<Child> childSet) {
+ this.childSet = childSet;
+ }
+
+ public List<Child> getChildList() {
+ return childList;
+ }
+
+ public void setChildList(List<Child> childList) {
+ this.childList = childList;
+ }
+ }
+
+
+ @AnythingValid
+ public static class Child {
+
+ private Integer id;
+
+ @javax.validation.constraints.NotNull
+ private String name;
+
+ @javax.validation.constraints.NotNull
+ private Integer age;
+
+ @javax.validation.constraints.NotNull
+ private Parent parent;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+ }
+
+
+ @Constraint(validatedBy = AnythingValidator.class)
+ @Retention(RUNTIME)
+ public @interface AnythingValid {
+
+ String message() default "{AnythingValid.message}";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+
+ public static class AnythingValidator implements ConstraintValidator<AnythingValid, Object> {
+
+ private static final String ID = "id";
+
+ @Override
+ public void initialize(AnythingValid constraintAnnotation) {
+ }
+
+ @Override
+ public boolean isValid(Object value, ConstraintValidatorContext context) {
+ List<Field> fieldsErros = new ArrayList<>();
+ Arrays.asList(value.getClass().getDeclaredFields()).forEach(f -> {
+ f.setAccessible(true);
+ try {
+ if (!f.getName().equals(ID) && f.get(value) == null) {
+ fieldsErros.add(f);
+ context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
+ .addNode(f.getName())
+ .addConstraintViolation();
+ }
+ } catch (IllegalAccessException ex) {
+ throw new IllegalStateException(ex);
+ }
+
+ });
+ return fieldsErros.isEmpty();
+ }
+ }
+
}
diff --git a/spring-context/src/test/resources/org/springframework/scripting/bsh/bsh-with-xsd.xml b/spring-context/src/test/resources/org/springframework/scripting/bsh/bsh-with-xsd.xml
index 60ea7467..e1ce67e1 100644
--- a/spring-context/src/test/resources/org/springframework/scripting/bsh/bsh-with-xsd.xml
+++ b/spring-context/src/test/resources/org/springframework/scripting/bsh/bsh-with-xsd.xml
@@ -1,9 +1,8 @@
<?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:lang="http://www.springframework.org/schema/lang"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
-
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:lang="http://www.springframework.org/schema/lang"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd">
<lang:bsh id="messenger" script-source="classpath:org/springframework/scripting/bsh/Messenger.bsh"
@@ -29,7 +28,7 @@
</lang:bsh>
<lang:bsh id="messengerByType" script-source="classpath:org/springframework/scripting/bsh/MessengerImpl.bsh"
- autowire="byType" dependency-check="objects" init-method="init" destroy-method="destroy">
+ autowire="byType" init-method="init" destroy-method="destroy">
</lang:bsh>
<lang:bsh id="messengerByName" script-source="classpath:org/springframework/scripting/bsh/MessengerImpl.bsh"
@@ -52,12 +51,12 @@
<lang:property name="message" value="Hello World!"/>
</lang:bsh>
-<lang:bsh id="eventListener" script-interfaces="org.springframework.context.ApplicationListener,org.springframework.scripting.Messenger" >
-<lang:inline-script><![CDATA[
-int count;
-void onApplicationEvent (org.springframework.context.ApplicationEvent event) { count++; System.out.println(event); }
-String getMessage() { return "count=" + count; }
-]]></lang:inline-script>
-</lang:bsh>
+ <lang:bsh id="eventListener" script-interfaces="org.springframework.context.ApplicationListener,org.springframework.scripting.Messenger" >
+ <lang:inline-script><![CDATA[
+ int count;
+ void onApplicationEvent (org.springframework.context.ApplicationEvent event) { count++; System.out.println(event); }
+ String getMessage() { return "count=" + count; }
+ ]]></lang:inline-script>
+ </lang:bsh>
</beans>