16
16
package org .springframework .hateoas .server .core ;
17
17
18
18
import java .lang .reflect .Method ;
19
+ import java .util .Collection ;
19
20
import java .util .List ;
21
+ import java .util .Objects ;
22
+ import java .util .function .Function ;
20
23
import java .util .stream .Collectors ;
21
24
22
25
import org .springframework .core .ResolvableType ;
23
26
import org .springframework .hateoas .Affordance ;
24
27
import org .springframework .hateoas .Link ;
25
28
import org .springframework .hateoas .LinkRelation ;
26
29
import org .springframework .hateoas .QueryParameter ;
27
- import org .springframework .hateoas .mediatype .AffordanceModelFactory ;
28
30
import org .springframework .hateoas .mediatype .Affordances ;
31
+ import org .springframework .http .HttpMethod ;
32
+ import org .springframework .http .MediaType ;
33
+ import org .springframework .lang .Nullable ;
34
+ import org .springframework .util .ConcurrentLruCache ;
29
35
import org .springframework .web .bind .annotation .RequestBody ;
36
+ import org .springframework .web .bind .annotation .RequestMapping ;
30
37
import org .springframework .web .bind .annotation .RequestParam ;
31
38
32
39
/**
37
44
*/
38
45
public class SpringAffordanceBuilder {
39
46
47
+ @ SuppressWarnings ("deprecation" ) //
48
+ public static final MappingDiscoverer DISCOVERER = CachingMappingDiscoverer
49
+ .of (new PropertyResolvingMappingDiscoverer (new AnnotationMappingDiscoverer (RequestMapping .class )));
50
+
51
+ private static final ConcurrentLruCache <AffordanceKey , Function <Affordances , List <Affordance >>> AFFORDANCES_CACHE = new ConcurrentLruCache <>(
52
+ 256 , key -> SpringAffordanceBuilder .create (key .type , key .method ));
53
+
40
54
/**
41
- * Use the attributes of the current method call along with a collection of {@link AffordanceModelFactory}'s to create
42
- * a set of {@link Affordance}s.
55
+ * Returns all {@link Affordance}s for the given type's method and base URI.
43
56
*
44
57
* @param type must not be {@literal null}.
45
58
* @param method must not be {@literal null}.
46
- * @param href must not be {@literal null}.
47
- * @param discoverer must not be {@literal null}.
59
+ * @param href must not be {@literal null} or empty.
48
60
* @return
49
61
*/
50
- public static List <Affordance > create (Class <?> type , Method method , String href , MappingDiscoverer discoverer ) {
62
+ public static List <Affordance > getAffordances (Class <?> type , Method method , String href ) {
51
63
52
64
String methodName = method .getName ();
53
65
Link affordanceLink = Link .of (href , LinkRelation .of (methodName ));
54
66
67
+ return AFFORDANCES_CACHE
68
+ .get (new AffordanceKey (type , method ))
69
+ .apply (Affordances .of (affordanceLink ));
70
+ }
71
+
72
+ /**
73
+ * Returns the mapping for the given type's method.
74
+ *
75
+ * @param type must not be {@literal null}.
76
+ * @param method must not be {@literal null}.
77
+ * @return
78
+ */
79
+ @ Nullable
80
+ public static String getMapping (Class <?> type , Method method ) {
81
+ return DISCOVERER .getMapping (type , method );
82
+ }
83
+
84
+ private static Function <Affordances , List <Affordance >> create (Class <?> type , Method method ) {
85
+
86
+ String methodName = method .getName ();
87
+ ResolvableType outputType = ResolvableType .forMethodReturnType (method );
88
+ Collection <HttpMethod > requestMethods = DISCOVERER .getRequestMethod (type , method );
89
+ List <MediaType > inputMediaTypes = DISCOVERER .getConsumes (method );
90
+
55
91
MethodParameters parameters = MethodParameters .of (method );
56
92
57
93
ResolvableType inputType = parameters .getParametersWith (RequestBody .class ).stream () //
@@ -63,18 +99,66 @@ public static List<Affordance> create(Class<?> type, Method method, String href,
63
99
.map (QueryParameter ::of ) //
64
100
.collect (Collectors .toList ());
65
101
66
- ResolvableType outputType = ResolvableType .forMethodReturnType (method );
67
- Affordances affordances = Affordances .of (affordanceLink );
68
-
69
- return discoverer .getRequestMethod (type , method ).stream () //
102
+ return affordances -> requestMethods .stream () //
70
103
.flatMap (it -> affordances .afford (it ) //
71
104
.withInput (inputType ) //
72
105
.withOutput (outputType ) //
73
106
.withParameters (queryMethodParameters ) //
74
107
.withName (methodName ) //
75
- .withInputMediaTypes (discoverer . getConsumes ( method ) ) //
108
+ .withInputMediaTypes (inputMediaTypes ) //
76
109
.build () //
77
110
.stream ()) //
78
111
.collect (Collectors .toList ());
79
112
}
113
+
114
+ private static final class AffordanceKey {
115
+
116
+ private final Class <?> type ;
117
+ private final Method method ;
118
+
119
+ AffordanceKey (Class <?> type , Method method ) {
120
+
121
+ this .type = type ;
122
+ this .method = method ;
123
+ }
124
+
125
+ /*
126
+ * (non-Javadoc)
127
+ * @see java.lang.Object#equals(java.lang.Object)
128
+ */
129
+ @ Override
130
+ public boolean equals (@ Nullable Object o ) {
131
+
132
+ if (this == o ) {
133
+ return true ;
134
+ }
135
+
136
+ if (!(o instanceof AffordanceKey )) {
137
+ return false ;
138
+ }
139
+
140
+ AffordanceKey that = (AffordanceKey ) o ;
141
+
142
+ return Objects .equals (this .type , that .type ) //
143
+ && Objects .equals (this .method , that .method );
144
+ }
145
+
146
+ /*
147
+ * (non-Javadoc)
148
+ * @see java.lang.Object#hashCode()
149
+ */
150
+ @ Override
151
+ public int hashCode () {
152
+ return Objects .hash (this .type , this .method );
153
+ }
154
+
155
+ /*
156
+ * (non-Javadoc)
157
+ * @see java.lang.Object#toString()
158
+ */
159
+ @ Override
160
+ public String toString () {
161
+ return "WebHandler.AffordanceKey(type=" + this .type + ", method=" + this .method + ")" ;
162
+ }
163
+ }
80
164
}
0 commit comments