Skip to content

Commit 7bfcb4c

Browse files
committed
Reject sealed interfaces in AopProxyUtils.completeJdkProxyInterfaces()
See spring-projectsgh-28745
1 parent 9d42779 commit 7bfcb4c

File tree

2 files changed

+35
-28
lines changed

2 files changed

+35
-28
lines changed

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

+19-19
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
import org.springframework.aop.TargetSource;
3030
import org.springframework.aop.support.AopUtils;
3131
import org.springframework.aop.target.SingletonTargetSource;
32-
import org.springframework.aot.hint.ProxyHints;
33-
import org.springframework.aot.hint.RuntimeHints;
3432
import org.springframework.core.DecoratingProxy;
3533
import org.springframework.lang.Nullable;
3634
import org.springframework.util.Assert;
@@ -101,34 +99,35 @@ public static Class<?> ultimateTargetClass(Object candidate) {
10199
* proxy generated by Spring AOP.
102100
* <p>Specifically, {@link SpringProxy}, {@link Advised}, and {@link DecoratingProxy}
103101
* will be appended to the set of user-specified interfaces.
104-
* <p>Any {@linkplain Class#isSealed() sealed} interface in the set of
105-
* user-specified interfaces will be omitted from the results, since only
106-
* non-sealed interfaces are eligible for JDK dynamic proxies.
107-
* <p>This method can be useful when registering {@linkplain ProxyHints proxy
108-
* hints} for Spring's AOT support, as demonstrated in the following example
109-
* which uses this method via a {@code static} import.
102+
* <p>This method can be useful when registering
103+
* {@linkplain org.springframework.aot.hint.ProxyHints proxy hints} for Spring's
104+
* AOT support, as demonstrated in the following example which uses this method
105+
* via a {@code static} import.
110106
* <pre class="code">
111107
* RuntimeHints hints = ...
112108
* hints.proxies().registerJdkProxy(completeJdkProxyInterfaces(MyInterface.class));
113109
* </pre>
114110
* @param userInterfaces the set of user-specified interfaces implemented by
115111
* the component to be proxied
116112
* @return the complete set of interfaces that the proxy should implement
113+
* @throws IllegalArgumentException if a supplied {@code Class} is {@code null},
114+
* is not an {@linkplain Class#isInterface() interface}, or is a
115+
* {@linkplain Class#isSealed() sealed} interface
117116
* @since 6.0
118117
* @see SpringProxy
119118
* @see Advised
120119
* @see DecoratingProxy
121-
* @see RuntimeHints#proxies()
122-
* @see ProxyHints#registerJdkProxy(Class...)
120+
* @see org.springframework.aot.hint.RuntimeHints#proxies()
121+
* @see org.springframework.aot.hint.ProxyHints#registerJdkProxy(Class...)
123122
* @see #completeJdkProxyInterfaces(String...)
124123
*/
125124
public static Class<?>[] completeJdkProxyInterfaces(Class<?>... userInterfaces) {
126125
List<Class<?>> completedInterfaces = new ArrayList<>(userInterfaces.length + 3);
127126
for (Class<?> ifc : userInterfaces) {
128-
Assert.isTrue(ifc.isInterface(), () -> ifc.getName() + " must be an interface");
129-
if (!ifc.isSealed()) {
130-
completedInterfaces.add(ifc);
131-
}
127+
Assert.notNull(ifc, "'userInterfaces' must not contain null values");
128+
Assert.isTrue(ifc.isInterface() && !ifc.isSealed(),
129+
() -> ifc.getName() + " must be a non-sealed interface");
130+
completedInterfaces.add(ifc);
132131
}
133132
completedInterfaces.add(SpringProxy.class);
134133
completedInterfaces.add(Advised.class);
@@ -141,9 +140,10 @@ public static Class<?>[] completeJdkProxyInterfaces(Class<?>... userInterfaces)
141140
* proxy generated by Spring AOP.
142141
* <p>Specifically, {@link SpringProxy}, {@link Advised}, and {@link DecoratingProxy}
143142
* will be appended to the set of user-specified interfaces.
144-
* <p>This method can be useful when registering {@linkplain ProxyHints proxy
145-
* hints} for Spring's AOT support, as demonstrated in the following example
146-
* which uses this method via a {@code static} import.
143+
* <p>This method can be useful when registering
144+
* {@linkplain org.springframework.aot.hint.ProxyHints proxy hints} for Spring's
145+
* AOT support, as demonstrated in the following example which uses this method
146+
* via a {@code static} import.
147147
* <pre class="code">
148148
* RuntimeHints hints = ...
149149
* hints.proxies().registerJdkProxy(completeJdkProxyInterfaces("com.example.MyInterface"));
@@ -156,8 +156,8 @@ public static Class<?>[] completeJdkProxyInterfaces(Class<?>... userInterfaces)
156156
* @see SpringProxy
157157
* @see Advised
158158
* @see DecoratingProxy
159-
* @see RuntimeHints#proxies()
160-
* @see ProxyHints#registerJdkProxy(Class...)
159+
* @see org.springframework.aot.hint.RuntimeHints#proxies()
160+
* @see org.springframework.aot.hint.ProxyHints#registerJdkProxy(Class...)
161161
* @see #completeJdkProxyInterfaces(Class...)
162162
*/
163163
public static String[] completeJdkProxyInterfaces(String... userInterfaces) {

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

+16-9
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,25 @@ void proxiedUserInterfacesWithNoInterface() {
109109
assertThatIllegalArgumentException().isThrownBy(() -> AopProxyUtils.proxiedUserInterfaces(proxy));
110110
}
111111

112+
@Test
113+
void completeJdkProxyInterfacesFromNullInterface() {
114+
assertThatIllegalArgumentException()
115+
.isThrownBy(() -> AopProxyUtils.completeJdkProxyInterfaces(ITestBean.class, null, Comparable.class))
116+
.withMessage("'userInterfaces' must not contain null values");
117+
}
118+
112119
@Test
113120
void completeJdkProxyInterfacesFromClassThatIsNotAnInterface() {
114121
assertThatIllegalArgumentException()
115122
.isThrownBy(() -> AopProxyUtils.completeJdkProxyInterfaces(TestBean.class))
116-
.withMessage(TestBean.class.getName() + " must be an interface");
123+
.withMessage(TestBean.class.getName() + " must be a non-sealed interface");
124+
}
125+
126+
@Test
127+
void completeJdkProxyInterfacesFromSealedInterface() {
128+
assertThatIllegalArgumentException()
129+
.isThrownBy(() -> AopProxyUtils.completeJdkProxyInterfaces(SealedInterface.class))
130+
.withMessage(SealedInterface.class.getName() + " must be a non-sealed interface");
117131
}
118132

119133
@Test
@@ -130,13 +144,6 @@ void completeJdkProxyInterfacesFromMultipleClasses() {
130144
ITestBean.class, Comparable.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
131145
}
132146

133-
@Test
134-
void completeJdkProxyInterfacesIgnoresSealedInterfaces() {
135-
Class<?>[] jdkProxyInterfaces = AopProxyUtils.completeJdkProxyInterfaces(SealedInterface.class, Comparable.class);
136-
assertThat(jdkProxyInterfaces).containsExactly(
137-
Comparable.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
138-
}
139-
140147
@Test
141148
void completeJdkProxyInterfacesFromSingleClassName() {
142149
String[] jdkProxyInterfaces = AopProxyUtils.completeJdkProxyInterfaces(ITestBean.class.getName());
@@ -158,7 +165,7 @@ void completeJdkProxyInterfacesFromMultipleClassNames() {
158165
sealed interface SealedInterface {
159166
}
160167

161-
static final class SealedType implements SealedInterface {
168+
static final class SealedClass implements SealedInterface {
162169
}
163170

164171
}

0 commit comments

Comments
 (0)