Skip to content

Commit 48ce714

Browse files
committed
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
1 parent 72fc706 commit 48ce714

29 files changed

+2838
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.hint;
18+
19+
import java.util.Objects;
20+
21+
/**
22+
* Base {@link TypeReference} implementation that ensures consistent behaviour
23+
* for {@code equals()}, {@code hashCode()}, and {@code toString()} based on
24+
* the {@linkplain #getCanonicalName() canonical name}.
25+
*
26+
* @author Stephane Nicoll
27+
* @since 6.0
28+
*/
29+
public abstract class AbstractTypeReference implements TypeReference {
30+
31+
@Override
32+
public int hashCode() {
33+
return Objects.hash(getCanonicalName());
34+
}
35+
36+
@Override
37+
public boolean equals(Object other) {
38+
if (this == other) {
39+
return true;
40+
}
41+
if (!(other instanceof TypeReference otherReference)) {
42+
return false;
43+
}
44+
return getCanonicalName().equals(otherReference.getCanonicalName());
45+
}
46+
47+
@Override
48+
public String toString() {
49+
return getCanonicalName();
50+
}
51+
52+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.hint;
18+
19+
import java.util.Arrays;
20+
import java.util.LinkedList;
21+
import java.util.List;
22+
import java.util.Objects;
23+
import java.util.stream.Collectors;
24+
25+
/**
26+
* A hint that describes the need for a proxy against a concrete class.
27+
*
28+
* @author Stephane Nicoll
29+
* @since 6.0
30+
*/
31+
public final class ClassProxyHint {
32+
33+
private final TypeReference targetClass;
34+
35+
private final List<TypeReference> proxiedInterfaces;
36+
37+
38+
private ClassProxyHint(Builder builder) {
39+
this.targetClass = builder.targetClass;
40+
this.proxiedInterfaces = builder.proxiedInterfaces.stream().distinct().toList();
41+
}
42+
43+
/**
44+
* Initialize a builder with the target class to use.
45+
* @param targetClass the target class of the proxy
46+
* @return a builder for the hint
47+
*/
48+
public static Builder of(TypeReference targetClass) {
49+
return new Builder(targetClass);
50+
}
51+
52+
/**
53+
* Initialize a builder with the target class to use.
54+
* @param targetClass the target class of the proxy
55+
* @return a builder for the hint
56+
*/
57+
public static Builder of(Class<?> targetClass) {
58+
return of(TypeReference.of(targetClass));
59+
}
60+
61+
/**
62+
* Return the target class of the proxy.
63+
* @return the target class
64+
*/
65+
public TypeReference getTargetClass() {
66+
return this.targetClass;
67+
}
68+
69+
/**
70+
* Return the interfaces to be proxied.
71+
* @return the interfaces that the proxy should implement
72+
*/
73+
public List<TypeReference> getProxiedInterfaces() {
74+
return this.proxiedInterfaces;
75+
}
76+
77+
@Override
78+
public boolean equals(Object o) {
79+
if (this == o) {
80+
return true;
81+
}
82+
if (o == null || getClass() != o.getClass()) {
83+
return false;
84+
}
85+
ClassProxyHint that = (ClassProxyHint) o;
86+
return this.targetClass.equals(that.targetClass)
87+
&& this.proxiedInterfaces.equals(that.proxiedInterfaces);
88+
}
89+
90+
@Override
91+
public int hashCode() {
92+
return Objects.hash(this.targetClass, this.proxiedInterfaces);
93+
}
94+
95+
96+
/**
97+
* Builder for {@link ClassProxyHint}.
98+
*/
99+
public static class Builder {
100+
101+
private final TypeReference targetClass;
102+
103+
private final LinkedList<TypeReference> proxiedInterfaces = new LinkedList<>();
104+
105+
106+
public Builder(TypeReference targetClass) {
107+
this.targetClass = targetClass;
108+
}
109+
110+
/**
111+
* Add the specified interfaces that the proxy should implement.
112+
* @param proxiedInterfaces the interfaces the proxy should implement
113+
* @return {@code this}, to facilitate method chaining
114+
*/
115+
public Builder proxiedInterfaces(TypeReference... proxiedInterfaces) {
116+
this.proxiedInterfaces.addAll(Arrays.asList(proxiedInterfaces));
117+
return this;
118+
}
119+
120+
/**
121+
* Add the specified interfaces that the proxy should implement.
122+
* @param proxiedInterfaces the interfaces the proxy should implement
123+
* @return {@code this}, to facilitate method chaining
124+
*/
125+
public Builder proxiedInterfaces(Class<?>... proxiedInterfaces) {
126+
this.proxiedInterfaces.addAll(Arrays.stream(proxiedInterfaces)
127+
.map(TypeReference::of).collect(Collectors.toList()));
128+
return this;
129+
}
130+
131+
/**
132+
* Create a {@link ClassProxyHint} based on the state of this builder.
133+
* @return a class proxy hint
134+
*/
135+
public ClassProxyHint build() {
136+
return new ClassProxyHint(this);
137+
}
138+
139+
}
140+
141+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.hint;
18+
19+
import java.lang.reflect.Constructor;
20+
import java.lang.reflect.Executable;
21+
import java.lang.reflect.Method;
22+
import java.util.Arrays;
23+
import java.util.LinkedHashSet;
24+
import java.util.List;
25+
import java.util.Set;
26+
27+
import org.springframework.util.ObjectUtils;
28+
29+
/**
30+
* A hint that describes the need for reflection on a {@link Method} or
31+
* {@link Constructor}.
32+
*
33+
* @author Stephane Nicoll
34+
* @since 6.0
35+
*/
36+
public final class ExecutableHint extends MemberHint {
37+
38+
private final List<TypeReference> parameterTypes;
39+
40+
private final List<ExecutableMode> modes;
41+
42+
43+
private ExecutableHint(Builder builder) {
44+
super(builder.name);
45+
this.parameterTypes = List.copyOf(builder.parameterTypes);
46+
this.modes = List.copyOf(builder.modes);
47+
}
48+
49+
/**
50+
* Initialize a builder with the parameter types of a constructor.
51+
* @param parameterTypes the parameter types of the constructor
52+
* @return a builder
53+
*/
54+
public static Builder ofConstructor(List<TypeReference> parameterTypes) {
55+
return new Builder("<init>", parameterTypes);
56+
}
57+
58+
/**
59+
* Initialize a builder with the name and parameters types of a method.
60+
* @param name the name of the method
61+
* @param parameterTypes the parameter types of the method
62+
* @return a builder
63+
*/
64+
public static Builder ofMethod(String name, List<TypeReference> parameterTypes) {
65+
return new Builder(name, parameterTypes);
66+
}
67+
68+
/**
69+
* Return the parameter types of the executable.
70+
* @return the parameter types
71+
* @see Executable#getParameterTypes()
72+
*/
73+
public List<TypeReference> getParameterTypes() {
74+
return this.parameterTypes;
75+
}
76+
77+
/**
78+
* Return the {@linkplain ExecutableMode modes} that apply to this hint.
79+
* @return the modes
80+
*/
81+
public List<ExecutableMode> getModes() {
82+
return this.modes;
83+
}
84+
85+
86+
/**
87+
* Builder for {@link ExecutableHint}.
88+
*/
89+
public static final class Builder {
90+
91+
private final String name;
92+
93+
private final List<TypeReference> parameterTypes;
94+
95+
private final Set<ExecutableMode> modes = new LinkedHashSet<>();
96+
97+
98+
private Builder(String name, List<TypeReference> parameterTypes) {
99+
this.name = name;
100+
this.parameterTypes = parameterTypes;
101+
}
102+
103+
/**
104+
* Add the specified {@linkplain ExecutableMode mode} if necessary.
105+
* @param mode the mode to add
106+
* @return {@code this}, to facilitate method chaining
107+
*/
108+
public Builder withMode(ExecutableMode mode) {
109+
this.modes.add(mode);
110+
return this;
111+
}
112+
113+
/**
114+
* Set the {@linkplain ExecutableMode modes} to use.
115+
* @param modes the mode to use
116+
* @return {@code this}, to facilitate method chaining
117+
*/
118+
public Builder setModes(ExecutableMode... modes) {
119+
this.modes.clear();
120+
if (!ObjectUtils.isEmpty(modes)) {
121+
this.modes.addAll(Arrays.asList(modes));
122+
}
123+
return this;
124+
}
125+
126+
/**
127+
* Create an {@link ExecutableHint} based on the state of this builder.
128+
* @return an executable hint
129+
*/
130+
public ExecutableHint build() {
131+
return new ExecutableHint(this);
132+
}
133+
134+
}
135+
136+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.hint;
18+
19+
import java.lang.reflect.Executable;
20+
21+
/**
22+
* Represent the need of reflection for a given {@link Executable}.
23+
*
24+
* @author Stephane Nicoll
25+
* @since 6.0
26+
*/
27+
public enum ExecutableMode {
28+
29+
/**
30+
* Only retrieving the {@link Executable} and its metadata is required.
31+
*/
32+
INTROSPECT,
33+
34+
/**
35+
* Full reflection support is required, including the ability to invoke
36+
* the {@link Executable}.
37+
*/
38+
INVOKE;
39+
40+
}

0 commit comments

Comments
 (0)