16. 自定义Spring Data REST

有许多选项来定制Spring Data REST。展示如以下这些小节。

16.1. 定制项目的URI

默认情况下,项目资源的URI是由用于标识附加数据库馆藏资源的路径段组成的。允许我们用知识库中的findOne(…)方查看实体实例。作为Spring Data REST2.5这可以通过在API repositoryrestconfiguration定制(java 8优先)上进行配置或注册Entitylookup为应用程序中的Spring bean的实现。Spring Data REST会重新整理并根据实施调整URI。

假设一个Userusername的属性,唯一地标识它。同时,假设我们有一个方法Optional<User> findByUsername(String username)在库中。

在java 8我们可以简单的映射方法为弱的URI创建如下方法参考:

@ComponentpublicclassSpringDataRestCustomizationextendsRepositoryRestConfigurerAdapter{@Overridepublicvoid configureRepositoryRestConfiguration(RepositoryRestConfiguration config){

    config.withCustomEntityLookup().//
      forRepository(UserRepository.class,User::getUsername,UserRepository::findByUsername);}}

forRepository(…) 将存储库类型作为第一个参数,一个方法参考映射到某个目标类型的存储域类型,以及另一个方法引用使用所说的第一个参数的存储库映射。

如果你不是使用java 8或更高版本,你可以这个方法,但它需要使用一些相当冗长的匿名内部类。这就是为什么在旧的java版本你可能喜欢实现UserEntityLookup接口,就像:

@ComponentpublicclassUserEntityLookupextendsEntityLookupSupport<User>{privatefinalUserRepository repository;publicUserEntityLookup(UserRepository repository){this.repository = repository;}@OverridepublicSerializable getResourceIdentifier(User entity){return entity.getUsername();}@OverridepublicObject lookupEntity(Serializable id){return repository.findByUsername(id.toString());}}

注意,被创建使用URIgetResourceIdentifier(…)如何返回用户名。我们通过现在实现lookupEntity(…)接口使用查询方法在userrepository的返回值加载实体实例

16.2. 配置其余的网址路径

在JPA库资源出口下配置部分的URL路径是简单的。您只需在类级别和/或查询方法级别上添加注释。

默认情况下,出口商将暴露你的 CrudRepository使用域的类的名称。Spring Data REST也适用于 Evo Inflector 对多元化这个词。因此,一个存储库定义如下:

interfacePersonRepositoryextendsCrudRepository<Person,Long>{}

是的,默认情况下,暴露的URL [http://localhost:8080/persons/](http://localhost:8080/persons/)

为了改变该库的出口,增加一个@RestResource在类级别注释:

@RepositoryRestResource(path ="people")interfacePersonRepositoryextendsCrudRepository<Person,Long>{}

存储库将被访问的网址: [http://localhost:8080/people/](http://localhost:8080/people/)

如果您有定义的查询方法,也将默认会被它们的名字所暴露:

interfacePersonRepositoryextendsCrudRepository<Person,Long>{List<Person> findByName(String name);}

这将被暴露在URL:[http://localhost:8080/persons/search/findByName](http://localhost:8080/persons/search/findByName)

所有的查询方法资源都暴露在资源下 search.

在查询方法暴露情况下,改变不跟URL,再次使用 @RestResource标注:

@RepositoryRestResource(path ="people")interfacePersonRepositoryextendsCrudRepository<Person,Long>{@RestResource(path ="names")List<Person> findByName(String name);}

这个查询方法将暴露在URL下: [http://localhost:8080/people/search/names](http://localhost:8080/people/search/names)

16.2.1. 处理关系

由于这些资源都是可以找到的,你也可以影响到"rel"属性如何显示在出口商发送的链接中。、

例如,在默认配置中,如果你发送一个请求给[http://localhost:8080/persons/search](http://localhost:8080/persons/search) 找出哪些查询方法暴露了,你会得到一个链接列表:

{"_links":{"findByName":{"href":"http://localhost:8080/persons/search/findByName"}}}

为了改变相对价值,使用rel 特性对@RestResource注释:

@RepositoryRestResource(path ="people")interfacePersonRepositoryextendsCrudRepository<Person,Long>{@RestResource(path ="names", rel ="names")List<Person> findByName(String name);}

这将产生一个链接:

{"_links":{"names":{"href":"http://localhost:8080/persons/search/names"}}}
这些片段的JSON默认你使用的是Spring Data REST的默认格式 HAL.关掉 HAL这是可能的,因为这将导致不同输出。但是你的重写相关名称是完全独立的呈现格式。
@RepositoryRestResource(path ="people", rel ="people")interfacePersonRepositoryextendsCrudRepository<Person,Long>{@RestResource(path ="names", rel ="names")List<Person> findByName(String name);}

改变相关存储库调整顶级名:

{"_links":{"people":{"href":"http://localhost:8080/people"},…}}

在上面的顶级片段:

  • path = "people"href 里面的/persons值改成 /people

  • rel = "people" 把链接名从 persons 改成 people

当你导航到 search 这个存储库的资源时, 这查找器方法 @RestResource 的注释改变了如下所示的路径:

{"_links":{"names":{"href":"http://localhost:8080/people/search/names"}}}

这组在你存储库定义中的注释造成了以下更改:

  • 储存库层次注释的path = "people" 反映在基本URI中 /people

  • 作为一个查找器方法为你提供 /people/search

  • path = "names" 创建一个URI的 /people/search/names

  • rel = "names" 改变链接的名称由 findByNamesnames

16.2.2. 隐藏某些存储库,查询方法或字段

你可能不希望某些存储库,存储库的查询方法,或者实体被导出。 例如包括隐藏在password 上的一个 User 的字段对象或类似的敏感数据. 去告诉输出口不输出这些项目, 为 @RestResource添加注释和设定 exported = false.

例如,跳过出口一个存储库:

@RepositoryRestResource(exported =false)interfacePersonRepositoryextendsCrudRepository<Person,Long>{}

跳过出口查询方法:

@RepositoryRestResource(path ="people", rel ="people")interfacePersonRepositoryextendsCrudRepository<Person,Long>{@RestResource(exported =false)List<Person> findByName(String name);}

或跳过输出字段:

@EntitypublicclassPerson{@Id@GeneratedValueprivateLong id;@OneToMany@RestResource(exported =false)privateMap<String,Profile> profiles;}
预测提供了有效的手段去改变导出side step these settings. 如果你对相同的域对象创建了预测, 这是你的责任不导出这些域. 看

16.2.3. 隐藏存储库的CRUD方法

如果你不想在你的 CrudRepository上公开保存或删除方法, 你可以使用 @RestResource(exported = false) 把你想关掉和放置注释在重载版本通过重写的方式设置. 例如, 在CrudRepository中为了防止HTTP用户调用删除方法, 覆盖所有的人,并将注释添加到重载方法.

@RepositoryRestResource(path ="people", rel ="people")interfacePersonRepositoryextendsCrudRepository<Person,Long>{@Override@RestResource(exported =false)voiddelete(Long id);@Override@RestResource(exported =false)voiddelete(Person entity);}
很重要的是,你想覆盖 both 删除方法作为导出 决定使用一些简单的算法,该方法使用CRUD具有更好的运行性能 . 现在不可能关闭一个需要身份证的删除的版本,但留下导出的版本需要一个实体实例。 F或者时间,您可以导出删除方法或不导出. 如果你想去关闭他们, 然后记住,你必须为这两个版本的注释 exported = false.

16.3. 添加 Spring Data REST 到现有的Spring MVC应用程序

如果你正在使用以下步骤,除非你正在使用 Spring Boot. spring-boot-starter-data-rest 将使它自动添加到您的应用程序.

如果你有一个现有的 Spring MVC 应用程序并且你想整合 Spring Data REST, 这其实很简单。

在你的 Spring MVC 配置中 (最有可能在你配置MVC资源的那里) 添加一个引用JavaConfig类,负责配置 RepositoryRestController. 这个类的名字是 org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration.

在java中你将看到这些:

import org.springframework.context.annotation.Import;import org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration;@Configuration@Import(RepositoryRestMvcConfiguration.class)publicclassMyApplicationConfiguration{…}

在XML中看起来会是这样:

<beanclass="org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration"/>

当你遇到这个bean的定义和应用,它将引导必要的 Spring MVC 资源到完全配置控制器导出它发现的存储库 ApplicationContext 和任何父上下文.

16.3.1. More on required configuration

有一对Spring MVC资源Spring Data Rest取决于必须要在现有的Spring MVC应用工作的正确配置。我们试图隔离任何类似的资源,这些资源已经存在于你的应用程序,但它可能是你想自定义一些Spring Data Rest行为通过修改这些MVC组件

最重要的东西是我们的配置,特别包括Spring Data Rest。

RepositoryRestHandlerMapping

我们注册一个自定义 HandlerMapping 仅响应的实例 RepositoryRestController 只有当一条路径是由Spring Data Rest处理。为了保持的路径,是指由您的应用程序分开处理Spring Data Rest自定义 HandlerMapping 检查网址路径和检查,看看是否有一个存储库已被导出到该名称下. 如果它有,它允许要求被Spring Data REST处理. 如果没有在这个名称下导出的存储库, 它会返回 null, 这就意味着 "让其他 HandlerMapping 实例试图服务此请求".

The Spring Data REST HandlerMapping配置 order=(Ordered.LOWEST_PRECEDENCE - 100) 这意味着它通常会在第一次上线时,来映射一个网址路径. 为了一个存储库,您的现有应用程序将永远不会得到服务请求的机会。 举个列子, 如果您有一个根据名称导出的存储库 "person", 然后你请求的所有应用程序启动/person 将被 Spring Data REST 处理然后您的应用程序将永远不会看到该请求。 如果您的存储库是在不同的名称下导出的, (like "people"),然后请求 /people 将来到Spring Data REST 并要求 "/person" 将由您的应用程序处理.

16.4. Overriding Spring Data REST Response Handlers

Sometimes you may want to write a custom handler for a specific resource. To take advantage of Spring Data REST’s settings, message converters, exception handling, and more, use the @RepositoryRestController annotation instead of a standard Spring MVC @Controller or @RestController:

@Repository RestController public class ScannerController{privatefinalScannerRepository repository;@AutowiredpublicScannerController(ScannerRepository repo){(1)
        repository = repo;}@RequestMapping(method = GET, value ="/scanners/search/listProducers")(2)public@ResponseBodyResponseEntity<?> getProducers(){List<String> producers = repository.listProducers();(3)//// do some intermediate processing, logging, etc. with the producers//Resources<String> resources =newResources<String>(producers);(4)

        resources.add(linkTo(methodOn(ScannerController.class).getProducers()).withSelfRel());(5)// add other links as neededreturnResponseEntity.ok(resources);(6)}}

该控制器将从RepositoryRestConfiguration.setBasePath定义相同的API基本路径所使用的所有其它基于REST的端点(e.g. /api)提供服务。它还具有以下特点:

1 这个例子使用构造器注入。
2 此处理程序插入一个Spring Data查找方法自定义处理程序。
3 该处理程序使用底层的存储库来获取数据,但是在将最终的数据集返回到客户端之前会做一些形式的后处理。
4 结果需要包裹在Spring HATEOAS Resources对象来返回一个集合,但只有一个Resource为单个项目。
5 添加一个链接返回到这个确切的方法作为一个"自我"链接
6 返回使用Spring MVC的ResponseEntity包装确认收集正确包装和呈现在适当的接受式的集合。

Resources为一个集合而Resource为单个的项目。这些类型可以组合使用。如果你知道一个集合中每个项目的链接,使用Resources<Resource&lt;String&gt;>(或任何核心域类型)。这可以让你组装环节的每个项目,以及整个集合。

在这个例子中, 合并的路径将是 RepositoryRestConfiguration.getBasePath() + /scanners/search/listProducers

如果你对特定实体的操作不感兴趣但仍希望创建basePath自定义操作,如Spring MVC的观点,资源等等。使用@BasePathAwareController

如果你对任意项目使用@Controller 或者 @RestController , 代码将完全超过 Spring Data REST的范围。 这扩展到请求处理,消息转换器,异常处理等。

16.5. 定制的JSON输出

有时候,在你的应用程序中你需要提供由特定实体到其他资源的链接。例如,一个Customer的反应可能会被链接到当前的购物车,或者链接来管理丰富的相关的实体资源。Spring Data REST提供集成Spring HATEOAS并为用户提供了一个扩展钩以改变对客户端的资源的表现形式。

16.5.1. 这个ResourceProcessor接口

Spring HATEOAS 定义了一个ResourceProcessor&lt;&gt;接口用于处理实体。所有类型为ResourceProcessor<Resource&lt;T&gt;>的beans会自动由Spring Data REST出口自动回收和序列化类型T的实体时触发。

例如,要给一个Person实体定义一个处理器,添加一个@Bean到你的ApplicationContext像下面的(从 Spring Data REST 实验得到):

@BeanpublicResourceProcessor<Resource<Person>> personProcessor(){returnnewResourceProcessor<Resource<Person>>(){@OverridepublicResource<Person> process(Resource<Person> resource){

      resource.add(newLink("http://localhost:8080/people","added-link"));return resource;}};}
这个例子的硬代码链接到 [http://localhost:8080/people](http://localhost:8080/people). 如果在你的应用程序中有Spring MVC端点你想链接到,考虑使用Spring HATEOAS的 linkTo(…​) 方法来避免管理URL.

这个可能就像上面的例子通过简单的调用resource.add(Link)添加链接到实体的默认表示上。任何链接添加到Resource都将被添加到最终输出。

16.5.3. 自定义表示

Spring Data REST在创建输出形式之前发现任意ResourceProcessors执行。它通过创建一个Converter<Entity, Resource>实例与内部ConversionService做到这一点。这是负责创建链接引用的实体部件(例如根据这些对象的_links在对象的JSON上表示属性)。这需要一个@Entity和遍历其属性,创造了一个链接由一个Repository跨入任何嵌入或简单的属性管理和复制这些属性。

如果你的项目需要有不同的输出格式,然而,它可能完全替代自己默认的传出JSON表示。如果在ApplicationContext中创建自己的ConversionService并创建自己的Converter<Person, Resource>,那么你就可以回到一个Resource去实现你所选择的。

16.6. 添加定制的 Jackson’s ObjectMapper序列

有时候Spring Data REST’s ObjectMapper已专门配置为使用智能串行器,可以把 域对象转化为链接,然后再返回,可能无法正确处理你的域模型的行为。有很多方法可以组织你的数据,你会发现你自己的域模型没有被翻译成正确的JSON。这也是有时并不在这些情况下,实际的尝试,并支持复杂的域模型的通用方法。有时,根据不同的复杂性,这甚至不可能提供一个通用的解决方案。

因此,为了适用用例的比例最大,Spring Data REST很难正确的呈现你的对象图,它会尝试序列化正常的POJOs为非受管的beans,它会尝试创建链接到受管的beans,这是必要的。但是,如果你的领域模型不容易让自己读取或写入纯JSON,你可能需要配置Jackson’s ObjectMapper与自己自定义的类映射和串行器。

16.6.1. 抽象类创建

当你在你的领域模型中使用抽象类或接口时,你可能需要挂接到一个关键的配置点。 Jackson将不知道怎样默认创建实现一个接口。看看下面的例子:

@Entity public class MyEntity{ @OneToMany private List<MyInterface> interfaces; }

results matching ""

    No results matching ""