Simplify hint registration for Spring AOP proxies

Prior to this commit, when users wished to register proxy hints for a
Spring AOP JDK dynamic proxy, they were required to explicitly specify
SpringProxy, Advised, and DecoratingProxy along with user interfaces.

This commit simplifies hint registration for Spring AOP proxies by
introducing two completeJdkProxyInterfaces() methods in AopProxyUtils,
one that accepts strings and one that accepts classes that represent
the user-specified interfaces implemented the user component to be
proxied. The SpringProxy, Advised, and DecoratingProxy interfaces are
appended to the user-specified interfaces and returned as the complete
set of interfaces that the proxy will implement.

Closes gh-28745
This commit is contained in:
Sam Brannen
2022-07-10 20:09:49 +02:00
parent 2a0b3c1af9
commit 5178e9c28e
5 changed files with 141 additions and 13 deletions

View File

@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.aop.SpringProxy;
@@ -28,6 +29,8 @@ import org.springframework.aop.TargetClassAware;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.aot.hint.ProxyHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.core.DecoratingProxy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -93,6 +96,79 @@ public abstract class AopProxyUtils {
return result;
}
/**
* Complete the set of interfaces that are typically required in a JDK dynamic
* proxy generated by Spring AOP.
* <p>Specifically, {@link SpringProxy}, {@link Advised}, and {@link DecoratingProxy}
* will be appended to the set of user-specified interfaces.
* <p>Any {@linkplain Class#isSealed() sealed} interface in the set of
* user-specified interfaces will be omitted from the results, since only
* non-sealed interfaces are eligible for JDK dynamic proxies.
* <p>This method can be useful when registering {@linkplain ProxyHints proxy
* hints} for Spring's AOT support, as demonstrated in the following example
* which uses this method via a {@code static} import.
* <pre class="code">
* RuntimeHints hints = ...
* hints.proxies().registerJdkProxy(completeJdkProxyInterfaces(MyInterface.class));
* </pre>
* @param userInterfaces the set of user-specified interfaces implemented by
* the component to be proxied
* @return the complete set of interfaces that the proxy should implement
* @since 6.0
* @see SpringProxy
* @see Advised
* @see DecoratingProxy
* @see RuntimeHints#proxies()
* @see ProxyHints#registerJdkProxy(Class...)
* @see #completeJdkProxyInterfaces(String...)
*/
public static Class<?>[] completeJdkProxyInterfaces(Class<?>... userInterfaces) {
List<Class<?>> completedInterfaces = new ArrayList<>(userInterfaces.length + 3);
for (Class<?> ifc : userInterfaces) {
Assert.isTrue(ifc.isInterface(), () -> ifc.getName() + " must be an interface");
if (!ifc.isSealed()) {
completedInterfaces.add(ifc);
}
}
completedInterfaces.add(SpringProxy.class);
completedInterfaces.add(Advised.class);
completedInterfaces.add(DecoratingProxy.class);
return completedInterfaces.toArray(Class<?>[]::new);
}
/**
* Complete the set of interfaces that are typically required in a JDK dynamic
* proxy generated by Spring AOP.
* <p>Specifically, {@link SpringProxy}, {@link Advised}, and {@link DecoratingProxy}
* will be appended to the set of user-specified interfaces.
* <p>This method can be useful when registering {@linkplain ProxyHints proxy
* hints} for Spring's AOT support, as demonstrated in the following example
* which uses this method via a {@code static} import.
* <pre class="code">
* RuntimeHints hints = ...
* hints.proxies().registerJdkProxy(completeJdkProxyInterfaces("com.example.MyInterface"));
* </pre>
* @param userInterfaces the set of fully qualified names of user-specified
* interfaces implemented by the component to be proxied
* @return the complete set of fully qualified names of interfaces that the
* proxy should implement
* @since 6.0
* @see SpringProxy
* @see Advised
* @see DecoratingProxy
* @see RuntimeHints#proxies()
* @see ProxyHints#registerJdkProxy(Class...)
* @see #completeJdkProxyInterfaces(Class...)
*/
public static String[] completeJdkProxyInterfaces(String... userInterfaces) {
List<String> completedInterfaces = new ArrayList<>(userInterfaces.length + 3);
Collections.addAll(completedInterfaces, userInterfaces);
completedInterfaces.add(SpringProxy.class.getName());
completedInterfaces.add(Advised.class.getName());
completedInterfaces.add(DecoratingProxy.class.getName());
return completedInterfaces.toArray(String[]::new);
}
/**
* Determine the complete set of interfaces to proxy for the given AOP configuration.
* <p>This will always add the {@link Advised} interface unless the AdvisedSupport's