基于Spring Data Rest的Spring Boot动态查询实现困境
解决Spring Boot 1.5.9 + Spring Data Rest动态SQL过滤的依赖注入问题
你遇到的核心问题其实是Spring Data Rest的Repository代理机制和Request Scope Bean注入的冲突——Spring Data会为你的Repository接口生成动态代理对象,你自定义的抽象实现类和默认方法没法直接拿到Spring容器里的Request Scope Bean。下面是一步步的解决方案:
问题根源拆解
- Spring Data Rest生成的Repository代理不会继承你写的
SampleRepositoryComponents的Autowired属性,因为代理是基于接口动态生成的,和你的抽象Component类没有关联; - Repository接口的默认方法不属于Spring的Bean方法生命周期范畴,没法直接注入或访问Spring容器中的Bean。
解决方案步骤
1. 正确配置Request Scope的QueryParam Bean
首先确保QueryParam被正确注册为Request Scope的Bean,并且启用代理模式,这样在单例Bean中注入它时能正常工作:
@Component @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public class QueryParam { private String query_string; private Map<String, String[]> parameter_map; // Getters & Setters public String getQuery_string() { return query_string; } public void setQuery_string(String query_string) { this.query_string = query_string; } public Map<String, String[]> getParameter_map() { return parameter_map; } public void setParameter_map(Map<String, String[]> parameter_map) { this.parameter_map = parameter_map; } }
2. 实现拦截器,把请求参数存入QueryParam
修改你的拦截器,直接注入QueryParam并设置请求参数:
@Component public class QueryParamInterceptor extends HandlerInterceptorAdapter { private final QueryParam queryParam; @Autowired public QueryParamInterceptor(QueryParam queryParam) { this.queryParam = queryParam; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { queryParam.setQuery_string(request.getQueryString()); queryParam.setParameter_map(request.getParameterMap()); return true; } }
别忘了在Web配置中注册拦截器:
@Configuration public class WebConfig extends WebMvcConfigurerAdapter { private final QueryParamInterceptor queryParamInterceptor; @Autowired public WebConfig(QueryParamInterceptor queryParamInterceptor) { this.queryParamInterceptor = queryParamInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(queryParamInterceptor) .addPathPatterns("/Sample/**"); // 只针对你的Sample接口路径,更高效 } }
3. 让Specification能获取QueryParam
因为Specification本身不是Spring管理的Bean,我们让它实现ApplicationContextAware来获取Spring上下文,从而拿到Request Scope的QueryParam:
public class QuerySpecification<T> implements Specification<T>, ApplicationContextAware { private static ApplicationContext applicationContext; private QueryParam queryParam; @Override public void setApplicationContext(ApplicationContext ctx) throws BeansException { applicationContext = ctx; } public QuerySpecification() { // 通过上下文获取Request Scope的QueryParam,代理模式会自动绑定当前请求的实例 this.queryParam = applicationContext.getBean(QueryParam.class); } @Override public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { // 这里可以正常使用queryParam里的参数构建过滤逻辑 Map<String, String[]> params = queryParam.getParameter_map(); String queryString = queryParam.getQuery_string(); // 示例:根据name参数构建模糊查询 Predicate predicate = criteriaBuilder.conjunction(); if (params != null && params.containsKey("name")) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.like(root.get("name"), "%" + params.get("name")[0] + "%")); } return predicate; } }
4. 简化Repository接口
现在Repository接口不需要再处理QueryParam的注入了,直接在默认的findAll方法中使用QuerySpecification:
@RepositoryRestResource(collectionResourceRel = "Sample", path = "Sample") public interface SampleRepository extends PagingAndSortingRepository<Sample, UUID>, JpaSpecificationExecutor<Sample> { @Override default Page<Sample> findAll(Pageable pageable) { return findAll(new QuerySpecification<>(), pageable); } }
为什么之前的方案行不通?
- 你写的
SampleRepositoryComponents抽象类虽然实现了Repository接口,但Spring Data Rest生成的动态代理不会关联这个类的Autowired属性,导致getQuery_param()始终返回null; - Repository接口的默认方法不受Spring的Bean生命周期管理,没法直接访问Spring容器中的Bean,所以依赖注入不会生效。
内容的提问来源于stack exchange,提问作者superflux
相关产品推荐
相关产品推荐

