From c9edfef95af304419fd20457c00febcc1a4931d5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 9 Jul 2021 13:23:04 +0200 Subject: [PATCH] Make proxyTargetClass=true with introduction advice work for JDK proxy targets Closes gh-27044 --- .../autoproxy/AbstractAutoProxyCreator.java | 13 +++++++++- .../autoproxy/AutoProxyCreatorTests.java | 26 +++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java index 746fd1d3d4a9..3e68f820ecb9 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java @@ -17,6 +17,7 @@ package org.springframework.aop.framework.autoproxy; import java.lang.reflect.Constructor; +import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -440,7 +441,17 @@ protected Object createProxy(Class beanClass, @Nullable String beanName, ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); - if (!proxyFactory.isProxyTargetClass()) { + if (proxyFactory.isProxyTargetClass()) { + // Explicit handling of JDK proxy targets (for introduction advice scenarios) + if (Proxy.isProxyClass(beanClass)) { + // Must allow for introductions; can't just set interfaces to the proxy's interfaces only. + for (Class ifc : beanClass.getInterfaces()) { + proxyFactory.addInterface(ifc); + } + } + } + else { + // No proxyTargetClass flag enforced, let's apply our default checks... if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } diff --git a/spring-context/src/test/java/org/springframework/aop/framework/autoproxy/AutoProxyCreatorTests.java b/spring-context/src/test/java/org/springframework/aop/framework/autoproxy/AutoProxyCreatorTests.java index b0fde5f2be9c..eb39f93218cf 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/autoproxy/AutoProxyCreatorTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/autoproxy/AutoProxyCreatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 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. @@ -28,6 +28,7 @@ import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.AopUtils; +import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.aop.target.SingletonTargetSource; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.BeanFactory; @@ -219,7 +220,7 @@ public void testAutoProxyCreatorWithFallbackToDynamicProxy() { MutablePropertyValues pvs = new MutablePropertyValues(); pvs.add("proxyFactoryBean", "false"); - sac.registerSingleton("testAutoProxyCreator", TestAutoProxyCreator.class, pvs); + sac.registerSingleton("testAutoProxyCreator", IntroductionTestAutoProxyCreator.class, pvs); sac.registerSingleton("noInterfaces", NoInterfaces.class); sac.registerSingleton("containerCallbackInterfacesOnly", ContainerCallbackInterfacesOnly.class); @@ -248,9 +249,9 @@ public void testAutoProxyCreatorWithFallbackToDynamicProxy() { singletonNoInterceptor.getName(); assertThat(tapc.testInterceptor.nrOfInvocations).isEqualTo(0); singletonToBeProxied.getAge(); - assertThat(tapc.testInterceptor.nrOfInvocations).isEqualTo(1); - prototypeToBeProxied.getSpouse(); assertThat(tapc.testInterceptor.nrOfInvocations).isEqualTo(2); + prototypeToBeProxied.getSpouse(); + assertThat(tapc.testInterceptor.nrOfInvocations).isEqualTo(4); } @Test @@ -404,7 +405,7 @@ protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String name, else if (name.endsWith("ToBeProxied")) { boolean isFactoryBean = FactoryBean.class.isAssignableFrom(beanClass); if ((this.proxyFactoryBean && isFactoryBean) || (this.proxyObject && !isFactoryBean)) { - return new Object[] {this.testInterceptor}; + return getAdvicesAndAdvisors(); } else { return DO_NOT_PROXY; @@ -414,6 +415,10 @@ else if (name.endsWith("ToBeProxied")) { return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; } } + + protected Object[] getAdvicesAndAdvisors() { + return new Object[] {this.testInterceptor}; + } } @@ -426,6 +431,17 @@ public FallbackTestAutoProxyCreator() { } + @SuppressWarnings("serial") + public static class IntroductionTestAutoProxyCreator extends TestAutoProxyCreator { + + protected Object[] getAdvicesAndAdvisors() { + DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(this.testInterceptor); + advisor.addInterface(Serializable.class); + return new Object[] {this.testInterceptor, advisor}; + } + } + + /** * Interceptor that counts the number of non-finalize method calls. */