Skip to content

Commit 552a5cd

Browse files
committed
Create CGLIB proxy in case of no target interfaces (just introductions)
Closes gh-31304
1 parent 8941e28 commit 552a5cd

File tree

2 files changed

+44
-34
lines changed

2 files changed

+44
-34
lines changed

spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
5959

6060
@Override
6161
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
62-
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
62+
if (config.isOptimize() || config.isProxyTargetClass() || !hasTargetInterfaces(config)) {
6363
Class<?> targetClass = config.getTargetClass();
6464
if (targetClass == null) {
6565
throw new AopConfigException("TargetSource cannot determine target class: " +
@@ -75,14 +75,14 @@ public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException
7575
}
7676
}
7777

78-
/**
79-
* Determine whether the supplied {@link AdvisedSupport} has only the
80-
* {@link org.springframework.aop.SpringProxy} interface specified
81-
* (or no proxy interfaces specified at all).
82-
*/
83-
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
84-
Class<?>[] ifcs = config.getProxiedInterfaces();
85-
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
78+
private boolean hasTargetInterfaces(AdvisedSupport config) {
79+
Class<?> targetClass = config.getTargetClass();
80+
for (Class<?> ifc : config.getProxiedInterfaces()) {
81+
if (targetClass != null ? ifc.isAssignableFrom(targetClass) : !SpringProxy.class.isAssignableFrom(ifc)) {
82+
return true;
83+
}
84+
}
85+
return false;
8686
}
8787

8888
}

spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java

+34-24
Original file line numberDiff line numberDiff line change
@@ -189,25 +189,22 @@ public int compareTo(Object arg0) {
189189
}
190190
}
191191
TestBeanSubclass raw = new TestBeanSubclass();
192-
ProxyFactory factory = new ProxyFactory(raw);
193-
//System.out.println("Proxied interfaces are " + StringUtils.arrayToDelimitedString(factory.getProxiedInterfaces(), ","));
194-
assertThat(factory.getProxiedInterfaces()).as("Found correct number of interfaces").hasSize(5);
195-
ITestBean tb = (ITestBean) factory.getProxy();
192+
ProxyFactory pf = new ProxyFactory(raw);
193+
assertThat(pf.getProxiedInterfaces()).as("Found correct number of interfaces").hasSize(5);
194+
ITestBean tb = (ITestBean) pf.getProxy();
196195
assertThat(tb).as("Picked up secondary interface").isInstanceOf(IOther.class);
197196
raw.setAge(25);
198197
assertThat(tb.getAge()).isEqualTo(raw.getAge());
199198

199+
Class<?>[] oldProxiedInterfaces = pf.getProxiedInterfaces();
200200
long t = 555555L;
201201
TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(t);
202+
pf.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class));
202203

203-
Class<?>[] oldProxiedInterfaces = factory.getProxiedInterfaces();
204-
205-
factory.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class));
206-
207-
Class<?>[] newProxiedInterfaces = factory.getProxiedInterfaces();
204+
Class<?>[] newProxiedInterfaces = pf.getProxiedInterfaces();
208205
assertThat(newProxiedInterfaces).as("Advisor proxies one more interface after introduction").hasSize(oldProxiedInterfaces.length + 1);
209206

210-
TimeStamped ts = (TimeStamped) factory.getProxy();
207+
TimeStamped ts = (TimeStamped) pf.getProxy();
211208
assertThat(ts.getTimeStamp()).isEqualTo(t);
212209
// Shouldn't fail;
213210
((IOther) ts).absquatulate();
@@ -224,26 +221,26 @@ public Object invoke(MethodInvocation invocation) {
224221

225222
NopInterceptor di = new NopInterceptor();
226223
NopInterceptor diUnused = new NopInterceptor();
227-
ProxyFactory factory = new ProxyFactory(new TestBean());
228-
factory.addAdvice(0, di);
229-
assertThat(factory.getProxy()).isInstanceOf(ITestBean.class);
230-
assertThat(factory.adviceIncluded(di)).isTrue();
231-
assertThat(factory.adviceIncluded(diUnused)).isFalse();
232-
assertThat(factory.countAdvicesOfType(NopInterceptor.class)).isEqualTo(1);
233-
assertThat(factory.countAdvicesOfType(MyInterceptor.class)).isEqualTo(0);
234-
235-
factory.addAdvice(0, diUnused);
236-
assertThat(factory.adviceIncluded(diUnused)).isTrue();
237-
assertThat(factory.countAdvicesOfType(NopInterceptor.class)).isEqualTo(2);
224+
ProxyFactory pf = new ProxyFactory(new TestBean());
225+
pf.addAdvice(0, di);
226+
assertThat(pf.getProxy()).isInstanceOf(ITestBean.class);
227+
assertThat(pf.adviceIncluded(di)).isTrue();
228+
assertThat(pf.adviceIncluded(diUnused)).isFalse();
229+
assertThat(pf.countAdvicesOfType(NopInterceptor.class)).isEqualTo(1);
230+
assertThat(pf.countAdvicesOfType(MyInterceptor.class)).isEqualTo(0);
231+
232+
pf.addAdvice(0, diUnused);
233+
assertThat(pf.adviceIncluded(diUnused)).isTrue();
234+
assertThat(pf.countAdvicesOfType(NopInterceptor.class)).isEqualTo(2);
238235
}
239236

240237
@Test
241238
void sealedInterfaceExclusion() {
242239
// String implements ConstantDesc on JDK 12+, sealed as of JDK 17
243-
ProxyFactory factory = new ProxyFactory("");
240+
ProxyFactory pf = new ProxyFactory("");
244241
NopInterceptor di = new NopInterceptor();
245-
factory.addAdvice(0, di);
246-
Object proxy = factory.getProxy();
242+
pf.addAdvice(0, di);
243+
Object proxy = pf.getProxy();
247244
assertThat(proxy).isInstanceOf(CharSequence.class);
248245
}
249246

@@ -330,6 +327,19 @@ void proxyTargetClassWithConcreteClassAsTarget() {
330327
assertThat(AopProxyUtils.ultimateTargetClass(proxy2)).isEqualTo(TestBean.class);
331328
}
332329

330+
@Test
331+
void proxyTargetClassWithIntroducedInterface() {
332+
ProxyFactory pf = new ProxyFactory();
333+
pf.setTargetClass(MyDate.class);
334+
TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(0L);
335+
pf.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class));
336+
Object proxy = pf.getProxy();
337+
assertThat(AopUtils.isCglibProxy(proxy)).as("Proxy is a CGLIB proxy").isTrue();
338+
assertThat(proxy).isInstanceOf(MyDate.class);
339+
assertThat(proxy).isInstanceOf(TimeStamped.class);
340+
assertThat(AopProxyUtils.ultimateTargetClass(proxy)).isEqualTo(MyDate.class);
341+
}
342+
333343
@Test
334344
void interfaceProxiesCanBeOrderedThroughAnnotations() {
335345
Object proxy1 = new ProxyFactory(new A()).getProxy();

0 commit comments

Comments
 (0)