【问题标题】:Using a Spring Data Rest @Projection as a representation for a resource in a custom controller使用 Spring Data Rest @Projection 作为自定义控制器中资源的表示
【发布时间】:2016-01-22 04:08:06
【问题描述】:

有没有办法使用@Projection 接口作为 SDR 中资源的默认表示?通过 SDR 存储库还是通过自定义控制器?

过去可以在自定义控制器中通过注入 ProjectionFactory 并使用 createProjection 方法来执行此操作,但这已被最近的 Spring Data Rest 更新打破。

我想在实体上强制执行特定视图,而 SDR 投影似乎是执行此操作的理想方法,尤其是在 HAL API 的上下文中,而不是为自定义控制器和映射编写硬 DTO 类它们之间等等。摘录预测不是我所追求的,因为这些仅在查看相关资源时适用。

【问题讨论】:

    标签: java spring spring-boot spring-data-rest spring-hateoas


    【解决方案1】:

    不,不可能开箱即用。如果嵌入了资源,则始终使用摘录投影。在单个资源上,您可以提供所需的投影作为查询参数。

    您可以做的是使用 Jackson Mixins 来更改 json 表示。

    你可以在这里找到一些很好的例子: https://github.com/olivergierke/spring-restbucks/blob/master/src/main/java/org/springsource/restbucks/JacksonCustomizations.java

    【讨论】:

    • 谢谢。以前(SDR 2.4.0 之前的版本),可以只将SpelAwareProxyProjectionFactory 的实例注入您的控制器类,然后在您的自定义控制器中,在您想要投影的域对象上调用createProjection,然后返回回应中的预测,但不幸的是,这已被 SDR 方面的一个或多个变化打破。 Jackson Mixins 路线更加复杂和笨拙。
    • 之前有类似的问题,Oliver Gierke 直接回答了这个问题,并给出了一些很好的论据,为什么对单一资源使用投影不是一个好主意 - 请参阅 stackoverflow.com/questions/30220333/…
    • 是的,我看到了,尽管这主要与摘录和使用标准 SDR 导出的存储库有关。尽管如此,Oliver 还在这里概述了如何做我所追求的(以及我正在做的事情):stackoverflow.com/questions/29376090/… 和这里:spring.io/blog/2015/03/26/what-s-new-in-spring-data-fowler,但这些代码示例在当前版本的 SDR 中不再适用,引发 Jackson 异常。
    【解决方案2】:

    要回答我自己的问题,现在有几种简单的方法可以做到这一点。

    您可以让 SDR 存储库查找器默认返回投影:

    public interface PersonRepository extends PagingAndSortingRepository<Person,Long> {
    
        Set<PersonProjection> findByLastName(String lastName);
    
    }
    

    您还可以通过使用 @BasePathAwareController 创建自定义 Spring MVC 控制器来选择性地覆盖 SDR 默认为您处理的响应。如果您计划提供分页响应,则需要注入 ProjectionFactory 和可能的 PagedResourcesAssembler。

    @BasePathAwareController
    public class CustomPersonController {
    
    @Autowired
    private ProjectionFactory factory;
    
    @Autowired
    private PersonRepository personRepository;
    
    @Autowired
    private PagedResourcesAssembler<PersonProjection> assembler;
    
    @RequestMapping(value="/persons", method = RequestMethod.GET, produces = "application/hal+json")
    public ResponseEntity<?> getPeople(Pageable pageable) {
        Page<Person> people = personRepository.findAll(pageable);
        Page<PersonProjection> projected = people.map(l -> factory.createProjection(PersonProjection.class, l));
        PagedResources<Resource<PersonProjection>> resources = assembler.toResource(projected);
        return ResponseEntity.ok(resources);
    }
    

    【讨论】:

    • 除了@adam 响应外,还需要在一些@Configuration 文件中添加@Bean,例如@Configuration class SomeConfig { @Bean public SpelAwareProxyProjectionFactory projectionFactory() { return new SpelAwareProxyProjectionFactory(); } }source
    • 太棒了!谢谢!
    【解决方案3】:

    我想提出另一个解决方案。

    我按照@adam 的建议使用自定义控制器,直到我不得不PATCH 一个资源并获得新的表示。默认情况下,无法覆盖存储库的 save 方法以使用投影。一直在实现自定义控制器会给项目带来一些样板。

    由于我已经使用了ResourceProcessors,我决定在这些处理器中应用默认投影。

    @Component
    public class ProductResourceProcessor implements ResourceProcessor<Resource<Product>> {
    
        @Autowired
        private ProjectionFactory projectionFactory;
    
        @Override
        @SuppressWarnings("unchecked")
        public Resource<Product> process(Resource<Product> resource) {
            Product content = resource.getContent();
            ProductInline projection = projectionFactory.createProjection(ProductInline.class, content);
            Resource<ProductInline> result = new Resource<>(projection);
            //copying and adding links
            return (Resource) result;
        }
    }
    

    处理器重新打包处理资源的内容。无论哪个处理程序将资源作为响应返回(GETPOSTPATCH 任何内容),都会应用投影。

    这种方法的缺点是它假定spring-data-rest 不要求处理后的内容类型相同。这可能会在未来的版本中随时更改。

    【讨论】:

      猜你喜欢
      • 2018-04-26
      • 2016-02-07
      • 1970-01-01
      • 2018-01-19
      • 1970-01-01
      • 2015-12-07
      • 1970-01-01
      • 2017-09-21
      • 2017-03-10
      相关资源
      最近更新 更多