Browse Source

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.
pull/262/head
Jens Schauder 8 years ago committed by Oliver Gierke
parent
commit
300f3cfd66
  1. 54
      src/main/java/org/springframework/data/projection/SpelAwareProxyProjectionFactory.java
  2. 43
      src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java
  3. 6
      src/test/java/org/springframework/data/repository/core/support/QueryExecutorMethodInterceptorUnitTests.java

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 the original author or authors.
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -39,6 +39,7 @@ import org.springframework.util.ReflectionUtils; @@ -39,6 +39,7 @@ import org.springframework.util.ReflectionUtils;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
* @author Jens Schauder
* @since 1.10
*/
public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory implements BeanFactoryAware {
@ -63,29 +64,7 @@ public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory impl @@ -63,29 +64,7 @@ public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory impl
*/
@Override
protected ProjectionInformation createProjectionInformation(Class<?> projectionType) {
return new DefaultProjectionInformation(projectionType) {
/*
* (non-Javadoc)
* @see org.springframework.data.projection.DefaultProjectionInformation#isInputProperty(java.beans.PropertyDescriptor)
*/
@Override
protected boolean isInputProperty(PropertyDescriptor descriptor) {
if (!super.isInputProperty(descriptor)) {
return false;
}
Method readMethod = descriptor.getReadMethod();
if (readMethod == null) {
return false;
}
return AnnotationUtils.findAnnotation(readMethod, Value.class) == null;
}
};
return new SpelAwareProjectionInformation(projectionType);
}
/**
@ -121,4 +100,31 @@ public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory impl @@ -121,4 +100,31 @@ public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory impl
return callback.hasFoundAnnotation();
}
protected static class SpelAwareProjectionInformation extends DefaultProjectionInformation {
protected SpelAwareProjectionInformation(Class<?> projectionType) {
super(projectionType);
}
/*
* (non-Javadoc)
* @see org.springframework.data.projection.DefaultProjectionInformation#isInputProperty(java.beans.PropertyDescriptor)
*/
@Override
protected boolean isInputProperty(PropertyDescriptor descriptor) {
if (!super.isInputProperty(descriptor)) {
return false;
}
Method readMethod = descriptor.getReadMethod();
if (readMethod == null) {
return false;
}
return AnnotationUtils.findAnnotation(readMethod, Value.class) == null;
}
}
}

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

@ -34,6 +34,7 @@ import java.util.stream.Collectors; @@ -34,6 +34,7 @@ import java.util.stream.Collectors;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.beans.BeanUtils;
@ -45,6 +46,7 @@ import org.springframework.core.MethodParameter; @@ -45,6 +46,7 @@ import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.EntityInformation;
@ -77,6 +79,7 @@ import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType; @@ -77,6 +79,7 @@ import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
* @author Oliver Gierke
* @author Mark Paluch
* @author Christoph Strobl
* @author Jens Schauder
*/
public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, BeanFactoryAware {
@ -312,7 +315,10 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, @@ -312,7 +315,10 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
postProcessors.forEach(processor -> processor.postProcess(result, information));
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
result.addAdvice(new QueryExecutorMethodInterceptor(information));
result.addAdvice(new QueryExecutorMethodInterceptor( //
information, //
getProjectionFactory(classLoader, beanFactory) //
));
composition = composition.append(RepositoryFragment.implemented(target));
result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
@ -320,6 +326,16 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, @@ -320,6 +326,16 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
return (T) result.getProxy(classLoader);
}
@NotNull
protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFactory beanFactory) {
SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory();
factory.setBeanClassLoader(classLoader);
factory.setBeanFactory(beanFactory);
return factory;
}
/**
* Returns the {@link RepositoryMetadata} for the given repository interface.
*
@ -501,7 +517,8 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, @@ -501,7 +517,8 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* Creates a new {@link QueryExecutorMethodInterceptor}. Builds a model of {@link QueryMethod}s to be invoked on
* execution of repository interface methods.
*/
public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation) {
public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation,
ProjectionFactory projectionFactory) {
this.resultHandler = new QueryExecutionResultHandler();
@ -515,18 +532,20 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, @@ -515,18 +532,20 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
+ "infrastructure apparently does not support query methods!");
}
this.queries = lookupStrategy.map(it -> {
SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory();
factory.setBeanClassLoader(classLoader);
factory.setBeanFactory(beanFactory);
this.queries = lookupStrategy.map( //
it -> mapMethodsToQuery(repositoryInformation, projectionFactory, it) //
).orElse(Collections.emptyMap());
}
return repositoryInformation.getQueryMethods().stream()//
.map(method -> Pair.of(method, it.resolveQuery(method, repositoryInformation, factory, namedQueries)))//
.peek(pair -> invokeListeners(pair.getSecond()))//
.collect(Pair.toMap());
private Map<Method, RepositoryQuery> mapMethodsToQuery(RepositoryInformation repositoryInformation,
ProjectionFactory projectionFactory, QueryLookupStrategy lookupStrategy) {
}).orElse(Collections.emptyMap());
return repositoryInformation.getQueryMethods().stream() //
.map(method -> Pair.of( //
method, //
lookupStrategy.resolveQuery(method, repositoryInformation, projectionFactory, namedQueries))) //
.peek(pair -> invokeListeners(pair.getSecond())) //
.collect(Pair.toMap());
}
@SuppressWarnings({ "rawtypes", "unchecked" })

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

@ -23,6 +23,7 @@ import org.junit.Test; @@ -23,6 +23,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.util.Streamable;
@ -32,6 +33,7 @@ import org.springframework.data.util.Streamable; @@ -32,6 +33,7 @@ import org.springframework.data.util.Streamable;
*
* @author Oliver Gierke
* @author Mark Paluch
* @author Jens Schauder
*/
@RunWith(MockitoJUnitRunner.class)
public class QueryExecutorMethodInterceptorUnitTests {
@ -46,7 +48,7 @@ public class QueryExecutorMethodInterceptorUnitTests { @@ -46,7 +48,7 @@ public class QueryExecutorMethodInterceptorUnitTests {
when(information.hasQueryMethods()).thenReturn(true);
when(factory.getQueryLookupStrategy(any(), any())).thenReturn(Optional.empty());
factory.new QueryExecutorMethodInterceptor(information);
factory.new QueryExecutorMethodInterceptor(information, new SpelAwareProxyProjectionFactory());
}
@Test
@ -55,7 +57,7 @@ public class QueryExecutorMethodInterceptorUnitTests { @@ -55,7 +57,7 @@ public class QueryExecutorMethodInterceptorUnitTests {
when(information.getQueryMethods()).thenReturn(Streamable.empty());
when(factory.getQueryLookupStrategy(any(), any())).thenReturn(Optional.of(strategy));
factory.new QueryExecutorMethodInterceptor(information);
factory.new QueryExecutorMethodInterceptor(information, new SpelAwareProxyProjectionFactory());
verify(strategy, times(0)).resolveQuery(any(), any(), any(), any());
}

Loading…
Cancel
Save