Browse Source

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
pull/35899/head
Juergen Hoeller 3 weeks ago
parent
commit
3686b89ab5
  1. 5
      spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
  2. 1
      spring-context/src/main/java/org/springframework/context/annotation/Proxyable.java
  3. 14
      spring-context/src/main/java/org/springframework/resilience/annotation/EnableResilientMethods.java
  4. 4
      spring-context/src/main/java/org/springframework/resilience/annotation/ResilientMethodsConfiguration.java
  5. 13
      spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
  6. 4
      spring-context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java
  7. 43
      spring-context/src/test/java/org/springframework/resilience/RetryInterceptorTests.java
  8. 5
      spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java

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

@ -177,6 +177,11 @@ public @interface EnableCaching { @@ -177,6 +177,11 @@ public @interface EnableCaching {
* be upgraded to subclass proxying at the same time. This approach has no negative
* impact in practice unless one is explicitly expecting one type of proxy vs another,
* for example, in tests.
* <p>It is usually recommendable to rely on a global default proxy configuration
* instead, with specific proxy requirements for certain beans expressed through
* a {@link org.springframework.context.annotation.Proxyable} annotation on
* the affected bean classes.
* @see org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
*/
boolean proxyTargetClass() default false;

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

@ -54,5 +54,4 @@ public @interface Proxyable { @@ -54,5 +54,4 @@ public @interface Proxyable {
*/
Class<?>[] interfaces() default {};
}

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

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

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

@ -52,7 +52,9 @@ public class ResilientMethodsConfiguration implements ImportAware { @@ -52,7 +52,9 @@ public class ResilientMethodsConfiguration implements ImportAware {
private void configureProxySupport(ProxyProcessorSupport proxySupport) {
if (this.enableResilientMethods != null) {
proxySupport.setProxyTargetClass(this.enableResilientMethods.getBoolean("proxyTargetClass"));
if (this.enableResilientMethods.getBoolean("proxyTargetClass")) {
proxySupport.setProxyTargetClass(true);
}
proxySupport.setOrder(this.enableResilientMethods.getNumber("order"));
}
}

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

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

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

@ -51,7 +51,9 @@ public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration { @@ -51,7 +51,9 @@ public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
if (this.enableAsync.getBoolean("proxyTargetClass")) {
bpp.setProxyTargetClass(true);
}
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}

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

@ -29,8 +29,8 @@ import java.util.concurrent.atomic.AtomicInteger; @@ -29,8 +29,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.aopalliance.intercept.MethodInterceptor;
import org.junit.jupiter.api.Test;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.framework.ProxyConfig;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
@ -128,11 +128,8 @@ class RetryInterceptorTests { @@ -128,11 +128,8 @@ class RetryInterceptorTests {
@Test
void withPostProcessorForMethodWithInterfaceAndDefaultTargetClass() {
ProxyConfig defaultProxyConfig = new ProxyConfig();
defaultProxyConfig.setProxyTargetClass(true);
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton(AutoProxyUtils.DEFAULT_PROXY_CONFIG_BEAN_NAME, defaultProxyConfig);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(bf);
bf.registerBeanDefinition("bean", new RootBeanDefinition(AnnotatedMethodBeanWithInterface.class));
RetryAnnotationBeanPostProcessor bpp = new RetryAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
@ -164,11 +161,8 @@ class RetryInterceptorTests { @@ -164,11 +161,8 @@ class RetryInterceptorTests {
@Test
void withPostProcessorForMethodWithInterfaceAndExposeInterfaces() {
ProxyConfig defaultProxyConfig = new ProxyConfig();
defaultProxyConfig.setProxyTargetClass(true);
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton(AutoProxyUtils.DEFAULT_PROXY_CONFIG_BEAN_NAME, defaultProxyConfig);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(bf);
RootBeanDefinition bd = new RootBeanDefinition(AnnotatedMethodBeanWithInterface.class);
bd.setAttribute(AutoProxyUtils.EXPOSED_INTERFACES_ATTRIBUTE, AutoProxyUtils.ALL_INTERFACES_ATTRIBUTE_VALUE);
bf.registerBeanDefinition("bean", bd);
@ -284,6 +278,37 @@ class RetryInterceptorTests { @@ -284,6 +278,37 @@ class RetryInterceptorTests {
assertThat(target.threadChange).hasValue(2);
}
@Test
void withEnableAnnotationAndDefaultTargetClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(ctx);
ctx.registerBeanDefinition("bean", new RootBeanDefinition(AnnotatedMethodBeanWithInterface.class));
ctx.registerBeanDefinition("config", new RootBeanDefinition(EnablingConfig.class));
ctx.refresh();
AnnotatedInterface proxy = ctx.getBean(AnnotatedInterface.class);
AnnotatedMethodBeanWithInterface target = (AnnotatedMethodBeanWithInterface) AopProxyUtils.getSingletonTarget(proxy);
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
assertThatIOException().isThrownBy(proxy::retryOperation).withMessage("6");
assertThat(target.counter).isEqualTo(6);
}
@Test
void withEnableAnnotationAndPreserveTargetClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
RootBeanDefinition bd = new RootBeanDefinition(AnnotatedMethodBeanWithInterface.class);
bd.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
ctx.registerBeanDefinition("bean", bd);
ctx.registerBeanDefinition("config", new RootBeanDefinition(EnablingConfig.class));
ctx.refresh();
AnnotatedInterface proxy = ctx.getBean(AnnotatedInterface.class);
AnnotatedMethodBeanWithInterface target = (AnnotatedMethodBeanWithInterface) AopProxyUtils.getSingletonTarget(proxy);
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
assertThatIOException().isThrownBy(proxy::retryOperation).withMessage("6");
assertThat(target.counter).isEqualTo(6);
}
@Test
void withAsyncAnnotation() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

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

@ -173,6 +173,11 @@ public @interface EnableTransactionManagement { @@ -173,6 +173,11 @@ public @interface EnableTransactionManagement {
* {@code @Async} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, for example, in tests.
* <p>It is usually recommendable to rely on a global default proxy configuration
* instead, with specific proxy requirements for certain beans expressed through
* a {@link org.springframework.context.annotation.Proxyable} annotation on
* the affected bean classes.
* @see org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
*/
boolean proxyTargetClass() default false;

Loading…
Cancel
Save