Check for advisor-introduced interfaces specifically

See gh-31304
This commit is contained in:
Juergen Hoeller 2024-09-27 11:07:11 +02:00
parent d3dd01e227
commit 8680c43368
3 changed files with 53 additions and 27 deletions

View File

@ -34,6 +34,7 @@ import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.IntroductionInfo; import org.springframework.aop.IntroductionInfo;
import org.springframework.aop.Pointcut; import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor; import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetSource; import org.springframework.aop.TargetSource;
import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DefaultPointcutAdvisor;
@ -222,15 +223,15 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
/** /**
* Add a new proxied interface. * Add a new proxied interface.
* @param intf the additional interface to proxy * @param ifc the additional interface to proxy
*/ */
public void addInterface(Class<?> intf) { public void addInterface(Class<?> ifc) {
Assert.notNull(intf, "Interface must not be null"); Assert.notNull(ifc, "Interface must not be null");
if (!intf.isInterface()) { if (!ifc.isInterface()) {
throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface"); throw new IllegalArgumentException("[" + ifc.getName() + "] is not an interface");
} }
if (!this.interfaces.contains(intf)) { if (!this.interfaces.contains(ifc)) {
this.interfaces.add(intf); this.interfaces.add(ifc);
adviceChanged(); adviceChanged();
} }
} }
@ -238,12 +239,12 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
/** /**
* Remove a proxied interface. * Remove a proxied interface.
* <p>Does nothing if the given interface isn't proxied. * <p>Does nothing if the given interface isn't proxied.
* @param intf the interface to remove from the proxy * @param ifc the interface to remove from the proxy
* @return {@code true} if the interface was removed; {@code false} * @return {@code true} if the interface was removed; {@code false}
* if the interface was not found and hence could not be removed * if the interface was not found and hence could not be removed
*/ */
public boolean removeInterface(Class<?> intf) { public boolean removeInterface(Class<?> ifc) {
return this.interfaces.remove(intf); return this.interfaces.remove(ifc);
} }
@Override @Override
@ -252,15 +253,37 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
} }
@Override @Override
public boolean isInterfaceProxied(Class<?> intf) { public boolean isInterfaceProxied(Class<?> ifc) {
for (Class<?> proxyIntf : this.interfaces) { for (Class<?> proxyIntf : this.interfaces) {
if (intf.isAssignableFrom(proxyIntf)) { if (ifc.isAssignableFrom(proxyIntf)) {
return true; return true;
} }
} }
return false; return false;
} }
boolean hasUserSuppliedInterfaces() {
for (Class<?> ifc : this.interfaces) {
if (!SpringProxy.class.isAssignableFrom(ifc) && !isAdvisorIntroducedInterface(ifc)) {
return true;
}
}
return false;
}
private boolean isAdvisorIntroducedInterface(Class<?> ifc) {
for (Advisor advisor : this.advisors) {
if (advisor instanceof IntroductionAdvisor introductionAdvisor) {
for (Class<?> introducedInterface : introductionAdvisor.getInterfaces()) {
if (introducedInterface == ifc) {
return true;
}
}
}
}
return false;
}
@Override @Override
public final Advisor[] getAdvisors() { public final Advisor[] getAdvisors() {

View File

@ -19,7 +19,6 @@ package org.springframework.aop.framework;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
/** /**
@ -59,7 +58,7 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override @Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || !hasTargetInterfaces(config)) { if (config.isOptimize() || config.isProxyTargetClass() || !config.hasUserSuppliedInterfaces()) {
Class<?> targetClass = config.getTargetClass(); Class<?> targetClass = config.getTargetClass();
if (targetClass == null) { if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " + throw new AopConfigException("TargetSource cannot determine target class: " +
@ -75,14 +74,4 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
} }
} }
private boolean hasTargetInterfaces(AdvisedSupport config) {
Class<?> targetClass = config.getTargetClass();
for (Class<?> ifc : config.getProxiedInterfaces()) {
if (targetClass != null ? ifc.isAssignableFrom(targetClass) : !SpringProxy.class.isAssignableFrom(ifc)) {
return true;
}
}
return false;
}
} }

View File

@ -199,7 +199,7 @@ class ProxyFactoryTests {
Class<?>[] oldProxiedInterfaces = pf.getProxiedInterfaces(); Class<?>[] oldProxiedInterfaces = pf.getProxiedInterfaces();
long t = 555555L; long t = 555555L;
TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(t); TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(t);
pf.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class)); pf.addAdvisor(new DefaultIntroductionAdvisor(ti, TimeStamped.class));
Class<?>[] newProxiedInterfaces = pf.getProxiedInterfaces(); Class<?>[] newProxiedInterfaces = pf.getProxiedInterfaces();
assertThat(newProxiedInterfaces).as("Advisor proxies one more interface after introduction").hasSize(oldProxiedInterfaces.length + 1); assertThat(newProxiedInterfaces).as("Advisor proxies one more interface after introduction").hasSize(oldProxiedInterfaces.length + 1);
@ -328,11 +328,11 @@ class ProxyFactoryTests {
} }
@Test @Test
void proxyTargetClassWithIntroducedInterface() { void proxyTargetClassInCaseOfIntroducedInterface() {
ProxyFactory pf = new ProxyFactory(); ProxyFactory pf = new ProxyFactory();
pf.setTargetClass(MyDate.class); pf.setTargetClass(MyDate.class);
TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(0L); TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(0L);
pf.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class)); pf.addAdvisor(new DefaultIntroductionAdvisor(ti, TimeStamped.class));
Object proxy = pf.getProxy(); Object proxy = pf.getProxy();
assertThat(AopUtils.isCglibProxy(proxy)).as("Proxy is a CGLIB proxy").isTrue(); assertThat(AopUtils.isCglibProxy(proxy)).as("Proxy is a CGLIB proxy").isTrue();
assertThat(proxy).isInstanceOf(MyDate.class); assertThat(proxy).isInstanceOf(MyDate.class);
@ -340,6 +340,20 @@ class ProxyFactoryTests {
assertThat(AopProxyUtils.ultimateTargetClass(proxy)).isEqualTo(MyDate.class); assertThat(AopProxyUtils.ultimateTargetClass(proxy)).isEqualTo(MyDate.class);
} }
@Test
void proxyInterfaceInCaseOfNonTargetInterface() {
ProxyFactory pf = new ProxyFactory();
pf.setTargetClass(MyDate.class);
pf.addInterface(TimeStamped.class);
pf.addAdvice((MethodInterceptor) invocation -> {
throw new UnsupportedOperationException();
});
Object proxy = pf.getProxy();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).as("Proxy is a JDK proxy").isTrue();
assertThat(proxy).isInstanceOf(TimeStamped.class);
assertThat(AopProxyUtils.ultimateTargetClass(proxy)).isEqualTo(MyDate.class);
}
@Test @Test
void interfaceProxiesCanBeOrderedThroughAnnotations() { void interfaceProxiesCanBeOrderedThroughAnnotations() {
Object proxy1 = new ProxyFactory(new A()).getProxy(); Object proxy1 = new ProxyFactory(new A()).getProxy();