From c44447f622b35331e6e3a41eb7f8578fcd62b7c1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:01 +0100 Subject: [PATCH 1/8] Avoid early initialization of empty interceptor names Closes gh-12238 --- .../aop/framework/ProxyFactoryBean.java | 12 ++---- .../aop/framework/ProxyFactoryBeanTests.java | 38 +++++++++++++++++-- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java index 6c9efc49f0d..5e1ed1603fa 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -421,11 +421,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport * are unaffected by such changes. */ private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException { - if (this.advisorChainInitialized) { - return; - } - - if (!ObjectUtils.isEmpty(this.interceptorNames)) { + if (!this.advisorChainInitialized && !ObjectUtils.isEmpty(this.interceptorNames)) { if (this.beanFactory == null) { throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " + "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames)); @@ -464,9 +460,9 @@ public class ProxyFactoryBean extends ProxyCreatorSupport addAdvisorOnChainCreation(advice); } } - } - this.advisorChainInitialized = true; + this.advisorChainInitialized = true; + } } diff --git a/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java index 57df369b68e..91d85d35dad 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -65,10 +65,10 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIOException; /** - * @since 13.03.2003 * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams + * @since 13.03.2003 */ public class ProxyFactoryBeanTests { @@ -633,20 +633,50 @@ public class ProxyFactoryBeanTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(FROZEN_CONTEXT, CLASS)); - Advised advised = (Advised)bf.getBean("frozen"); + Advised advised = (Advised) bf.getBean("frozen"); assertThat(advised.isFrozen()).as("The proxy should be frozen").isTrue(); } @Test - public void testDetectsInterfaces() throws Exception { + public void testDetectsInterfaces() { ProxyFactoryBean fb = new ProxyFactoryBean(); fb.setTarget(new TestBean()); fb.addAdvice(new DebugInterceptor()); fb.setBeanFactory(new DefaultListableBeanFactory()); + ITestBean proxy = (ITestBean) fb.getObject(); assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue(); } + @Test + public void testWithInterceptorNames() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.registerSingleton("debug", new DebugInterceptor()); + + ProxyFactoryBean fb = new ProxyFactoryBean(); + fb.setTarget(new TestBean()); + fb.setInterceptorNames("debug"); + fb.setBeanFactory(bf); + + Advised proxy = (Advised) fb.getObject(); + assertThat(proxy.getAdvisorCount()).isEqualTo(1); + } + + @Test + public void testWithLateInterceptorNames() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.registerSingleton("debug", new DebugInterceptor()); + + ProxyFactoryBean fb = new ProxyFactoryBean(); + fb.setTarget(new TestBean()); + fb.setBeanFactory(bf); + fb.getObject(); + + fb.setInterceptorNames("debug"); + Advised proxy = (Advised) fb.getObject(); + assertThat(proxy.getAdvisorCount()).isEqualTo(1); + } + /** * Fires only on void methods. Saves list of methods intercepted. From d7b9270672a141cd5b046fbdeb8a22f269c73a6f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:13 +0100 Subject: [PATCH 2/8] Clarify SchedulerFactoryBean's LocalDataSourceJobStore overriding Includes clarification of interface-level cache annotations for target-class proxies. Closes gh-27709 See gh-27726 --- .../quartz/LocalDataSourceJobStore.java | 3 ++ .../quartz/SchedulerFactoryBean.java | 4 +- .../config/EnableCachingIntegrationTests.java | 43 +++++++++++++++++++ src/docs/asciidoc/integration.adoc | 32 +++++++------- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java index e0b12f443f9..e4b09c0798f 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java @@ -39,6 +39,7 @@ import org.springframework.lang.Nullable; * Subclass of Quartz's {@link JobStoreCMT} class that delegates to a Spring-managed * {@link DataSource} instead of using a Quartz-managed JDBC connection pool. * This JobStore will be used if SchedulerFactoryBean's "dataSource" property is set. + * You may also configure it explicitly, possibly as a custom subclass of this class. * *

Supports both transactional and non-transactional DataSource access. * With a non-XA DataSource and local Spring transactions, a single DataSource @@ -58,6 +59,8 @@ import org.springframework.lang.Nullable; * @since 1.1 * @see SchedulerFactoryBean#setDataSource * @see SchedulerFactoryBean#setNonTransactionalDataSource + * @see SchedulerFactoryBean#getConfigTimeDataSource() + * @see SchedulerFactoryBean#getConfigTimeNonTransactionalDataSource() * @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection */ diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java index e0982a2e5ff..15185bba6c3 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java @@ -310,9 +310,11 @@ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBe /** * Set the default {@link DataSource} to be used by the Scheduler. - * If set, this will override corresponding settings in Quartz properties. *

Note: If this is set, the Quartz settings should not define * a job store "dataSource" to avoid meaningless double configuration. + * Also, do not define a "org.quartz.jobStore.class" property at all. + * (You may explicitly define Spring's {@link LocalDataSourceJobStore} + * but that's the default when using this method anyway.) *

A Spring-specific subclass of Quartz' JobStoreCMT will be used. * It is therefore strongly recommended to perform all operations on * the Scheduler within Spring-managed (or plain JTA) transactions. diff --git a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java index 6e10f5d04c6..6a7e1127aec 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java @@ -83,6 +83,19 @@ public class EnableCachingIntegrationTests { assertCacheHit(key, value, cache); } + @Test + public void barServiceWithCacheableInterfaceCglib() { + this.context = new AnnotationConfigApplicationContext(BarConfigCglib.class); + BarService service = this.context.getBean(BarService.class); + Cache cache = getCache(); + + Object key = new Object(); + assertCacheMiss(key, cache); + + Object value = service.getSimple(key); + assertCacheHit(key, value, cache); + } + @Test public void beanConditionOff() { this.context = new AnnotationConfigApplicationContext(BeanConditionConfig.class); @@ -185,6 +198,36 @@ public class EnableCachingIntegrationTests { } + @Configuration + @Import(SharedConfig.class) + @EnableCaching(proxyTargetClass = true) + static class BarConfigCglib { + + @Bean + public BarService barService() { + return new BarServiceImpl(); + } + } + + + interface BarService { + + @Cacheable(cacheNames = "testCache") + Object getSimple(Object key); + } + + + static class BarServiceImpl implements BarService { + + private final AtomicLong counter = new AtomicLong(); + + @Override + public Object getSimple(Object key) { + return this.counter.getAndIncrement(); + } + } + + @Configuration @Import(FooConfig.class) @EnableCaching diff --git a/src/docs/asciidoc/integration.adoc b/src/docs/asciidoc/integration.adoc index f2ec53ad0ae..9ea8f646d7d 100644 --- a/src/docs/asciidoc/integration.adoc +++ b/src/docs/asciidoc/integration.adoc @@ -5396,6 +5396,7 @@ You can use these macros instead of the six-digit value, thus: `@Scheduled(cron |=== + [[scheduling-quartz]] === Using the Quartz Scheduler @@ -5451,7 +5452,6 @@ has it applied automatically: protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException { // do the actual work } - } ---- @@ -5571,11 +5571,19 @@ seconds and one running every morning at 6 AM. To finalize everything, we need t ---- -More properties are available for the `SchedulerFactoryBean`, such as the calendars -used by the job details, properties to customize Quartz with, and others. See the -{api-spring-framework}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`] +More properties are available for the `SchedulerFactoryBean`, such as the calendars used by the +job details, properties to customize Quartz with, and a Spring-provided JDBC DataSource. See +the {api-spring-framework}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`] javadoc for more information. +NOTE: `SchedulerFactoryBean` also recognizes a `quartz.properties` file in the classpath, +based on Quartz property keys, as with regular Quartz configuration. Please note that many +`SchedulerFactoryBean` settings interact with common Quartz settings in the properties file; +it is therefore not recommended to specify values at both levels. For example, do not set +an "org.quartz.jobStore.class" property if you mean to rely on a Spring-provided DataSource, +or specify an `org.springframework.scheduling.quartz.LocalDataSourceJobStore` variant which +is a full-fledged replacement for the standard `org.quartz.impl.jdbcjobstore.JobStoreTX`. + @@ -5877,7 +5885,6 @@ is updated in the cache. The following example shows how to use the `sync` attri ---- <1> Using the `sync` attribute. - NOTE: This is an optional feature, and your favorite cache library may not support it. All `CacheManager` implementations provided by the core framework support it. See the documentation of your cache provider for more details. @@ -6035,7 +6042,6 @@ all entries from the `books` cache: ---- <1> Using the `allEntries` attribute to evict all entries from the cache. - This option comes in handy when an entire cache region needs to be cleared out. Rather than evicting each entry (which would take a long time, since it is inefficient), all the entries are removed in one operation, as the preceding example shows. @@ -6094,7 +6100,6 @@ comes into play. The following examples uses `@CacheConfig` to set the name of t ---- <1> Using `@CacheConfig` to set the name of the cache. - `@CacheConfig` is a class-level annotation that allows sharing the cache names, the custom `KeyGenerator`, the custom `CacheManager`, and the custom `CacheResolver`. Placing this annotation on the class does not turn on any caching operation. @@ -6235,13 +6240,11 @@ if you need to annotate non-public methods, as it changes the bytecode itself. **** TIP: Spring recommends that you only annotate concrete classes (and methods of concrete -classes) with the `@Cache{asterisk}` annotation, as opposed to annotating interfaces. -You certainly can place the `@Cache{asterisk}` annotation on an interface (or an interface -method), but this works only as you would expect it to if you use interface-based proxies. -The fact that Java annotations are not inherited from interfaces means that, if you use -class-based proxies (`proxy-target-class="true"`) or the weaving-based aspect -(`mode="aspectj"`), the caching settings are not recognized by the proxying and weaving -infrastructure, and the object is not wrapped in a caching proxy. +classes) with the `@Cache{asterisk}` annotations, as opposed to annotating interfaces. +You certainly can place an `@Cache{asterisk}` annotation on an interface (or an interface +method), but this works only if you use the proxy mode (`mode="proxy"`). If you use the +weaving-based aspect (`mode="aspectj"`), the caching settings are not recognized on +interface-level declarations by the weaving infrastructure. NOTE: In proxy mode (the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the @@ -6378,7 +6381,6 @@ to customize the factory for each cache operation, as the following example show ---- <1> Customizing the factory for this operation. - NOTE: For all referenced classes, Spring tries to locate a bean with the given type. If more than one match exists, a new instance is created and can use the regular bean lifecycle callbacks, such as dependency injection. From 0802581aff7e3ec5b00f7c2f44e1af3d5574461b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:26 +0100 Subject: [PATCH 3/8] Unit test for identifying type variable argument See gh-27748 --- .../core/ResolvableTypeTests.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index ba58532c74f..3ca17918b53 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -1159,6 +1159,20 @@ class ResolvableTypeTests { assertThatResolvableType(complex4).isNotAssignableFrom(complex3); } + @Test + void identifyTypeVariable() throws Exception { + Method method = ClassArguments.class.getMethod("typedArgumentFirst", Class.class, Class.class, Class.class); + ResolvableType returnType = ResolvableType.forMethodReturnType(method, ClassArguments.class); + + ResolvableType arg0 = ResolvableType.forMethodParameter(method, 0, ClassArguments.class); + ResolvableType arg1 = ResolvableType.forMethodParameter(method, 1, ClassArguments.class); + ResolvableType arg2 = ResolvableType.forMethodParameter(method, 2, ClassArguments.class); + + assertThat(returnType.getType().equals(arg0.as(Class.class).getGeneric(0).getType())).isTrue(); + assertThat(returnType.getType().equals(arg1.as(Class.class).getGeneric(0).getType())).isFalse(); + assertThat(returnType.getType().equals(arg2.as(Class.class).getGeneric(0).getType())).isFalse(); + } + @Test void hashCodeAndEquals() throws Exception { ResolvableType forClass = ResolvableType.forClass(List.class); @@ -1427,6 +1441,10 @@ class ResolvableTypeTests { } + interface TypedMethods extends Methods { + } + + static class AssignmentBase { public O o; @@ -1479,7 +1497,9 @@ class ResolvableTypeTests { } - interface TypedMethods extends Methods { + interface ClassArguments { + + T typedArgumentFirst(Class arg0, Class arg1, Class arg2); } From ac581bed92181ce8ddfe629e94629a52e3dc972a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:42 +0100 Subject: [PATCH 4/8] Avoid NPE against null value from toString call Closes gh-27782 --- .../java/org/springframework/core/log/LogFormatUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java b/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java index 9a2693e2ce6..550a202255c 100644 --- a/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java +++ b/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java @@ -22,6 +22,7 @@ import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; /** * Utility methods for formatting and logging messages. @@ -71,10 +72,10 @@ public abstract class LogFormatUtils { } String result; try { - result = value.toString(); + result = ObjectUtils.nullSafeToString(value); } catch (Throwable ex) { - result = ex.toString(); + result = ObjectUtils.nullSafeToString(ex); } if (maxLength != -1) { result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result); From 1af21bb4513e27add1b32a024fd10e9e69253776 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:59 +0100 Subject: [PATCH 5/8] Declare serialVersionUID on DefaultAopProxyFactory Closes gh-27784 --- .../springframework/aop/framework/DefaultAopProxyFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java index 3e8c838704c..8692371d3a8 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java @@ -45,9 +45,10 @@ import org.springframework.core.NativeDetector; * @see AdvisedSupport#setProxyTargetClass * @see AdvisedSupport#setInterfaces */ -@SuppressWarnings("serial") public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { + private static final long serialVersionUID = 7930414337282325166L; + @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { From ca84559588a26c4acb48cd0110ded9a89b43852e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:47:12 +0100 Subject: [PATCH 6/8] Provide findAnnotationOnBean variant with allowFactoryBeanInit flag Closes gh-27796 --- .../beans/factory/ListableBeanFactory.java | 24 ++++++++++++++++++- .../support/DefaultListableBeanFactory.java | 17 +++++++++---- .../support/StaticListableBeanFactory.java | 13 ++++++++-- .../support/AbstractApplicationContext.java | 10 ++++++++ .../setup/StubWebApplicationContext.java | 11 ++++++++- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java index 389f19c9e48..2642e72d7ae 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -353,9 +353,31 @@ public interface ListableBeanFactory extends BeanFactory { * @since 3.0 * @see #getBeanNamesForAnnotation * @see #getBeansWithAnnotation + * @see #getType(String) */ @Nullable A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException; + /** + * Find an {@link Annotation} of {@code annotationType} on the specified bean, + * traversing its interfaces and super classes if no annotation can be found on + * the given class itself, as well as checking the bean's factory method (if any). + * @param beanName the name of the bean to look for annotations on + * @param annotationType the type of annotation to look for + * (at class, interface or factory method level of the specified bean) + * @param allowFactoryBeanInit whether a {@code FactoryBean} may get initialized + * just for the purpose of determining its object type + * @return the annotation of the given type if found, or {@code null} otherwise + * @throws NoSuchBeanDefinitionException if there is no bean with the given name + * @since 5.3.14 + * @see #getBeanNamesForAnnotation + * @see #getBeansWithAnnotation + * @see #getType(String, boolean) + */ + @Nullable + A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException; + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 048612fbed7..b019ef5361f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -730,14 +730,23 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto public A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException { - return findMergedAnnotationOnBean(beanName, annotationType) + return findAnnotationOnBean(beanName, annotationType, true); + } + + @Override + @Nullable + public A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException { + + return findMergedAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit) .synthesize(MergedAnnotation::isPresent).orElse(null); } private MergedAnnotation findMergedAnnotationOnBean( - String beanName, Class annotationType) { + String beanName, Class annotationType, boolean allowFactoryBeanInit) { - Class beanType = getType(beanName); + Class beanType = getType(beanName, allowFactoryBeanInit); if (beanType != null) { MergedAnnotation annotation = MergedAnnotations.from(beanType, SearchStrategy.TYPE_HIERARCHY).get(annotationType); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index a5430120dfd..1105ead3e12 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -459,7 +459,16 @@ public class StaticListableBeanFactory implements ListableBeanFactory { public A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException { - Class beanType = getType(beanName); + return findAnnotationOnBean(beanName, annotationType, true); + } + + @Override + @Nullable + public A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException { + + Class beanType = getType(beanName, allowFactoryBeanInit); return (beanType != null ? AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType) : null); } 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 aca706e82c1..38243b17944 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 @@ -1331,6 +1331,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader return getBeanFactory().findAnnotationOnBean(beanName, annotationType); } + @Override + @Nullable + public A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException { + + assertBeanFactoryActive(); + return getBeanFactory().findAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit); + } + //--------------------------------------------------------------------- // Implementation of HierarchicalBeanFactory interface diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java index c5271e8d18e..4a64b49d994 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -314,6 +314,15 @@ class StubWebApplicationContext implements WebApplicationContext { return this.beanFactory.findAnnotationOnBean(beanName, annotationType); } + @Override + @Nullable + public A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException { + + return this.beanFactory.findAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit); + } + //--------------------------------------------------------------------- // Implementation of HierarchicalBeanFactory interface From 4b7d9b1f9c38c60ac43a752ed1f244abbb1000e2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:48:07 +0100 Subject: [PATCH 7/8] Avoid compilation warning for test class with serializable base class --- .../concurrent/ThreadPoolExecutorFactoryBeanTests.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java index d350341a8c4..8f81b987c8a 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java @@ -76,6 +76,7 @@ class ThreadPoolExecutorFactoryBeanTests { verify(threadPoolExecutor).prestartAllCoreThreads(); } + @Configuration static class ExecutorConfig { @@ -83,9 +84,10 @@ class ThreadPoolExecutorFactoryBeanTests { ThreadPoolExecutorFactoryBean executor() { return new ThreadPoolExecutorFactoryBean(); } - } + + @SuppressWarnings("serial") private static class TestThreadPoolExecutorFactoryBean extends ThreadPoolExecutorFactoryBean { @Override From 0ebb1c5baabd280c1587e3be83c223e951f76133 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:48:40 +0100 Subject: [PATCH 8/8] Upgrade to Tomcat 9.0.56, Undertow 2.2.14, Apache HttpClient 5.1.2 & HttpAsyncClient 4.1.5, Mockito 4.1 --- build.gradle | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index e1cd581786a..992aa6391a2 100644 --- a/build.gradle +++ b/build.gradle @@ -128,41 +128,41 @@ configure(allprojects) { project -> dependency "org.webjars:webjars-locator-core:0.48" dependency "org.webjars:underscorejs:1.8.3" - dependencySet(group: 'org.apache.tomcat', version: '9.0.55') { + dependencySet(group: 'org.apache.tomcat', version: '9.0.56') { entry 'tomcat-util' entry('tomcat-websocket') { - exclude group: "org.apache.tomcat", name: "tomcat-websocket-api" exclude group: "org.apache.tomcat", name: "tomcat-servlet-api" + exclude group: "org.apache.tomcat", name: "tomcat-websocket-api" } } - dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.55') { + dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.56') { entry 'tomcat-embed-core' entry 'tomcat-embed-websocket' } - dependencySet(group: 'io.undertow', version: '2.2.13.Final') { + dependencySet(group: 'io.undertow', version: '2.2.14.Final') { entry 'undertow-core' - entry('undertow-websockets-jsr') { - exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec" - } entry('undertow-servlet') { exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec" exclude group: "org.jboss.spec.javax.annotation", name: "jboss-annotations-api_1.3_spec" } + entry('undertow-websockets-jsr') { + exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec" + } } + dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.9" + dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.2' + dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.2' + dependency("org.apache.httpcomponents:httpclient:4.5.13") { + exclude group: "commons-logging", name: "commons-logging" + } + dependency("org.apache.httpcomponents:httpasyncclient:4.1.5") { + exclude group: "commons-logging", name: "commons-logging" + } dependencySet(group: 'com.squareup.okhttp3', version: '3.14.9') { entry 'okhttp' entry 'mockwebserver' } - dependency("org.apache.httpcomponents:httpclient:4.5.13") { - exclude group: "commons-logging", name: "commons-logging" - } - dependency("org.apache.httpcomponents:httpasyncclient:4.1.4") { - exclude group: "commons-logging", name: "commons-logging" - } - dependency 'org.apache.httpcomponents.client5:httpclient5:5.1' - dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.1' - dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.9" dependency "org.jruby:jruby:9.2.20.1" dependency "org.python:jython-standalone:2.7.1" @@ -198,7 +198,7 @@ configure(allprojects) { project -> exclude group: "org.hamcrest", name: "hamcrest-core" } } - dependencySet(group: 'org.mockito', version: '4.0.0') { + dependencySet(group: 'org.mockito', version: '4.1.0') { entry('mockito-core') { exclude group: "org.hamcrest", name: "hamcrest-core" }