Hide builder implementations in RuntimeHints API

This commit is contained in:
Stephane Nicoll 2022-04-14 10:05:38 +02:00
parent c28fd91a10
commit 780d07217b
9 changed files with 79 additions and 48 deletions

View File

@ -20,7 +20,6 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* A hint that describes the need for a proxy against a concrete class.
@ -55,6 +54,9 @@ public final class ClassProxyHint {
* @return a builder for the hint
*/
public static Builder of(Class<?> targetClass) {
if (targetClass.isInterface()) {
throw new IllegalArgumentException("Should not be an interface: " + targetClass);
}
return of(TypeReference.of(targetClass));
}
@ -103,7 +105,7 @@ public final class ClassProxyHint {
private final LinkedList<TypeReference> proxiedInterfaces = new LinkedList<>();
public Builder(TypeReference targetClass) {
Builder(TypeReference targetClass) {
this.targetClass = targetClass;
}
@ -124,7 +126,7 @@ public final class ClassProxyHint {
*/
public Builder proxiedInterfaces(Class<?>... proxiedInterfaces) {
this.proxiedInterfaces.addAll(Arrays.stream(proxiedInterfaces)
.map(TypeReference::of).collect(Collectors.toList()));
.map(TypeReference::of).toList());
return this;
}
@ -132,7 +134,7 @@ public final class ClassProxyHint {
* Create a {@link ClassProxyHint} based on the state of this builder.
* @return a class proxy hint
*/
public ClassProxyHint build() {
ClassProxyHint build() {
return new ClassProxyHint(this);
}

View File

@ -86,7 +86,7 @@ public final class ExecutableHint extends MemberHint {
/**
* Builder for {@link ExecutableHint}.
*/
public static final class Builder {
public static class Builder {
private final String name;
@ -95,7 +95,7 @@ public final class ExecutableHint extends MemberHint {
private final Set<ExecutableMode> modes = new LinkedHashSet<>();
private Builder(String name, List<TypeReference> parameterTypes) {
Builder(String name, List<TypeReference> parameterTypes) {
this.name = name;
this.parameterTypes = parameterTypes;
}
@ -127,7 +127,7 @@ public final class ExecutableHint extends MemberHint {
* Create an {@link ExecutableHint} based on the state of this builder.
* @return an executable hint
*/
public ExecutableHint build() {
ExecutableHint build() {
return new ExecutableHint(this);
}

View File

@ -66,7 +66,7 @@ public final class FieldHint extends MemberHint {
private boolean allowUnsafeAccess;
public Builder(String name) {
Builder(String name) {
this.name = name;
}
@ -94,7 +94,7 @@ public final class FieldHint extends MemberHint {
* Create a {@link FieldHint} based on the state of this builder.
* @return a field hint
*/
public FieldHint build() {
FieldHint build() {
return new FieldHint(this);
}

View File

@ -21,7 +21,6 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* A hint that describes the need of a JDK {@link Proxy}, that is an
@ -39,6 +38,24 @@ public final class JdkProxyHint {
this.proxiedInterfaces = List.copyOf(builder.proxiedInterfaces);
}
/**
* Initialize a builder with the proxied interfaces to use.
* @param proxiedInterfaces the interfaces the proxy should implement
* @return a builder for the hint
*/
public static Builder of(TypeReference... proxiedInterfaces) {
return new Builder().proxiedInterfaces(proxiedInterfaces);
}
/**
* Initialize a builder with the proxied interfaces to use.
* @param proxiedInterfaces the interfaces the proxy should implement
* @return a builder for the hint
*/
public static Builder of(Class<?>... proxiedInterfaces) {
return new Builder().proxiedInterfaces(proxiedInterfaces);
}
/**
* Return the interfaces to be proxied.
* @return the interfaces that the proxy should implement
@ -70,8 +87,11 @@ public final class JdkProxyHint {
*/
public static class Builder {
private final LinkedList<TypeReference> proxiedInterfaces = new LinkedList<>();
private final LinkedList<TypeReference> proxiedInterfaces;
Builder() {
this.proxiedInterfaces = new LinkedList<>();
}
/**
* Add the specified interfaces that the proxy should implement.
@ -89,8 +109,7 @@ public final class JdkProxyHint {
* @return {@code this}, to facilitate method chaining
*/
public Builder proxiedInterfaces(Class<?>... proxiedInterfaces) {
this.proxiedInterfaces.addAll(Arrays.stream(proxiedInterfaces)
.map(TypeReference::of).collect(Collectors.toList()));
this.proxiedInterfaces.addAll(toTypeReferences(proxiedInterfaces));
return this;
}
@ -98,10 +117,19 @@ public final class JdkProxyHint {
* Create a {@link JdkProxyHint} based on the state of this builder.
* @return a jdk proxy hint
*/
public JdkProxyHint build() {
JdkProxyHint build() {
return new JdkProxyHint(this);
}
private static List<TypeReference> toTypeReferences(Class<?>... proxiedInterfaces) {
List<String> concreteTypes = Arrays.stream(proxiedInterfaces)
.filter(candidate -> !candidate.isInterface()).map(Class::getName).toList();
if (!concreteTypes.isEmpty()) {
throw new IllegalArgumentException("Not an interface: " + concreteTypes);
}
return Arrays.stream(proxiedInterfaces).map(TypeReference::of).toList();
}
}
}

View File

@ -16,13 +16,9 @@
package org.springframework.aot.hint;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.aot.hint.ClassProxyHint.Builder;
@ -58,11 +54,13 @@ public class ProxyHints {
/**
* Register a {@link JdkProxyHint}.
* @param hint the supplier to the hint
* @param jdkProxyHint the supplier to the hint
* @return {@code this}, to facilitate method chaining
*/
public ProxyHints registerJdkProxy(Supplier<JdkProxyHint> hint) {
this.jdkProxies.add(hint.get());
public ProxyHints registerJdkProxy(Consumer<JdkProxyHint.Builder> jdkProxyHint) {
JdkProxyHint.Builder builder = new JdkProxyHint.Builder();
jdkProxyHint.accept(builder);
this.jdkProxies.add(builder.build());
return this;
}
@ -73,8 +71,8 @@ public class ProxyHints {
* @return {@code this}, to facilitate method chaining
*/
public ProxyHints registerJdkProxy(TypeReference... proxiedInterfaces) {
return registerJdkProxy(() -> new JdkProxyHint.Builder()
.proxiedInterfaces(proxiedInterfaces).build());
return registerJdkProxy(jdkProxyHint ->
jdkProxyHint.proxiedInterfaces(proxiedInterfaces));
}
/**
@ -84,13 +82,8 @@ public class ProxyHints {
* @return {@code this}, to facilitate method chaining
*/
public ProxyHints registerJdkProxy(Class<?>... proxiedInterfaces) {
List<String> concreteTypes = Arrays.stream(proxiedInterfaces)
.filter(candidate -> !candidate.isInterface()).map(Class::getName).collect(Collectors.toList());
if (!concreteTypes.isEmpty()) {
throw new IllegalArgumentException("Not an interface: " + concreteTypes);
}
return registerJdkProxy(() -> new JdkProxyHint.Builder()
.proxiedInterfaces(proxiedInterfaces).build());
return registerJdkProxy(jdkProxyHint ->
jdkProxyHint.proxiedInterfaces(proxiedInterfaces));
}
/**
@ -101,10 +94,7 @@ public class ProxyHints {
* @return {@code this}, to facilitate method chaining
*/
public ProxyHints registerClassProxy(TypeReference targetClass, Consumer<Builder> classProxyHint) {
Builder builder = ClassProxyHint.of(targetClass);
classProxyHint.accept(builder);
this.classProxies.add(builder.build());
return this;
return addClassProxyHint(ClassProxyHint.of(targetClass), classProxyHint);
}
/**
@ -114,10 +104,13 @@ public class ProxyHints {
* @return {@code this}, to facilitate method chaining
*/
public ProxyHints registerClassProxy(Class<?> targetClass, Consumer<Builder> classProxyHint) {
if (targetClass.isInterface()) {
throw new IllegalArgumentException("Should not be an interface: " + targetClass);
}
return registerClassProxy(TypeReference.of(targetClass), classProxyHint);
return addClassProxyHint(ClassProxyHint.of(targetClass), classProxyHint);
}
private ProxyHints addClassProxyHint(ClassProxyHint.Builder builder, Consumer<ClassProxyHint.Builder> classProxyHint) {
classProxyHint.accept(builder);
this.classProxies.add(builder.build());
return this;
}
}

View File

@ -25,6 +25,7 @@ import java.util.function.Consumer;
import java.util.stream.Stream;
import org.springframework.aot.hint.ResourcePatternHint.Builder;
import org.springframework.lang.Nullable;
/**
* Gather the need for resources available at runtime.
@ -72,7 +73,7 @@ public class ResourceHints {
* @param resourceHint a builder to further customize the resource pattern
* @return {@code this}, to facilitate method chaining
*/
public ResourceHints registerPattern(String include, Consumer<Builder> resourceHint) {
public ResourceHints registerPattern(String include, @Nullable Consumer<Builder> resourceHint) {
Builder builder = new Builder().includes(include);
if (resourceHint != null) {
resourceHint.accept(builder);
@ -136,7 +137,7 @@ public class ResourceHints {
return candidate.replace(".", "/") + ".class";
}
private void buildName(TypeReference type, StringBuilder sb) {
private void buildName(@Nullable TypeReference type, StringBuilder sb) {
if (type == null) {
return;
}

View File

@ -96,7 +96,7 @@ public final class ResourcePatternHint {
* builder.
* @return a resource pattern hint
*/
public ResourcePatternHint build() {
ResourcePatternHint build() {
return new ResourcePatternHint(this);
}

View File

@ -223,7 +223,7 @@ public final class TypeHint {
* Create a {@link TypeHint} based on the state of this builder.
* @return a type hint
*/
public TypeHint build() {
TypeHint build() {
return new TypeHint(this);
}

View File

@ -21,13 +21,10 @@ import java.util.Arrays;
import java.util.Properties;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.JdkProxyHint.Builder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@ -78,6 +75,16 @@ class ProxyHintsTests {
assertThat(this.proxyHints.jdkProxies()).singleElement().satisfies(proxiedInterfaces(Function.class));
}
@Test
void registerClassProxyWithTargetClassName() {
this.proxyHints.registerClassProxy(TypeReference.of(Properties.class.getName()), classProxyHint ->
classProxyHint.proxiedInterfaces(Serializable.class));
assertThat(this.proxyHints.classProxies()).singleElement().satisfies(classProxyHint -> {
assertThat(classProxyHint.getTargetClass()).isEqualTo(TypeReference.of(Properties.class));
assertThat(classProxyHint.getProxiedInterfaces()).containsOnly(TypeReference.of(Serializable.class));
});
}
@Test
void registerClassProxyWithTargetClass() {
this.proxyHints.registerClassProxy(Properties.class, classProxyHint ->
@ -94,11 +101,11 @@ class ProxyHintsTests {
})).withMessageContaining(Serializable.class.getName());
}
private static Supplier<JdkProxyHint> springProxy(TypeReference proxiedInterface) {
return () -> new Builder().proxiedInterfaces(Stream.of("org.springframework.aop.SpringProxy",
private static Consumer<JdkProxyHint.Builder> springProxy(TypeReference proxiedInterface) {
return builder -> builder.proxiedInterfaces(Stream.of("org.springframework.aop.SpringProxy",
"org.springframework.aop.framework.Advised", "org.springframework.core.DecoratingProxy")
.map(TypeReference::of).toArray(TypeReference[]::new))
.proxiedInterfaces(proxiedInterface).build();
.proxiedInterfaces(proxiedInterface);
}
private Consumer<JdkProxyHint> proxiedInterfaces(String... proxiedInterfaces) {