38
38
import org .springframework .beans .factory .BeanFactory ;
39
39
import org .springframework .beans .factory .BeanFactoryAware ;
40
40
import org .springframework .beans .factory .NoSuchBeanDefinitionException ;
41
+ import org .springframework .context .EnvironmentAware ;
41
42
import org .springframework .core .convert .support .DefaultConversionService ;
42
43
import org .springframework .core .convert .support .GenericConversionService ;
44
+ import org .springframework .core .env .Environment ;
45
+ import org .springframework .core .env .EnvironmentCapable ;
46
+ import org .springframework .core .env .StandardEnvironment ;
43
47
import org .springframework .core .log .LogMessage ;
44
48
import org .springframework .core .metrics .ApplicationStartup ;
45
49
import org .springframework .core .metrics .StartupStep ;
50
+ import org .springframework .data .expression .ValueExpressionParser ;
46
51
import org .springframework .data .projection .DefaultMethodInvokingMethodInterceptor ;
47
52
import org .springframework .data .projection .ProjectionFactory ;
48
53
import org .springframework .data .projection .SpelAwareProxyProjectionFactory ;
58
63
import org .springframework .data .repository .query .QueryLookupStrategy .Key ;
59
64
import org .springframework .data .repository .query .QueryMethod ;
60
65
import org .springframework .data .repository .query .QueryMethodEvaluationContextProvider ;
66
+ import org .springframework .data .repository .query .QueryMethodValueEvaluationContextProviderFactory ;
61
67
import org .springframework .data .repository .query .RepositoryQuery ;
68
+ import org .springframework .data .repository .query .ValueExpressionSupportHolder ;
62
69
import org .springframework .data .repository .util .QueryExecutionConverters ;
63
70
import org .springframework .data .util .Lazy ;
64
71
import org .springframework .data .util .ReflectionUtils ;
72
+ import org .springframework .expression .ExpressionParser ;
73
+ import org .springframework .expression .spel .standard .SpelExpressionParser ;
65
74
import org .springframework .lang .Nullable ;
66
75
import org .springframework .transaction .interceptor .TransactionalProxy ;
67
76
import org .springframework .util .Assert ;
80
89
* @author John Blum
81
90
* @author Johannes Englmeier
82
91
*/
83
- public abstract class RepositoryFactorySupport implements BeanClassLoaderAware , BeanFactoryAware {
92
+ public abstract class RepositoryFactorySupport
93
+ implements BeanClassLoaderAware , BeanFactoryAware , EnvironmentAware , EnvironmentCapable {
84
94
85
95
static final GenericConversionService CONVERSION_SERVICE = new DefaultConversionService ();
96
+ private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser ();
97
+ private static final ValueExpressionParser VALUE_PARSER = ValueExpressionParser .create (() -> EXPRESSION_PARSER );
98
+
86
99
private static final Log logger = LogFactory .getLog (RepositoryFactorySupport .class );
87
100
88
101
static {
@@ -93,15 +106,16 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
93
106
private final Map <RepositoryInformationCacheKey , RepositoryInformation > repositoryInformationCache ;
94
107
private final List <RepositoryProxyPostProcessor > postProcessors ;
95
108
96
- private Optional < Class <?> > repositoryBaseClass ;
109
+ private @ Nullable Class <?> repositoryBaseClass ;
97
110
private boolean exposeMetadata ;
98
111
private @ Nullable QueryLookupStrategy .Key queryLookupStrategyKey ;
99
- private List <QueryCreationListener <?>> queryPostProcessors ;
100
- private List <RepositoryMethodInvocationListener > methodInvocationListeners ;
112
+ private final List <QueryCreationListener <?>> queryPostProcessors ;
113
+ private final List <RepositoryMethodInvocationListener > methodInvocationListeners ;
101
114
private NamedQueries namedQueries ;
102
115
private ClassLoader classLoader ;
103
116
private QueryMethodEvaluationContextProvider evaluationContextProvider ;
104
117
private BeanFactory beanFactory ;
118
+ private Environment environment ;
105
119
private Lazy <ProjectionFactory > projectionFactory ;
106
120
107
121
private final QueryCollectingQueryCreationListener collectingListener = new QueryCollectingQueryCreationListener ();
@@ -112,7 +126,6 @@ public RepositoryFactorySupport() {
112
126
this .repositoryInformationCache = new HashMap <>(16 );
113
127
this .postProcessors = new ArrayList <>();
114
128
115
- this .repositoryBaseClass = Optional .empty ();
116
129
this .namedQueries = PropertiesBasedNamedQueries .EMPTY ;
117
130
this .classLoader = org .springframework .util .ClassUtils .getDefaultClassLoader ();
118
131
this .evaluationContextProvider = QueryMethodEvaluationContextProvider .DEFAULT ;
@@ -143,7 +156,7 @@ public void setExposeMetadata(boolean exposeMetadata) {
143
156
}
144
157
145
158
/**
146
- * Sets the strategy of how to lookup a query to execute finders.
159
+ * Sets the strategy of how to look up a query to execute finders.
147
160
*
148
161
* @param key
149
162
*/
@@ -156,12 +169,12 @@ public void setQueryLookupStrategyKey(Key key) {
156
169
*
157
170
* @param namedQueries the namedQueries to set
158
171
*/
159
- public void setNamedQueries (NamedQueries namedQueries ) {
172
+ public void setNamedQueries (@ Nullable NamedQueries namedQueries ) {
160
173
this .namedQueries = namedQueries == null ? PropertiesBasedNamedQueries .EMPTY : namedQueries ;
161
174
}
162
175
163
176
@ Override
164
- public void setBeanClassLoader (ClassLoader classLoader ) {
177
+ public void setBeanClassLoader (@ Nullable ClassLoader classLoader ) {
165
178
this .classLoader = classLoader == null ? org .springframework .util .ClassUtils .getDefaultClassLoader () : classLoader ;
166
179
this .projectionFactory = createProjectionFactory ();
167
180
}
@@ -172,14 +185,29 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
172
185
this .projectionFactory = createProjectionFactory ();
173
186
}
174
187
188
+ @ Override
189
+ public void setEnvironment (Environment environment ) {
190
+ this .environment = environment ;
191
+ }
192
+
193
+ @ Override
194
+ public Environment getEnvironment () {
195
+
196
+ if (this .environment == null ) {
197
+ this .environment = new StandardEnvironment ();
198
+ }
199
+
200
+ return this .environment ;
201
+ }
202
+
175
203
/**
176
204
* Sets the {@link QueryMethodEvaluationContextProvider} to be used to evaluate SpEL expressions in manually defined
177
205
* queries.
178
206
*
179
207
* @param evaluationContextProvider can be {@literal null}, defaults to
180
208
* {@link QueryMethodEvaluationContextProvider#DEFAULT}.
181
209
*/
182
- public void setEvaluationContextProvider (QueryMethodEvaluationContextProvider evaluationContextProvider ) {
210
+ public void setEvaluationContextProvider (@ Nullable QueryMethodEvaluationContextProvider evaluationContextProvider ) {
183
211
this .evaluationContextProvider = evaluationContextProvider == null ? QueryMethodEvaluationContextProvider .DEFAULT
184
212
: evaluationContextProvider ;
185
213
}
@@ -191,15 +219,15 @@ public void setEvaluationContextProvider(QueryMethodEvaluationContextProvider ev
191
219
* @param repositoryBaseClass the repository base class to back the repository proxy, can be {@literal null}.
192
220
* @since 1.11
193
221
*/
194
- public void setRepositoryBaseClass (Class <?> repositoryBaseClass ) {
195
- this .repositoryBaseClass = Optional . ofNullable ( repositoryBaseClass ) ;
222
+ public void setRepositoryBaseClass (@ Nullable Class <?> repositoryBaseClass ) {
223
+ this .repositoryBaseClass = repositoryBaseClass ;
196
224
}
197
225
198
226
/**
199
227
* Adds a {@link QueryCreationListener} to the factory to plug in functionality triggered right after creation of
200
228
* {@link RepositoryQuery} instances.
201
229
*
202
- * @param listener
230
+ * @param listener the listener to add.
203
231
*/
204
232
public void addQueryCreationListener (QueryCreationListener <?> listener ) {
205
233
@@ -211,7 +239,7 @@ public void addQueryCreationListener(QueryCreationListener<?> listener) {
211
239
* Adds a {@link RepositoryMethodInvocationListener} to the factory to plug in functionality triggered right after
212
240
* running {@link RepositoryQuery query methods} and {@link Method fragment methods}.
213
241
*
214
- * @param listener
242
+ * @param listener the listener to add.
215
243
* @since 2.4
216
244
*/
217
245
public void addInvocationListener (RepositoryMethodInvocationListener listener ) {
@@ -225,7 +253,7 @@ public void addInvocationListener(RepositoryMethodInvocationListener listener) {
225
253
* the proxy gets created. Note that the {@link QueryExecutorMethodInterceptor} will be added to the proxy
226
254
* <em>after</em> the {@link RepositoryProxyPostProcessor}s are considered.
227
255
*
228
- * @param processor
256
+ * @param processor the post-processor to add.
229
257
*/
230
258
public void addRepositoryProxyPostProcessor (RepositoryProxyPostProcessor processor ) {
231
259
@@ -236,8 +264,8 @@ public void addRepositoryProxyPostProcessor(RepositoryProxyPostProcessor process
236
264
/**
237
265
* Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add repository-specific extensions.
238
266
*
239
- * @param metadata
240
- * @return
267
+ * @param metadata the repository metadata to use.
268
+ * @return fragment composition.
241
269
*/
242
270
protected RepositoryFragments getRepositoryFragments (RepositoryMetadata metadata ) {
243
271
return RepositoryFragments .empty ();
@@ -246,8 +274,8 @@ protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata
246
274
/**
247
275
* Creates {@link RepositoryComposition} based on {@link RepositoryMetadata} for repository-specific method handling.
248
276
*
249
- * @param metadata
250
- * @return
277
+ * @param metadata the repository metadata to use.
278
+ * @return repository composition.
251
279
*/
252
280
private RepositoryComposition getRepositoryComposition (RepositoryMetadata metadata ) {
253
281
return RepositoryComposition .fromMetadata (metadata );
@@ -257,7 +285,7 @@ private RepositoryComposition getRepositoryComposition(RepositoryMetadata metada
257
285
* Returns a repository instance for the given interface.
258
286
*
259
287
* @param repositoryInterface must not be {@literal null}.
260
- * @return
288
+ * @return the implemented repository interface.
261
289
*/
262
290
public <T > T getRepository (Class <T > repositoryInterface ) {
263
291
return getRepository (repositoryInterface , RepositoryFragments .empty ());
@@ -269,7 +297,7 @@ public <T> T getRepository(Class<T> repositoryInterface) {
269
297
*
270
298
* @param repositoryInterface must not be {@literal null}.
271
299
* @param customImplementation must not be {@literal null}.
272
- * @return
300
+ * @return the implemented repository interface.
273
301
*/
274
302
public <T > T getRepository (Class <T > repositoryInterface , Object customImplementation ) {
275
303
return getRepository (repositoryInterface , RepositoryFragments .just (customImplementation ));
@@ -281,7 +309,7 @@ public <T> T getRepository(Class<T> repositoryInterface, Object customImplementa
281
309
*
282
310
* @param repositoryInterface must not be {@literal null}.
283
311
* @param fragments must not be {@literal null}.
284
- * @return
312
+ * @return the implemented repository interface.
285
313
* @since 2.0
286
314
*/
287
315
@ SuppressWarnings ({ "unchecked" })
@@ -298,7 +326,9 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
298
326
299
327
StartupStep repositoryInit = onEvent (applicationStartup , "spring.data.repository.init" , repositoryInterface );
300
328
301
- repositoryBaseClass .ifPresent (it -> repositoryInit .tag ("baseClass" , it .getName ()));
329
+ if (repositoryBaseClass != null ) {
330
+ repositoryInit .tag ("baseClass" , repositoryBaseClass .getName ());
331
+ }
302
332
303
333
StartupStep repositoryMetadataStep = onEvent (applicationStartup , "spring.data.repository.metadata" ,
304
334
repositoryInterface );
@@ -384,7 +414,9 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
384
414
}
385
415
386
416
Optional <QueryLookupStrategy > queryLookupStrategy = getQueryLookupStrategy (queryLookupStrategyKey ,
387
- evaluationContextProvider );
417
+ new ValueExpressionSupportHolder (
418
+ new QueryMethodValueEvaluationContextProviderFactory (getEnvironment (), evaluationContextProvider ),
419
+ VALUE_PARSER ));
388
420
result .addAdvice (new QueryExecutorMethodInterceptor (information , getProjectionFactory (), queryLookupStrategy ,
389
421
namedQueries , queryPostProcessors , methodInvocationListeners ));
390
422
@@ -412,7 +444,7 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
412
444
*/
413
445
protected ProjectionFactory getProjectionFactory (ClassLoader classLoader , BeanFactory beanFactory ) {
414
446
415
- SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory ();
447
+ SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory (EXPRESSION_PARSER );
416
448
factory .setBeanClassLoader (classLoader );
417
449
factory .setBeanFactory (beanFactory );
418
450
@@ -476,7 +508,7 @@ private RepositoryInformation getRepositoryInformation(RepositoryMetadata metada
476
508
477
509
return repositoryInformationCache .computeIfAbsent (cacheKey , key -> {
478
510
479
- Class <?> baseClass = repositoryBaseClass . orElse ( getRepositoryBaseClass (metadata ) );
511
+ Class <?> baseClass = repositoryBaseClass != null ? repositoryBaseClass : getRepositoryBaseClass (metadata );
480
512
481
513
return new DefaultRepositoryInformation (metadata , baseClass , composition );
482
514
});
@@ -537,6 +569,25 @@ protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key
537
569
return Optional .empty ();
538
570
}
539
571
572
+ /**
573
+ * Returns the {@link QueryLookupStrategy} for the given {@link Key} and {@link ValueExpressionSupportHolder}. Favor
574
+ * implementing this method over {@link #getQueryLookupStrategy(Key, QueryMethodEvaluationContextProvider)} for
575
+ * extended {@link org.springframework.data.expression.ValueExpression} support.
576
+ * <p>
577
+ * This method delegates to {@link #getQueryLookupStrategy(Key, QueryMethodEvaluationContextProvider)} unless
578
+ * overridden.
579
+ * </p>
580
+ *
581
+ * @param key can be {@literal null}.
582
+ * @param expressionSupport will never be {@literal null}.
583
+ * @return the {@link QueryLookupStrategy} to use or {@literal null} if no queries should be looked up.
584
+ * @since 3.3
585
+ */
586
+ protected Optional <QueryLookupStrategy > getQueryLookupStrategy (@ Nullable Key key ,
587
+ ValueExpressionSupportHolder expressionSupport ) {
588
+ return getQueryLookupStrategy (key , evaluationContextProvider );
589
+ }
590
+
540
591
/**
541
592
* Validates the given repository interface as well as the given custom implementation.
542
593
*
0 commit comments