diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2017-12-04 13:19:26 +0100 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2017-12-04 13:19:26 +0100 |
commit | 1cb0ad9c5dd218623094b3d6369be7a949094e8d (patch) | |
tree | 922bf9fc992eb711a1d11ae7d6cb256ab7eb45fd /spring-context | |
parent | 86ee2f0c49ee1002a7bcd624aa105caba0166617 (diff) |
New upstream version 4.3.13
Diffstat (limited to 'spring-context')
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"> * @Bean + * @Profile("production") * @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"> * @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; * } * * @Component - * public class JdbcFooRepository { + * public class JdbcFooRepository extends FooRepository { * - * public JdbcFooService(DataSource dataSource) { + * public JdbcFooRepository(DataSource dataSource) { * // ... * } * } * * @Primary * @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> |