mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-24 07:39:09 +00:00
Provide an API to record various runtime hints
This commit provides an API to record the need for reflection, resources, serialization, and proxies so that the runtime can be optimized accordingly. `RuntimeHints` provides an entry point to register the following: * Reflection hints: individual elements of a type can be defined, as well as a predefined categories (identified by the `MemberCategory` enum). A method or constructor hint can refine whether the executable should only be introspected or also invoked. * Resource hints: patterns using includes/excludes identify the resources to include at runtime. Resource bundles are also supported. * Java Serialization hints: types that use java serialization can be registered. * Proxy hints: both interfaces-based (JDK) proxy and class-based proxy can be defined. This commit also introduces a `TypeReference` abstraction that permits to record hints for types that are not available on the classpath, or not compiled yet (generated code). Closes gh-27829
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.Serializable;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.hint.JdkProxyHint.Builder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ClassProxyHint}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ClassProxyHintTests {
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameInstanceIsTrue() {
|
||||
ClassProxyHint hint = ClassProxyHint.of(Properties.class).build();
|
||||
assertThat(hint).isEqualTo(hint);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameTargetClassIsTrue() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(TypeReference.of(Properties.class)).build();
|
||||
assertThat(first).isEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameProxiedInterfacesIsTrue() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(Serializable.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(TypeReference.of(Serializable.class)).build();
|
||||
assertThat(first).isEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithDifferentTargetClassIsFalse() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Hashtable.class).build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameProxiedInterfacesDifferentOrderIsFalse() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(Serializable.class, Closeable.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(TypeReference.of(Closeable.class), TypeReference.of(Serializable.class))
|
||||
.build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithDifferentProxiedInterfacesIsFalse() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(Serializable.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(TypeReference.of(Closeable.class)).build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithNonClassProxyHintIsFalse() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class).build();
|
||||
JdkProxyHint second = new Builder().proxiedInterfaces(Function.class).build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JavaSerializationHints}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class JavaSerializationHintsTests {
|
||||
|
||||
private final JavaSerializationHints javaSerializationHints = new JavaSerializationHints();
|
||||
|
||||
@Test
|
||||
void registerTypeTwiceExposesOneHint() {
|
||||
this.javaSerializationHints.registerType(URL.class);
|
||||
this.javaSerializationHints.registerType(TypeReference.of(URL.class.getName()));
|
||||
assertThat(this.javaSerializationHints.types()).singleElement()
|
||||
.isEqualTo(TypeReference.of(URL.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.hint.JdkProxyHint.Builder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JdkProxyHint}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class JdkProxyHintTests {
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameInstanceIsTrue() {
|
||||
JdkProxyHint hint = new Builder().proxiedInterfaces(Function.class, Consumer.class).build();
|
||||
assertThat(hint).isEqualTo(hint);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameProxiedInterfacesIsTrue() {
|
||||
JdkProxyHint first = new Builder().proxiedInterfaces(Function.class, Consumer.class).build();
|
||||
JdkProxyHint second = new Builder().proxiedInterfaces(TypeReference.of(Function.class.getName()),
|
||||
TypeReference.of(Consumer.class)).build();
|
||||
assertThat(first).isEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameProxiedInterfacesDifferentOrderIsFalse() {
|
||||
JdkProxyHint first = new Builder().proxiedInterfaces(Function.class, Consumer.class).build();
|
||||
JdkProxyHint second = new Builder().proxiedInterfaces(TypeReference.of(Consumer.class),
|
||||
TypeReference.of(Function.class.getName())).build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithDifferentProxiedInterfacesIsFalse() {
|
||||
JdkProxyHint first = new Builder().proxiedInterfaces(Function.class).build();
|
||||
JdkProxyHint second = new Builder().proxiedInterfaces(TypeReference.of(Function.class.getName()),
|
||||
TypeReference.of(Consumer.class)).build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithNonJdkProxyHintIsFalse() {
|
||||
JdkProxyHint first = new Builder().proxiedInterfaces(Function.class).build();
|
||||
TypeReference second = TypeReference.of(Function.class);
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import java.io.Serializable;
|
||||
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.core.hint.JdkProxyHint.Builder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ProxyHints}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ProxyHintsTests {
|
||||
|
||||
private final ProxyHints proxyHints = new ProxyHints();
|
||||
|
||||
|
||||
@Test
|
||||
void registerJdkProxyWithInterfaceClass() {
|
||||
this.proxyHints.registerJdkProxy(Function.class);
|
||||
assertThat(this.proxyHints.jdkProxies()).singleElement().satisfies(proxiedInterfaces(Function.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerJdkProxyWithConcreteClass() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.proxyHints.registerJdkProxy(String.class))
|
||||
.withMessageContaining(String.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerJdkProxyWithInterfaceClassNames() {
|
||||
this.proxyHints.registerJdkProxy(TypeReference.of(Function.class),
|
||||
TypeReference.of("com.example.Advised"));
|
||||
assertThat(this.proxyHints.jdkProxies()).singleElement().satisfies(proxiedInterfaces(
|
||||
Function.class.getName(), "com.example.Advised"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerJdkProxyWithSupplier() {
|
||||
this.proxyHints.registerJdkProxy(springProxy(TypeReference.of("com.example.Test")));
|
||||
assertThat(this.proxyHints.jdkProxies()).singleElement().satisfies(proxiedInterfaces(
|
||||
"org.springframework.aop.SpringProxy",
|
||||
"org.springframework.aop.framework.Advised",
|
||||
"org.springframework.core.DecoratingProxy",
|
||||
"com.example.Test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerJdkProxyTwiceExposesOneHint() {
|
||||
this.proxyHints.registerJdkProxy(Function.class);
|
||||
this.proxyHints.registerJdkProxy(TypeReference.of(Function.class.getName()));
|
||||
assertThat(this.proxyHints.jdkProxies()).singleElement().satisfies(proxiedInterfaces(Function.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerClassProxyWithTargetClass() {
|
||||
this.proxyHints.registerClassProxy(Properties.class, 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 registerClassProxyWithTargetInterface() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.proxyHints.registerClassProxy(Serializable.class, classProxyHint -> {
|
||||
})).withMessageContaining(Serializable.class.getName());
|
||||
}
|
||||
|
||||
private static Supplier<JdkProxyHint> springProxy(TypeReference proxiedInterface) {
|
||||
return () -> new 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();
|
||||
}
|
||||
|
||||
private Consumer<JdkProxyHint> proxiedInterfaces(String... proxiedInterfaces) {
|
||||
return jdkProxyHint -> assertThat(jdkProxyHint.getProxiedInterfaces())
|
||||
.containsExactly(Arrays.stream(proxiedInterfaces)
|
||||
.map(TypeReference::of).toArray(TypeReference[]::new));
|
||||
}
|
||||
|
||||
private Consumer<JdkProxyHint> proxiedInterfaces(Class<?>... proxiedInterfaces) {
|
||||
return jdkProxyHint -> assertThat(jdkProxyHint.getProxiedInterfaces())
|
||||
.containsExactly(Arrays.stream(proxiedInterfaces)
|
||||
.map(TypeReference::of).toArray(TypeReference[]::new));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReflectionHints}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ReflectionHintsTests {
|
||||
|
||||
private final ReflectionHints reflectionHints = new ReflectionHints();
|
||||
|
||||
@Test
|
||||
void registerType() {
|
||||
this.reflectionHints.registerType(TypeReference.of(String.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS));
|
||||
assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(
|
||||
typeWithMemberCategories(String.class, MemberCategory.DECLARED_FIELDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerTypeReuseBuilder() {
|
||||
this.reflectionHints.registerType(TypeReference.of(String.class),
|
||||
typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
this.reflectionHints.registerField(ReflectionUtils.findField(String.class, "value"));
|
||||
assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getType().getCanonicalName()).isEqualTo(String.class.getCanonicalName());
|
||||
assertThat(typeHint.fields()).singleElement().satisfies(fieldHint -> assertThat(fieldHint.getName()).isEqualTo("value"));
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerClass() {
|
||||
this.reflectionHints.registerType(Integer.class,
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
||||
assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(
|
||||
typeWithMemberCategories(Integer.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerField() {
|
||||
this.reflectionHints.registerField(ReflectionUtils.findField(TestType.class, "field"));
|
||||
assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getType().getCanonicalName()).isEqualTo(TestType.class.getCanonicalName());
|
||||
assertThat(typeHint.fields()).singleElement().satisfies(fieldHint ->
|
||||
assertThat(fieldHint.getName()).isEqualTo("field"));
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.methods()).isEmpty();
|
||||
assertThat(typeHint.getMemberCategories()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerConstructor() {
|
||||
this.reflectionHints.registerConstructor(TestType.class.getDeclaredConstructors()[0]);
|
||||
assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getMemberCategories()).isEmpty();
|
||||
assertThat(typeHint.getType().getCanonicalName()).isEqualTo(TestType.class.getCanonicalName());
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
assertThat(typeHint.constructors()).singleElement().satisfies(constructorHint -> {
|
||||
assertThat(constructorHint.getParameterTypes()).isEmpty();
|
||||
assertThat(constructorHint.getModes()).containsOnly(ExecutableMode.INVOKE);
|
||||
});
|
||||
assertThat(typeHint.methods()).isEmpty();
|
||||
assertThat(typeHint.getMemberCategories()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerMethod() {
|
||||
this.reflectionHints.registerMethod(ReflectionUtils.findMethod(TestType.class, "setName", String.class));
|
||||
assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getType().getCanonicalName()).isEqualTo(TestType.class.getCanonicalName());
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> {
|
||||
assertThat(methodHint.getName()).isEqualTo("setName");
|
||||
assertThat(methodHint.getParameterTypes()).containsOnly(TypeReference.of(String.class));
|
||||
assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Consumer<TypeHint> typeWithMemberCategories(Class<?> type, MemberCategory... memberCategories) {
|
||||
return typeHint -> {
|
||||
assertThat(typeHint.getType().getCanonicalName()).isEqualTo(type.getCanonicalName());
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.methods()).isEmpty();
|
||||
assertThat(typeHint.getMemberCategories()).containsExactly(memberCategories);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class TestType {
|
||||
|
||||
private String field;
|
||||
|
||||
void setName(String name) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.hint.ResourceHintsTests.Nested.Inner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceHints}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ResourceHintsTests {
|
||||
|
||||
private final ResourceHints resourceHints = new ResourceHints();
|
||||
|
||||
@Test
|
||||
void registerType() {
|
||||
this.resourceHints.registerType(String.class);
|
||||
assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(
|
||||
patternOf("java/lang/String.class"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerTypeWithNestedType() {
|
||||
this.resourceHints.registerType(TypeReference.of(Nested.class));
|
||||
assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(
|
||||
patternOf("org/springframework/core/hint/ResourceHintsTests$Nested.class"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerTypeWithInnerNestedType() {
|
||||
this.resourceHints.registerType(TypeReference.of(Inner.class));
|
||||
assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(
|
||||
patternOf("org/springframework/core/hint/ResourceHintsTests$Nested$Inner.class"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerTypeSeveralTimesAddsOnlyOneEntry() {
|
||||
this.resourceHints.registerType(String.class);
|
||||
this.resourceHints.registerType(TypeReference.of(String.class));
|
||||
assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(
|
||||
patternOf("java/lang/String.class"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerExactMatch() {
|
||||
this.resourceHints.registerPattern("com/example/test.properties");
|
||||
this.resourceHints.registerPattern("com/example/another.properties");
|
||||
assertThat(this.resourceHints.resourcePatterns())
|
||||
.anySatisfy(patternOf("com/example/test.properties"))
|
||||
.anySatisfy(patternOf("com/example/another.properties"))
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerPattern() {
|
||||
this.resourceHints.registerPattern("com/example/*.properties");
|
||||
assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(
|
||||
patternOf("com/example/*.properties"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerPatternWithIncludesAndExcludes() {
|
||||
this.resourceHints.registerPattern("com/example/*.properties",
|
||||
resourceHint -> resourceHint.excludes("com/example/to-ignore.properties"));
|
||||
assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(patternOf(
|
||||
List.of("com/example/*.properties"),
|
||||
List.of("com/example/to-ignore.properties")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerResourceBundle() {
|
||||
this.resourceHints.registerResourceBundle("com.example.message");
|
||||
assertThat(this.resourceHints.resourceBundles()).singleElement()
|
||||
.satisfies(resourceBundle("com.example.message"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerResourceBundleSeveralTimesAddsOneEntry() {
|
||||
this.resourceHints.registerResourceBundle("com.example.message")
|
||||
.registerResourceBundle("com.example.message");
|
||||
assertThat(this.resourceHints.resourceBundles()).singleElement()
|
||||
.satisfies(resourceBundle("com.example.message"));
|
||||
}
|
||||
|
||||
|
||||
private Consumer<ResourcePatternHint> patternOf(String... includes) {
|
||||
return patternOf(Arrays.asList(includes), Collections.emptyList());
|
||||
}
|
||||
|
||||
private Consumer<ResourceBundleHint> resourceBundle(String baseName) {
|
||||
return resourceBundleHint -> assertThat(resourceBundleHint.getBaseName()).isEqualTo(baseName);
|
||||
}
|
||||
|
||||
private Consumer<ResourcePatternHint> patternOf(List<String> includes, List<String> excludes) {
|
||||
return pattern -> {
|
||||
assertThat(pattern.getIncludes()).containsExactlyElementsOf(includes);
|
||||
assertThat(pattern.getExcludes()).containsExactlyElementsOf(excludes);
|
||||
};
|
||||
}
|
||||
|
||||
static class Nested {
|
||||
|
||||
static class Inner {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link RuntimeHints}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class RuntimeHintsTests {
|
||||
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
@Test
|
||||
void reflectionHintWithClass() {
|
||||
this.hints.reflection().registerType(String.class,
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
||||
assertThat(this.hints.reflection().typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getType().getCanonicalName()).isEqualTo(String.class.getCanonicalName());
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.methods()).isEmpty();
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourceHintWithClass() {
|
||||
this.hints.resources().registerType(String.class);
|
||||
assertThat(this.hints.resources().resourcePatterns()).singleElement().satisfies(resourceHint -> {
|
||||
assertThat(resourceHint.getIncludes()).containsExactly("java/lang/String.class");
|
||||
assertThat(resourceHint.getExcludes()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void javaSerializationHintWithClass() {
|
||||
this.hints.javaSerialization().registerType(String.class);
|
||||
assertThat(this.hints.javaSerialization().types()).containsExactly(TypeReference.of(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jdkProxyWithClass() {
|
||||
this.hints.proxies().registerJdkProxy(Function.class);
|
||||
assertThat(this.hints.proxies().jdkProxies()).singleElement().satisfies(jdkProxyHint ->
|
||||
assertThat(jdkProxyHint.getProxiedInterfaces()).containsExactly(TypeReference.of(Function.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.hint.TypeHint.Builder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link TypeHint}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class TypeHintTests {
|
||||
|
||||
@Test
|
||||
void createWithNullTypeReference() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> TypeHint.of(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithType() {
|
||||
TypeHint hint = TypeHint.of(TypeReference.of(String.class)).build();
|
||||
assertThat(hint).isNotNull();
|
||||
assertThat(hint.getType().getCanonicalName()).isEqualTo("java.lang.String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithTypeAndReachableType() {
|
||||
TypeHint hint = TypeHint.of(TypeReference.of(String.class))
|
||||
.onReachableType(TypeReference.of("com.example.Test")).build();
|
||||
assertThat(hint).isNotNull();
|
||||
assertThat(hint.getReachableType()).isNotNull();
|
||||
assertThat(hint.getReachableType().getCanonicalName()).isEqualTo("com.example.Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithField() {
|
||||
TypeHint hint = TypeHint.of(TypeReference.of(String.class))
|
||||
.withField("value", fieldHint -> fieldHint.allowWrite(true)).build();
|
||||
assertThat(hint.fields()).singleElement().satisfies(fieldHint -> {
|
||||
assertThat(fieldHint.getName()).isEqualTo("value");
|
||||
assertThat(fieldHint.isAllowWrite()).isTrue();
|
||||
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithFieldReuseBuilder() {
|
||||
Builder builder = TypeHint.of(TypeReference.of(String.class));
|
||||
builder.withField("value", fieldHint -> fieldHint.allowUnsafeAccess(true));
|
||||
builder.withField("value", fieldHint -> {
|
||||
fieldHint.allowWrite(true);
|
||||
fieldHint.allowUnsafeAccess(false);
|
||||
});
|
||||
TypeHint hint = builder.build();
|
||||
assertThat(hint.fields()).singleElement().satisfies(fieldHint -> {
|
||||
assertThat(fieldHint.getName()).isEqualTo("value");
|
||||
assertThat(fieldHint.isAllowWrite()).isTrue();
|
||||
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithConstructor() {
|
||||
List<TypeReference> parameterTypes = List.of(TypeReference.of(byte[].class), TypeReference.of(int.class));
|
||||
TypeHint hint = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes,
|
||||
constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)).build();
|
||||
assertThat(hint.constructors()).singleElement().satisfies(constructorHint -> {
|
||||
assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes);
|
||||
assertThat(constructorHint.getModes()).containsOnly(ExecutableMode.INVOKE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createConstructorReuseBuilder() {
|
||||
List<TypeReference> parameterTypes = List.of(TypeReference.of(byte[].class), TypeReference.of(int.class));
|
||||
Builder builder = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes,
|
||||
constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE));
|
||||
TypeHint hint = builder.withConstructor(parameterTypes, constructorHint ->
|
||||
constructorHint.withMode(ExecutableMode.INTROSPECT)).build();
|
||||
assertThat(hint.constructors()).singleElement().satisfies(constructorHint -> {
|
||||
assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
|
||||
assertThat(constructorHint.getModes()).containsOnly(ExecutableMode.INVOKE, ExecutableMode.INTROSPECT);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithMethod() {
|
||||
List<TypeReference> parameterTypes = List.of(TypeReference.of(char[].class));
|
||||
TypeHint hint = TypeHint.of(TypeReference.of(String.class)).withMethod("valueOf", parameterTypes,
|
||||
methodHint -> methodHint.withMode(ExecutableMode.INVOKE)).build();
|
||||
assertThat(hint.methods()).singleElement().satisfies(methodHint -> {
|
||||
assertThat(methodHint.getName()).isEqualTo("valueOf");
|
||||
assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
|
||||
assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithMethodReuseBuilder() {
|
||||
List<TypeReference> parameterTypes = List.of(TypeReference.of(char[].class));
|
||||
Builder builder = TypeHint.of(TypeReference.of(String.class)).withMethod("valueOf", parameterTypes,
|
||||
methodHint -> methodHint.withMode(ExecutableMode.INVOKE));
|
||||
TypeHint hint = builder.withMethod("valueOf", parameterTypes,
|
||||
methodHint -> methodHint.setModes(ExecutableMode.INTROSPECT)).build();
|
||||
assertThat(hint.methods()).singleElement().satisfies(methodHint -> {
|
||||
assertThat(methodHint.getName()).isEqualTo("valueOf");
|
||||
assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
|
||||
assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INTROSPECT);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithMemberCategory() {
|
||||
TypeHint hint = TypeHint.of(TypeReference.of(String.class))
|
||||
.withMembers(MemberCategory.DECLARED_FIELDS).build();
|
||||
assertThat(hint.getMemberCategories()).containsOnly(MemberCategory.DECLARED_FIELDS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.hint;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TypeReference}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class TypeReferenceTests {
|
||||
|
||||
@Test
|
||||
void typeReferenceWithClassName() {
|
||||
TypeReference type = TypeReference.of("java.lang.String");
|
||||
assertThat(type.getCanonicalName()).isEqualTo("java.lang.String");
|
||||
assertThat(type.getPackageName()).isEqualTo("java.lang");
|
||||
assertThat(type.getSimpleName()).isEqualTo("String");
|
||||
assertThat(type.getEnclosingType()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeReferenceWithInnerClassName() {
|
||||
TypeReference type = TypeReference.of("com.example.Example$Inner");
|
||||
assertThat(type.getCanonicalName()).isEqualTo("com.example.Example.Inner");
|
||||
assertThat(type.getPackageName()).isEqualTo("com.example");
|
||||
assertThat(type.getSimpleName()).isEqualTo("Inner");
|
||||
assertThat(type.getEnclosingType()).satisfies(enclosingType -> {
|
||||
assertThat(enclosingType.getCanonicalName()).isEqualTo("com.example.Example");
|
||||
assertThat(enclosingType.getPackageName()).isEqualTo("com.example");
|
||||
assertThat(enclosingType.getSimpleName()).isEqualTo("Example");
|
||||
assertThat(enclosingType.getEnclosingType()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeReferenceWithNestedInnerClassName() {
|
||||
TypeReference type = TypeReference.of("com.example.Example$Inner$Nested");
|
||||
assertThat(type.getCanonicalName()).isEqualTo("com.example.Example.Inner.Nested");
|
||||
assertThat(type.getPackageName()).isEqualTo("com.example");
|
||||
assertThat(type.getSimpleName()).isEqualTo("Nested");
|
||||
assertThat(type.getEnclosingType()).satisfies(enclosingType -> {
|
||||
assertThat(enclosingType.getCanonicalName()).isEqualTo("com.example.Example.Inner");
|
||||
assertThat(enclosingType.getPackageName()).isEqualTo("com.example");
|
||||
assertThat(enclosingType.getSimpleName()).isEqualTo("Inner");
|
||||
assertThat(enclosingType.getEnclosingType()).satisfies(parentEnclosingType -> {
|
||||
assertThat(parentEnclosingType.getCanonicalName()).isEqualTo("com.example.Example");
|
||||
assertThat(parentEnclosingType.getPackageName()).isEqualTo("com.example");
|
||||
assertThat(parentEnclosingType.getSimpleName()).isEqualTo("Example");
|
||||
assertThat(parentEnclosingType.getEnclosingType()).isNull();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithIdenticalNameIsTrue() {
|
||||
assertThat(TypeReference.of(String.class)).isEqualTo(
|
||||
TypeReference.of("java.lang.String"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithNonTypeReferenceIsFalse() {
|
||||
assertThat(TypeReference.of(String.class)).isNotEqualTo("java.lang.String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringUsesCanonicalName() {
|
||||
assertThat(TypeReference.of(String.class)).hasToString("java.lang.String");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user