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.
This commit is contained in:
Jens Schauder
2017-09-15 06:45:22 +02:00
committed by Oliver Gierke
parent 0237760345
commit 300f3cfd66
3 changed files with 65 additions and 38 deletions

View File

@@ -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;
* @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
*/
@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
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;
}
}
}

View File

@@ -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;
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;
* @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,
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,
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,
* 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,
+ "infrastructure apparently does not support query methods!");
}
this.queries = lookupStrategy.map(it -> {
this.queries = lookupStrategy.map( //
it -> mapMethodsToQuery(repositoryInformation, projectionFactory, it) //
).orElse(Collections.emptyMap());
}
SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory();
factory.setBeanClassLoader(classLoader);
factory.setBeanFactory(beanFactory);
private Map<Method, RepositoryQuery> mapMethodsToQuery(RepositoryInformation repositoryInformation,
ProjectionFactory projectionFactory, QueryLookupStrategy lookupStrategy) {
return repositoryInformation.getQueryMethods().stream()//
.map(method -> Pair.of(method, it.resolveQuery(method, repositoryInformation, factory, namedQueries)))//
.peek(pair -> invokeListeners(pair.getSecond()))//
.collect(Pair.toMap());
}).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" })