1
16 package org.springframework.data.repository.core.support;
17
18 import static org.springframework.data.repository.util.ClassUtils.*;
19 import static org.springframework.util.ReflectionUtils.*;
20
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Modifier;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.concurrent.ConcurrentHashMap;
28
29 import org.springframework.core.annotation.AnnotationUtils;
30 import org.springframework.data.annotation.QueryAnnotation;
31 import org.springframework.data.repository.core.CrudMethods;
32 import org.springframework.data.repository.core.RepositoryInformation;
33 import org.springframework.data.repository.core.RepositoryMetadata;
34 import org.springframework.data.util.Streamable;
35 import org.springframework.data.util.TypeInformation;
36 import org.springframework.util.Assert;
37 import org.springframework.util.ClassUtils;
38
39
47 class DefaultRepositoryInformation implements RepositoryInformation {
48
49 private final Map<Method, Method> methodCache = new ConcurrentHashMap<>();
50
51 private final RepositoryMetadata metadata;
52 private final Class<?> repositoryBaseClass;
53 private final RepositoryComposition composition;
54 private final RepositoryComposition baseComposition;
55
56
63 public DefaultRepositoryInformation(RepositoryMetadata metadata, Class<?> repositoryBaseClass,
64 RepositoryComposition composition) {
65
66 Assert.notNull(metadata, "Repository metadata must not be null!");
67 Assert.notNull(repositoryBaseClass, "Repository base class must not be null!");
68 Assert.notNull(composition, "Repository composition must not be null!");
69
70 this.metadata = metadata;
71 this.repositoryBaseClass = repositoryBaseClass;
72 this.composition = composition;
73 this.baseComposition = RepositoryComposition.of(RepositoryFragment.structural(repositoryBaseClass))
74 .withArgumentConverter(composition.getArgumentConverter())
75 .withMethodLookup(composition.getMethodLookup());
76 }
77
78
82 @Override
83 public Class<?> getDomainType() {
84 return metadata.getDomainType();
85 }
86
87
91 @Override
92 public Class<?> getIdType() {
93 return metadata.getIdType();
94 }
95
96
100 @Override
101 public Class<?> getRepositoryBaseClass() {
102 return this.repositoryBaseClass;
103 }
104
105
109 @Override
110 public Method getTargetClassMethod(Method method) {
111
112 if (methodCache.containsKey(method)) {
113 return methodCache.get(method);
114 }
115
116 Method result = composition.findMethod(method).orElse(method);
117
118 if (!result.equals(method)) {
119 return cacheAndReturn(method, result);
120 }
121
122 return cacheAndReturn(method, baseComposition.findMethod(method).orElse(method));
123 }
124
125 private Method cacheAndReturn(Method key, Method value) {
126
127 if (value != null) {
128 makeAccessible(value);
129 }
130
131 methodCache.put(key, value);
132 return value;
133 }
134
135
139 @Override
140 public Streamable<Method> getQueryMethods() {
141
142 Set<Method> result = new HashSet<>();
143
144 for (Method method : getRepositoryInterface().getMethods()) {
145 method = ClassUtils.getMostSpecificMethod(method, getRepositoryInterface());
146 if (isQueryMethodCandidate(method)) {
147 result.add(method);
148 }
149 }
150
151 return Streamable.of(Collections.unmodifiableSet(result));
152 }
153
154
160 private boolean isQueryMethodCandidate(Method method) {
161 return !method.isBridge() && !method.isDefault()
162 && !Modifier.isStatic(method.getModifiers())
163 && (isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method));
164 }
165
166
173 private boolean isQueryAnnotationPresentOn(Method method) {
174
175 return AnnotationUtils.findAnnotation(method, QueryAnnotation.class) != null;
176 }
177
178
182 @Override
183 public boolean isCustomMethod(Method method) {
184 return composition.getMethod(method) != null;
185 }
186
187
191 @Override
192 public boolean isQueryMethod(Method method) {
193 return getQueryMethods().stream().anyMatch(it -> it.equals(method));
194 }
195
196
200 @Override
201 public boolean isBaseClassMethod(Method method) {
202
203 Assert.notNull(method, "Method must not be null!");
204 return baseComposition.getMethod(method) != null;
205 }
206
207
211 @Override
212 public boolean hasCustomMethod() {
213
214 Class<?> repositoryInterface = getRepositoryInterface();
215
216
217 if (isGenericRepositoryInterface(repositoryInterface)) {
218 return false;
219 }
220
221 for (Method method : repositoryInterface.getMethods()) {
222 if (isCustomMethod(method) && !isBaseClassMethod(method)) {
223 return true;
224 }
225 }
226
227 return false;
228 }
229
230
234 @Override
235 public Class<?> getRepositoryInterface() {
236 return metadata.getRepositoryInterface();
237 }
238
239
243 @Override
244 public Class<?> getReturnedDomainClass(Method method) {
245 return metadata.getReturnedDomainClass(method);
246 }
247
248
252 @Override
253 public TypeInformation<?> getReturnType(Method method) {
254 return metadata.getReturnType(method);
255 }
256
257
261 @Override
262 public CrudMethods getCrudMethods() {
263 return metadata.getCrudMethods();
264 }
265
266
270 @Override
271 public boolean isPagingRepository() {
272 return metadata.isPagingRepository();
273 }
274
275
279 @Override
280 public Set<Class<?>> getAlternativeDomainTypes() {
281 return metadata.getAlternativeDomainTypes();
282 }
283
284
288 @Override
289 public boolean isReactiveRepository() {
290 return metadata.isReactiveRepository();
291 }
292 }
293