-
Notifications
You must be signed in to change notification settings - Fork 678
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DATACMNS-1785 - Add ReactiveQuerydslPredicateArgumentResolver and Rea…
…ctiveQuerydslWebConfiguration. We now provide ReactiveQuerydslPredicateArgumentResolver to resolve Querydsl Predicates when using Spring WebFlux. Related ticket: #2200. Original pull request: #2274.
- Loading branch information
Showing
4 changed files
with
314 additions
and
93 deletions.
There are no files selected for viewing
78 changes: 78 additions & 0 deletions
78
src/main/java/org/springframework/data/web/config/ReactiveQuerydslWebConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* Copyright 2015-2021 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. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.data.web.config; | ||
|
||
import java.util.Optional; | ||
|
||
import org.springframework.beans.factory.BeanFactory; | ||
import org.springframework.beans.factory.ObjectFactory; | ||
import org.springframework.beans.factory.ObjectProvider; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Lazy; | ||
import org.springframework.core.convert.ConversionService; | ||
import org.springframework.data.querydsl.EntityPathResolver; | ||
import org.springframework.data.querydsl.SimpleEntityPathResolver; | ||
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory; | ||
import org.springframework.data.web.querydsl.ReactiveQuerydslPredicateArgumentResolver; | ||
import org.springframework.web.reactive.config.WebFluxConfigurer; | ||
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; | ||
|
||
/** | ||
* Querydsl-specific web configuration for Spring Data. Registers a {@link HandlerMethodArgumentResolver} that builds up | ||
* {@link Predicate}s from web requests. | ||
* | ||
* @author Matías Hermosilla | ||
* @since 1.11 | ||
* @soundtrack Anika Nilles - Alter Ego | ||
*/ | ||
@Configuration(proxyBeanMethods = false) | ||
public class ReactiveQuerydslWebConfiguration implements WebFluxConfigurer { | ||
|
||
@Autowired | ||
@Qualifier("webFluxConversionService") ObjectFactory<ConversionService> conversionService; | ||
@Autowired ObjectProvider<EntityPathResolver> resolver; | ||
@Autowired BeanFactory beanFactory; | ||
|
||
/** | ||
* Default {@link ReactiveQuerydslPredicateArgumentResolver} to create Querydsl {@link Predicate} instances for | ||
* Spring WebFlux controller methods. | ||
* | ||
* @return | ||
*/ | ||
@Lazy | ||
@Bean | ||
public ReactiveQuerydslPredicateArgumentResolver querydslPredicateArgumentResolver() { | ||
return new ReactiveQuerydslPredicateArgumentResolver( | ||
beanFactory.getBean("querydslBindingsFactory", QuerydslBindingsFactory.class), | ||
Optional.of(conversionService.getObject())); | ||
} | ||
|
||
@Lazy | ||
@Bean | ||
public QuerydslBindingsFactory querydslBindingsFactory() { | ||
return new QuerydslBindingsFactory(resolver.getIfUnique(() -> SimpleEntityPathResolver.INSTANCE)); | ||
} | ||
|
||
@Override | ||
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { | ||
configurer.addCustomResolver(beanFactory.getBean("querydslPredicateArgumentResolver", | ||
ReactiveQuerydslPredicateArgumentResolver.class)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
138 changes: 138 additions & 0 deletions
138
.../java/org/springframework/data/web/querydsl/QuerydslPredicateArgumentResolverSupport.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
/* | ||
* Copyright 2015-2021 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. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.data.web.querydsl; | ||
|
||
import java.lang.reflect.Method; | ||
import java.util.Optional; | ||
|
||
import org.springframework.core.MethodParameter; | ||
import org.springframework.core.ResolvableType; | ||
import org.springframework.core.convert.ConversionService; | ||
import org.springframework.core.convert.support.DefaultConversionService; | ||
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory; | ||
import org.springframework.data.querydsl.binding.QuerydslPredicate; | ||
import org.springframework.data.querydsl.binding.QuerydslPredicateBuilder; | ||
import org.springframework.data.util.ClassTypeInformation; | ||
import org.springframework.data.util.TypeInformation; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
|
||
import com.querydsl.core.types.Predicate; | ||
|
||
/** | ||
* {@link HandlerMethodArgumentResolver} to allow injection of {@link com.querydsl.core.types.Predicate} into Spring MVC | ||
* controller methods. | ||
* | ||
* @author Christoph Strobl | ||
* @author Oliver Gierke | ||
* @author Matías Hermosilla | ||
* @since 1.11 | ||
*/ | ||
public abstract class QuerydslPredicateArgumentResolverSupport { | ||
|
||
private static final ResolvableType PREDICATE = ResolvableType.forClass(Predicate.class); | ||
protected static final ResolvableType OPTIONAL_OF_PREDICATE = ResolvableType.forClassWithGenerics(Optional.class, | ||
PREDICATE); | ||
|
||
protected final QuerydslBindingsFactory bindingsFactory; | ||
protected final QuerydslPredicateBuilder predicateBuilder; | ||
|
||
/** | ||
* Creates a new {@link QuerydslPredicateArgumentResolver} using the given {@link ConversionService}. | ||
* | ||
* @param factory | ||
* @param conversionService defaults to {@link DefaultConversionService} if {@literal null}. | ||
*/ | ||
public QuerydslPredicateArgumentResolverSupport(QuerydslBindingsFactory factory, | ||
Optional<ConversionService> conversionService) { | ||
|
||
this.bindingsFactory = factory; | ||
this.predicateBuilder = new QuerydslPredicateBuilder(conversionService.orElseGet(DefaultConversionService::new), | ||
factory.getEntityPathResolver()); | ||
} | ||
|
||
/* | ||
* (non-Javadoc) | ||
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter) | ||
*/ | ||
public boolean supportsParameter(MethodParameter parameter) { | ||
|
||
ResolvableType type = ResolvableType.forMethodParameter(parameter); | ||
|
||
if (PREDICATE.isAssignableFrom(type) || OPTIONAL_OF_PREDICATE.isAssignableFrom(type)) { | ||
return true; | ||
} | ||
|
||
if (parameter.hasParameterAnnotation(QuerydslPredicate.class)) { | ||
throw new IllegalArgumentException( | ||
String.format("Parameter at position %s must be of type Predicate but was %s.", | ||
parameter.getParameterIndex(), parameter.getParameterType())); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Obtains the domain type information from the given method parameter. Will favor an explicitly registered on | ||
* through {@link QuerydslPredicate#root()} but use the actual type of the method's return type as fallback. | ||
* | ||
* @param parameter must not be {@literal null}. | ||
* @return | ||
*/ | ||
protected static TypeInformation<?> extractTypeInfo(MethodParameter parameter) { | ||
|
||
Optional<QuerydslPredicate> annotation = Optional | ||
.ofNullable(parameter.getParameterAnnotation(QuerydslPredicate.class)); | ||
|
||
return annotation.filter(it -> !Object.class.equals(it.root()))// | ||
.<TypeInformation<?>> map(it -> ClassTypeInformation.from(it.root()))// | ||
.orElseGet(() -> detectDomainType(parameter)); | ||
} | ||
|
||
private static TypeInformation<?> detectDomainType(MethodParameter parameter) { | ||
|
||
Method method = parameter.getMethod(); | ||
|
||
if (method == null) { | ||
throw new IllegalArgumentException("Method parameter is not backed by a method!"); | ||
} | ||
|
||
return detectDomainType(ClassTypeInformation.fromReturnTypeOf(method)); | ||
} | ||
|
||
private static TypeInformation<?> detectDomainType(TypeInformation<?> source) { | ||
|
||
if (source.getTypeArguments().isEmpty()) { | ||
return source; | ||
} | ||
|
||
TypeInformation<?> actualType = source.getActualType(); | ||
|
||
if (actualType == null) { | ||
throw new IllegalArgumentException(String.format("Could not determine domain type from %s!", source)); | ||
} | ||
|
||
if (source != actualType) { | ||
return detectDomainType(actualType); | ||
} | ||
|
||
if (source instanceof Iterable) { | ||
return source; | ||
} | ||
|
||
return detectDomainType(source.getRequiredComponentType()); | ||
} | ||
|
||
} |
Oops, something went wrong.