Skip to main content

Spring Data REST

Tips

@Component
public class SpringDataRestCustomization extends RepositoryRestConfigurerAdapter {

@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {

config.withEntityLookup().
// 修改 URL 路径上的 ID 属性
forRepository(UserRepository.class, User::getUsername, UserRepository::findByUsername);
}
}

NOTES

  • ExcerptProjection
    • 片段映射
    • 只针对集合有效
    • 添加额外字段
  • RepositoryRestConfigurer
    • Spring Data WebMVC 的配置接口
  • RepositoryEntityController
    • WebMVC 入口
  • RestMediaTypes
    • application/hal+json
    • application/x-spring-data-compact+json
    • text/uri-list
    • application/json-patch+json
    • application/merge-patch+json
    • application/alps+json
    • application/schema+json
    • application/x-spring-data-verbose+json
  • BackendIdConverter
    • 配置 URL 上 id 生成方式
    • @BackendId 可用于注解该配置
  • ResourceAssembler
    • 用于构建资源
    • PersistentEntityResourceAssembler : ResourceAssembler<Object, PersistentEntityResource>
      • 将任意对象转换为 PersistentEntityResource
  • PersistentEntityJackson2Module
    • PersistentEntityResource 序列化模块
    • AssociationOmittingSerializerModifier
      • 将关联字段移除
      • 不能改动态修改, 因为序列化器构建成功后会缓存

HATEOAS

  • Resources
    • 表示一组资源内容
  • ResourceProcessor<T extends ResourceSupport>
    • 资源预处理接口
    • ResourceProcessorInvoker
      • 统一调用方法

如何实现动态映射 ?

  • 例如通过参数 select=id,name,parent(id,name) 来确定
  • PersistentEntityResourceAssemblerArgumentResolver 实现了 PersistentEntityResourceAssembler 的注入
  • 注入的 PersistentEntityProjector 是根据参数中的 projection 来创建的
  • 通过 ProjectionDefinitions 拿到映射类
  • 通过 ProjectionFactory 创建映射, 映射的最终结果就是映射后的内容
  • 映射后的内容被封装为 PersistentEntityResource
    • 包含 Link ,Embededs, Content, Entity
  • Spel 的映射是通过方法拦截实现的
  • TODO

使用虚拟映射字段

@Projection(name = "virtual", types = { Person.class })
public interface VirtualProjection {
@Value("#{target.firstName} #{target.lastName}")
String getFullName();
}

覆盖处理方法

@RepositoryRestController
public class ScannerController {

private final ScannerRepository repository;

@Autowired
public ScannerController(ScannerRepository repo) {
repository = repo;
}

@RequestMapping(method = GET, value = "/scanners/search/listProducers")
public @ResponseBody ResponseEntity<?> getProducers() {
List<String> producers = repository.listProducers();

//
// do some intermediate processing, logging, etc. with the producers
//

Resources<String> resources = new Resources<String>(producers);

resources.add(linkTo(methodOn(ScannerController.class).getProducers()).withSelfRel());

// add other links as needed

return ResponseEntity.ok(resources);
}

}

自定义实体处理

@Bean
public ResourceProcessor<Resource<Person>> personProcessor() {

return new ResourceProcessor<Resource<Person>>() {

@Override
public Resource<Person> process(Resource<Person> resource) {

resource.add(new Link("http://localhost:8080/people", "added-link"));
return resource;
}
};
}

添加 Spring Data REST 到现有 Spring MVC 项目

import org.springframework.context.annotation.Import;
import org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration;

@Configuration
@Import(RepositoryRestMvcConfiguration.class)
public class MyApplicationConfiguration {

}

自定义资源类型处理

@Controller
@ExposesResourceFor(Order.class)
@RequestMapping("/orders")
class OrderController {

@RequestMapping
ResponseEntity orders() {}

@RequestMapping("/{id}")
ResponseEntity order(@PathVariable("id")) {}
}