Skip to content

Commit 300f3cf

Browse files
schauderodrotbohm
authored andcommitted
DATACMNS-1158 - ProjectionFactory is now configurable by module implementations.
Introduced RepositoryFactorySupport.getProjectionFactory(…) to create a ProjectionFactory to be used for repository instances created. The default implementation uses the SpelAwareProxyProjectionFactory. The ProjectionInformation implementation is now a named class so it can be used for more specialized implementations. Original pull request: #243. Related issue: DATAJPA-1173.
1 parent 0237760 commit 300f3cf

File tree

3 files changed

+65
-38
lines changed

3 files changed

+65
-38
lines changed

src/main/java/org/springframework/data/projection/SpelAwareProxyProjectionFactory.java

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2016 the original author or authors.
2+
* Copyright 2015-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
3939
* @author Oliver Gierke
4040
* @author Thomas Darimont
4141
* @author Mark Paluch
42+
* @author Jens Schauder
4243
* @since 1.10
4344
*/
4445
public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory implements BeanFactoryAware {
@@ -63,29 +64,7 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
6364
*/
6465
@Override
6566
protected ProjectionInformation createProjectionInformation(Class<?> projectionType) {
66-
67-
return new DefaultProjectionInformation(projectionType) {
68-
69-
/*
70-
* (non-Javadoc)
71-
* @see org.springframework.data.projection.DefaultProjectionInformation#isInputProperty(java.beans.PropertyDescriptor)
72-
*/
73-
@Override
74-
protected boolean isInputProperty(PropertyDescriptor descriptor) {
75-
76-
if (!super.isInputProperty(descriptor)) {
77-
return false;
78-
}
79-
80-
Method readMethod = descriptor.getReadMethod();
81-
82-
if (readMethod == null) {
83-
return false;
84-
}
85-
86-
return AnnotationUtils.findAnnotation(readMethod, Value.class) == null;
87-
}
88-
};
67+
return new SpelAwareProjectionInformation(projectionType);
8968
}
9069

9170
/**
@@ -121,4 +100,31 @@ private static boolean hasMethodWithValueAnnotation(Class<?> type) {
121100

122101
return callback.hasFoundAnnotation();
123102
}
103+
104+
protected static class SpelAwareProjectionInformation extends DefaultProjectionInformation {
105+
106+
protected SpelAwareProjectionInformation(Class<?> projectionType) {
107+
super(projectionType);
108+
}
109+
110+
/*
111+
* (non-Javadoc)
112+
* @see org.springframework.data.projection.DefaultProjectionInformation#isInputProperty(java.beans.PropertyDescriptor)
113+
*/
114+
@Override
115+
protected boolean isInputProperty(PropertyDescriptor descriptor) {
116+
117+
if (!super.isInputProperty(descriptor)) {
118+
return false;
119+
}
120+
121+
Method readMethod = descriptor.getReadMethod();
122+
123+
if (readMethod == null) {
124+
return false;
125+
}
126+
127+
return AnnotationUtils.findAnnotation(readMethod, Value.class) == null;
128+
}
129+
}
124130
}

src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
import org.aopalliance.intercept.MethodInterceptor;
3636
import org.aopalliance.intercept.MethodInvocation;
37+
import org.jetbrains.annotations.NotNull;
3738
import org.springframework.aop.framework.ProxyFactory;
3839
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
3940
import org.springframework.beans.BeanUtils;
@@ -45,6 +46,7 @@
4546
import org.springframework.core.ResolvableType;
4647
import org.springframework.core.convert.TypeDescriptor;
4748
import org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor;
49+
import org.springframework.data.projection.ProjectionFactory;
4850
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
4951
import org.springframework.data.repository.Repository;
5052
import org.springframework.data.repository.core.EntityInformation;
@@ -77,6 +79,7 @@
7779
* @author Oliver Gierke
7880
* @author Mark Paluch
7981
* @author Christoph Strobl
82+
* @author Jens Schauder
8083
*/
8184
public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, BeanFactoryAware {
8285

@@ -312,14 +315,27 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
312315
postProcessors.forEach(processor -> processor.postProcess(result, information));
313316

314317
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
315-
result.addAdvice(new QueryExecutorMethodInterceptor(information));
318+
result.addAdvice(new QueryExecutorMethodInterceptor( //
319+
information, //
320+
getProjectionFactory(classLoader, beanFactory) //
321+
));
316322

317323
composition = composition.append(RepositoryFragment.implemented(target));
318324
result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
319325

320326
return (T) result.getProxy(classLoader);
321327
}
322328

329+
@NotNull
330+
protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFactory beanFactory) {
331+
332+
SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory();
333+
factory.setBeanClassLoader(classLoader);
334+
factory.setBeanFactory(beanFactory);
335+
336+
return factory;
337+
}
338+
323339
/**
324340
* Returns the {@link RepositoryMetadata} for the given repository interface.
325341
*
@@ -501,7 +517,8 @@ public class QueryExecutorMethodInterceptor implements MethodInterceptor {
501517
* Creates a new {@link QueryExecutorMethodInterceptor}. Builds a model of {@link QueryMethod}s to be invoked on
502518
* execution of repository interface methods.
503519
*/
504-
public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation) {
520+
public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation,
521+
ProjectionFactory projectionFactory) {
505522

506523
this.resultHandler = new QueryExecutionResultHandler();
507524

@@ -515,18 +532,20 @@ public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformatio
515532
+ "infrastructure apparently does not support query methods!");
516533
}
517534

518-
this.queries = lookupStrategy.map(it -> {
519-
520-
SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory();
521-
factory.setBeanClassLoader(classLoader);
522-
factory.setBeanFactory(beanFactory);
535+
this.queries = lookupStrategy.map( //
536+
it -> mapMethodsToQuery(repositoryInformation, projectionFactory, it) //
537+
).orElse(Collections.emptyMap());
538+
}
523539

524-
return repositoryInformation.getQueryMethods().stream()//
525-
.map(method -> Pair.of(method, it.resolveQuery(method, repositoryInformation, factory, namedQueries)))//
526-
.peek(pair -> invokeListeners(pair.getSecond()))//
527-
.collect(Pair.toMap());
540+
private Map<Method, RepositoryQuery> mapMethodsToQuery(RepositoryInformation repositoryInformation,
541+
ProjectionFactory projectionFactory, QueryLookupStrategy lookupStrategy) {
528542

529-
}).orElse(Collections.emptyMap());
543+
return repositoryInformation.getQueryMethods().stream() //
544+
.map(method -> Pair.of( //
545+
method, //
546+
lookupStrategy.resolveQuery(method, repositoryInformation, projectionFactory, namedQueries))) //
547+
.peek(pair -> invokeListeners(pair.getSecond())) //
548+
.collect(Pair.toMap());
530549
}
531550

532551
@SuppressWarnings({ "rawtypes", "unchecked" })

src/test/java/org/springframework/data/repository/core/support/QueryExecutorMethodInterceptorUnitTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.junit.runner.RunWith;
2424
import org.mockito.Mock;
2525
import org.mockito.junit.MockitoJUnitRunner;
26+
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
2627
import org.springframework.data.repository.core.RepositoryInformation;
2728
import org.springframework.data.repository.query.QueryLookupStrategy;
2829
import org.springframework.data.util.Streamable;
@@ -32,6 +33,7 @@
3233
*
3334
* @author Oliver Gierke
3435
* @author Mark Paluch
36+
* @author Jens Schauder
3537
*/
3638
@RunWith(MockitoJUnitRunner.class)
3739
public class QueryExecutorMethodInterceptorUnitTests {
@@ -46,7 +48,7 @@ public void rejectsRepositoryInterfaceWithQueryMethodsIfNoQueryLookupStrategyIsD
4648
when(information.hasQueryMethods()).thenReturn(true);
4749
when(factory.getQueryLookupStrategy(any(), any())).thenReturn(Optional.empty());
4850

49-
factory.new QueryExecutorMethodInterceptor(information);
51+
factory.new QueryExecutorMethodInterceptor(information, new SpelAwareProxyProjectionFactory());
5052
}
5153

5254
@Test
@@ -55,7 +57,7 @@ public void skipsQueryLookupsIfQueryLookupStrategyIsNotPresent() {
5557
when(information.getQueryMethods()).thenReturn(Streamable.empty());
5658
when(factory.getQueryLookupStrategy(any(), any())).thenReturn(Optional.of(strategy));
5759

58-
factory.new QueryExecutorMethodInterceptor(information);
60+
factory.new QueryExecutorMethodInterceptor(information, new SpelAwareProxyProjectionFactory());
5961

6062
verify(strategy, times(0)).resolveQuery(any(), any(), any(), any());
6163
}

0 commit comments

Comments
 (0)