Skip to content

Commit 3686b89

Browse files
committed
Revise proxyTargetClass handling in ResilientMethodsConfiguration and ProxyAsyncConfiguration
An annotation-specified proxyTargetClass attribute must only be applied when true, otherwise we need to participate in global defaulting. Closes gh-35863
1 parent 85ca9f4 commit 3686b89

File tree

8 files changed

+65
-24
lines changed

8 files changed

+65
-24
lines changed

spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@
177177
* be upgraded to subclass proxying at the same time. This approach has no negative
178178
* impact in practice unless one is explicitly expecting one type of proxy vs another,
179179
* for example, in tests.
180+
* <p>It is usually recommendable to rely on a global default proxy configuration
181+
* instead, with specific proxy requirements for certain beans expressed through
182+
* a {@link org.springframework.context.annotation.Proxyable} annotation on
183+
* the affected bean classes.
184+
* @see org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
180185
*/
181186
boolean proxyTargetClass() default false;
182187

spring-context/src/main/java/org/springframework/context/annotation/Proxyable.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,4 @@
5454
*/
5555
Class<?>[] interfaces() default {};
5656

57-
5857
}

spring-context/src/main/java/org/springframework/resilience/annotation/EnableResilientMethods.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@
4848
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
4949
* to standard Java interface-based proxies.
5050
* <p>The default is {@code false}.
51-
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
52-
* Spring-managed beans requiring proxying, not just those marked with {@code @Retryable}
53-
* or {@code @ConcurrencyLimit}. For example, other beans marked with Spring's
54-
* {@code @Transactional} annotation will be upgraded to subclass proxying at
55-
* the same time. This approach has no negative impact in practice unless one is
56-
* explicitly expecting one type of proxy vs. another &mdash; for example, in tests.
51+
* <p>Note that setting this attribute to {@code true} will only affect
52+
* {@link RetryAnnotationBeanPostProcessor} and
53+
* {@link ConcurrencyLimitBeanPostProcessor}.
54+
* <p>It is usually recommendable to rely on a global default proxy configuration
55+
* instead, with specific proxy requirements for certain beans expressed through
56+
* a {@link org.springframework.context.annotation.Proxyable} annotation on
57+
* the affected bean classes.
58+
* @see org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
5759
*/
5860
boolean proxyTargetClass() default false;
5961

spring-context/src/main/java/org/springframework/resilience/annotation/ResilientMethodsConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ public void setImportMetadata(AnnotationMetadata importMetadata) {
5252

5353
private void configureProxySupport(ProxyProcessorSupport proxySupport) {
5454
if (this.enableResilientMethods != null) {
55-
proxySupport.setProxyTargetClass(this.enableResilientMethods.getBoolean("proxyTargetClass"));
55+
if (this.enableResilientMethods.getBoolean("proxyTargetClass")) {
56+
proxySupport.setProxyTargetClass(true);
57+
}
5658
proxySupport.setOrder(this.enableResilientMethods.getNumber("order"));
5759
}
5860
}

spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,13 @@
183183
* to standard Java interface-based proxies.
184184
* <p><strong>Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}</strong>.
185185
* <p>The default is {@code false}.
186-
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
187-
* Spring-managed beans requiring proxying, not just those marked with {@code @Async}.
188-
* For example, other beans marked with Spring's {@code @Transactional} annotation
189-
* will be upgraded to subclass proxying at the same time. This approach has no
190-
* negative impact in practice unless one is explicitly expecting one type of proxy
191-
* vs. another &mdash; for example, in tests.
186+
* <p>Note that setting this attribute to {@code true} will only affect
187+
* {@link AsyncAnnotationBeanPostProcessor}.
188+
* <p>It is usually recommendable to rely on a global default proxy configuration
189+
* instead, with specific proxy requirements for certain beans expressed through
190+
* a {@link org.springframework.context.annotation.Proxyable} annotation on
191+
* the affected bean classes.
192+
* @see org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
192193
*/
193194
boolean proxyTargetClass() default false;
194195

spring-context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
5151
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
5252
bpp.setAsyncAnnotationType(customAsyncAnnotation);
5353
}
54-
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
54+
if (this.enableAsync.getBoolean("proxyTargetClass")) {
55+
bpp.setProxyTargetClass(true);
56+
}
5557
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
5658
return bpp;
5759
}

spring-context/src/test/java/org/springframework/resilience/RetryInterceptorTests.java

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
import org.aopalliance.intercept.MethodInterceptor;
3030
import org.junit.jupiter.api.Test;
3131

32+
import org.springframework.aop.config.AopConfigUtils;
3233
import org.springframework.aop.framework.AopProxyUtils;
33-
import org.springframework.aop.framework.ProxyConfig;
3434
import org.springframework.aop.framework.ProxyFactory;
3535
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
3636
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
@@ -128,11 +128,8 @@ void withPostProcessorForMethodWithInterface() {
128128

129129
@Test
130130
void withPostProcessorForMethodWithInterfaceAndDefaultTargetClass() {
131-
ProxyConfig defaultProxyConfig = new ProxyConfig();
132-
defaultProxyConfig.setProxyTargetClass(true);
133-
134131
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
135-
bf.registerSingleton(AutoProxyUtils.DEFAULT_PROXY_CONFIG_BEAN_NAME, defaultProxyConfig);
132+
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(bf);
136133
bf.registerBeanDefinition("bean", new RootBeanDefinition(AnnotatedMethodBeanWithInterface.class));
137134
RetryAnnotationBeanPostProcessor bpp = new RetryAnnotationBeanPostProcessor();
138135
bpp.setBeanFactory(bf);
@@ -164,11 +161,8 @@ void withPostProcessorForMethodWithInterfaceAndPreserveTargetClass() {
164161

165162
@Test
166163
void withPostProcessorForMethodWithInterfaceAndExposeInterfaces() {
167-
ProxyConfig defaultProxyConfig = new ProxyConfig();
168-
defaultProxyConfig.setProxyTargetClass(true);
169-
170164
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
171-
bf.registerSingleton(AutoProxyUtils.DEFAULT_PROXY_CONFIG_BEAN_NAME, defaultProxyConfig);
165+
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(bf);
172166
RootBeanDefinition bd = new RootBeanDefinition(AnnotatedMethodBeanWithInterface.class);
173167
bd.setAttribute(AutoProxyUtils.EXPOSED_INTERFACES_ATTRIBUTE, AutoProxyUtils.ALL_INTERFACES_ATTRIBUTE_VALUE);
174168
bf.registerBeanDefinition("bean", bd);
@@ -284,6 +278,37 @@ void withEnableAnnotation() throws Exception {
284278
assertThat(target.threadChange).hasValue(2);
285279
}
286280

281+
@Test
282+
void withEnableAnnotationAndDefaultTargetClass() {
283+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
284+
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(ctx);
285+
ctx.registerBeanDefinition("bean", new RootBeanDefinition(AnnotatedMethodBeanWithInterface.class));
286+
ctx.registerBeanDefinition("config", new RootBeanDefinition(EnablingConfig.class));
287+
ctx.refresh();
288+
AnnotatedInterface proxy = ctx.getBean(AnnotatedInterface.class);
289+
AnnotatedMethodBeanWithInterface target = (AnnotatedMethodBeanWithInterface) AopProxyUtils.getSingletonTarget(proxy);
290+
291+
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
292+
assertThatIOException().isThrownBy(proxy::retryOperation).withMessage("6");
293+
assertThat(target.counter).isEqualTo(6);
294+
}
295+
296+
@Test
297+
void withEnableAnnotationAndPreserveTargetClass() {
298+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
299+
RootBeanDefinition bd = new RootBeanDefinition(AnnotatedMethodBeanWithInterface.class);
300+
bd.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
301+
ctx.registerBeanDefinition("bean", bd);
302+
ctx.registerBeanDefinition("config", new RootBeanDefinition(EnablingConfig.class));
303+
ctx.refresh();
304+
AnnotatedInterface proxy = ctx.getBean(AnnotatedInterface.class);
305+
AnnotatedMethodBeanWithInterface target = (AnnotatedMethodBeanWithInterface) AopProxyUtils.getSingletonTarget(proxy);
306+
307+
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
308+
assertThatIOException().isThrownBy(proxy::retryOperation).withMessage("6");
309+
assertThat(target.counter).isEqualTo(6);
310+
}
311+
287312
@Test
288313
void withAsyncAnnotation() {
289314
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@
173173
* {@code @Async} annotation will be upgraded to subclass proxying at the same
174174
* time. This approach has no negative impact in practice unless one is explicitly
175175
* expecting one type of proxy vs another, for example, in tests.
176+
* <p>It is usually recommendable to rely on a global default proxy configuration
177+
* instead, with specific proxy requirements for certain beans expressed through
178+
* a {@link org.springframework.context.annotation.Proxyable} annotation on
179+
* the affected bean classes.
180+
* @see org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
176181
*/
177182
boolean proxyTargetClass() default false;
178183

0 commit comments

Comments
 (0)