Spring Data

核心概念

Repository

Repository 是Spring Data 的核心接口 。 它将domain 类 和 domain 类的ID作为管理参数 。 其他的Repository都继承实现该接口。 如CrudRepository :

public interface CrudRepository<T, ID extends Serializable>
  extends Repository<T, ID> {

  <S extends T> S save(S entity);      

  Optional<T> findById(ID primaryKey); 

  Iterable<T> findAll();               

  long count();                        

  void delete(T entity);               

  boolean existsById(ID primaryKey);   

  // … more functionality omitted.
}

其他特定技术的抽象如 JpaRepository or MongoRepository 都是CrudRepository的子类。

在CrudRepository上层还有一个抽象接口 PagingAndSortingRepository , 提供分页排序功能:

public interface PagingAndSortingRepository<T, ID extends Serializable>
  extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

Query 方法

使用spring data 进行数据查询有如下四个步骤:

  1. 声明一个接口, 该接口需要继承Repository 或其子接口 ,并提供domain 类和id参数, 如下:
interface PersonRepository extends Repository<Person, Long> { … }
  1. 在该接口中声明查询方法:
interface PersonRepository extends Repository<Person, Long> {
  List<Person> findByLastname(String lastname);
}
  1. 使用spring建立该接口的代理接口, 可以通过JavaConfig 或 Xml 配置。
    3.1 使用Javaconfig
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaRepositories
class Config {}

3.2 使用xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jpa="http://www.springframework.org/schema/data/jpa"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/data/jpa
     http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

   <jpa:repositories base-package="com.acme.repositories"/>

</beans>

上例使用了JPA名称空间。若使用其他的数据源,需要修改为其他的名称空间 。

注意:javaConfig 并咩有显示声明包名称, 因为默认会使用注解所在的包。 要修改默认需要使用 basePackage属性。

  1. 注入repository实例并使用之 :
class SomeClient {

  private final PersonRepository repository;

  SomeClient(PersonRepository repository) {
    this.repository = repository;
  }

  void doSomething() {
    List<Person> persons = repository.findByLastname("Matthews");
  }
}

后续章节将详细介绍这4个步骤。

定义Repository 接口

首先,定义repository接口,需要指定domain class 和id 类型 , 可以直接继承Repository接口,或者其子接口如CrudRepository 。

调整Repository接口

一般的,定义repository接口需要继承 Repository, CrudRepository, or PagingAndSortingRepository. 但如果不想使用Spring data提供的接口,也可以自定义 。自定义接口上加 @RepositoryDefinition 注解。

当继承CrudRepository时, 会暴露所有的CRUD接口, 若不想暴露那么多 , 则可以直接从Repository继承, 如下所示:

@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> {

  Optional<T> findById(ID id);

  <S extends T> S save(S entity);
}

interface UserRepository extends MyBaseRepository<User, Long> {
  User findByEmailAddress(EmailAddress emailAddress);
}

上例中, UserRepository 只会暴露 findById 、save findByEmailAddress接口, 不会暴露其他的 。

注意: @NoRepositoryBean 使spring data 不会生成相应repository的实例, 用在中间repository定义上。

Repository 的NULL处理

从spring data 2.0 开始 , repository的方法使用java 8 的Optional来表示可能缺少的值。 除此之外,spring data还为查询提供如下的封装类:

  • com.google.common.base.Optional

  • scala.Option

  • io.vavr.control.Option

  • javaslang.control.Option (deprecated as Javaslang is deprecated)

另外, 查询方法可以选择不使用这些封装类。 通过返回null表示没有查询结果 。 Repository 方法返回集合、变种集合、封装、流 时会保证不会出现null , 而是返回相应的空。

repository 方法的可空注解如下:

  • @NonNullApi : 在包级别上使用,以声明参数和返回值的默认行为是不接受或生成空值。
  • @NonNull : 使用在参数或返回值上,他们不能为null (在使用了@NonNullApi时不需要。)
  • @Nullable : 使用在参数或返回值上, 表示可以为null。

如:在package-info.java 上声明:

@org.springframework.lang.NonNullApi
package com.acme;

如例:

package com.acme;                                                       
//  该包定义在我们声明的non-null包中;  

import org.springframework.lang.Nullable;

interface UserRepository extends Repository<User, Long> {

  // 当返回为空时抛出 EmptyResultDataAccessException  异常 。 当输入参数emailAddress空时抛出 IllegalArgumentException 
  User getByEmailAddress(EmailAddress emailAddress);                    

  @Nullable
//  允许输入参数为空; 允许返回空值 
  User findByEmailAddress(@Nullable EmailAddress emailAdress);          

// 当没有查询结果时返回Optional.empty() ; 当输入emailAddress为空时抛出IllegalArgumentException 
  Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); 
}

多数据源repository

当使用单一spring data module时,会很简单, 因为所有的repository都会绑定到该模块上。 但是当应用要求多个数据module时, 需要明确repository使用的模块 。

当spring 检查到引入多个数据module时, 他会按照如下规则进行判断:

  1. repository 是否继承自特定数据源, 将根据特定数据源进行判断。
  2. domain 类上是否有特定数据源的注解, spring data支持第三方的注解(如JPA的@Entity ) , 也有自己的注解 (如Mongo和Elasticsearch的@Document )

如下例子显示了使用JPA的例子:

interface MyRepository extends JpaRepository<User, Long> { }

@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
  …
}

interface UserRepository extends MyBaseRepository<User, Long> {
  …
}

MyRepository and UserRepository 继承自JpaRepository , 会使用spring data JPA。

如下例子 repository 继承通用的repository:

interface AmbiguousRepository extends Repository<User, Long> {
 …
}

@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
  …
}

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> {
  …
}

通过上述继承关系是无法判断的 。
在定义user类时, 若使用@Entity ,则使用的是 JPA ; 若使用的是@Document , 则使用的是mongo。

如下例子在person上同时使用了两个注解, 会引发异常:

interface JpaPersonRepository extends Repository<Person, Long> {
 …
}

interface MongoDBPersonRepository extends Repository<Person, Long> {
 …
}

@Entity
@Document
class Person {
  …
}

在同一domain类型上使用多个持久性技术特定的注释是可能的,并允许跨多种持久性技术重用域类型。 但是,Spring Data不再能够确定用于绑定存储库的唯一模块。

区分repository的最后一种方法是定义使用repository的范围。 基础包定义了扫描repository接口定义的起点,这意味着将repository定义放在相应的包中。 默认情况下,注释驱动的配置使用配置类的包。 基于XML的配置中的基本包是必需的。

@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
interface Configuration { }

定义查询方法

有两种方式来确定查询方法:

  • 根据方法名推断
  • 手工定义查询

查询策略

使用XML配置,您可以通过query-lookup-strategy属性在命名空间配置策略。 对于Java配置,您可以使用Enable $ {store}存储库注释的queryLookupStrategy属性。 特定数据存储可能不支持某些策略。

  • CREATE: 从方法名构造特定数据源的查询语句 。 一般方法是去掉方法前缀,解析其余部分。后面有详细介绍。
  • USE_DECLARED_QUERY : 查找声明的查询,未找到则会抛出异常 。 可以是注解或其他方式。 repository在启动时尝试查找相应数据源的声明, 未找到则fail。
  • CREATE_IF_NOT_FOUND : 默认选项。 结合 CREATE and USE_DECLARED_QUERY 。 它首先查找声明,未声明则创建一个基于方法名的查询 。 这是默认方式。 它允许根据方法名快速定义,也允许自定义查询 。

Query Creation

其机制是剥离前缀find ... By,read ... By,query ... By,count ... By,and get ...By ,然后解析其余部分。同时也可以使用And Or distinct等等。

例:

interface PersonRepository extends Repository<User, Long> {

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // Enables the distinct flag for the query
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // Enabling ignoring case for an individual property
  List<Person> findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Enabling static ORDER BY for a query
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

最终的语句依赖不同的数据源有所不同, 但是有些共同的部分:

  • 表达式是属性和运算符的结合, 支持AND 、 OR 、Between 、LessThen 、GreaterThen 、like 等, 另外因数据源不同有些操作符也不同。
  • 方法解析器支持为各个属性设置IgnoreCase标志(例如,findByLastnameIgnoreCase(...))或支持所有属性忽略大小写(通常是String实例 - 例如,findByLastnameAndFirstnameAllIgnoreCase(...))。 是否支持忽略大小写可能因数据源不同而异,因此请参阅参考文档中有关查询方法的相关章节。
  • 排序, 使用OrderBy后接字段来实现, 并可以指定方向(Asc或Desc)

属性表达式

如 : person中定义了Address , address 有zipCode字段:
List<Person> findByAddressZipCode(ZipCode zipCode);

spring data 会尝试去判断在哪儿进行分割 ,有可能会出现错误。

建议使用进行显示分割,以防其分割错误:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
但是,
在java中是保留字符, 不建议使用, 建议使用驼峰结构来表示。

特殊参数处理

特定参数指分页 排序, Pageable and Sort 。 这俩参数将被特殊对待。
如下:

Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);

第一个方法传入参数 org.springframework.data.domain.Pageable 以生成动态分页查询。 返回的Page对象有全部页数信息和当前页信息。 全部页数信息是通过一个计数语句完成的。这在某些数据源上可能会比较昂贵。 另一种方式是返回Slice结构, 该结构会包含一个是否还有下一页数据字段,这对大数据集非常有用。

Pageable 实例同时会处理sort 。 若只需要sort ,可以直接使用 org.springframework.data.domain.Sort 。 分页也可以只返回List, 这样不会触发count查询, 但是这只适用于查询给定范围的信息。

限制查询结果

查询结果可以通过附加first 和top 关键字来返回一部分数据。 first 和top后跟一个数字, 若没有数字, 则默认为1 。
例子:

User findFirstByOrderByLastnameAsc();

User findTopByOrderByAgeDesc();

Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);

Slice<User> findTop3ByLastname(String lastname, Pageable pageable);

List<User> findFirst10ByLastname(String lastname, Sort sort);

List<User> findTop10ByLastname(String lastname, Pageable pageable);

限制查询支持Distinct 关键字。 查询结果也可以包装为Optional。

若限制查询中使用了 page 或slice , 则是对限制查询后的结果进行page 或slice 。

流化查询结果

可以将查询结果返回为Java8 Stream<T> 结构, 或者使用特定数据源的stream查询。
例子:

@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);

使用stream 需要在使用完后进行close , 可以手工close , 或使用try-with-resources结构:

try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
  stream.forEach(…);
}

注意: 不是所有的spring data模块都支持stream

异步查询

某些数据源支持异步查询, 查询方法会立即返回 , 但是不会立即出结果 。

@Async
Future<User> findByFirstname(String firstname);               

@Async
CompletableFuture<User> findOneByFirstname(String firstname); 

@Async
ListenableFuture<User> findOneByLastname(String lastname);    

Use java.util.concurrent.Future as the return type.
Use a Java 8 java.util.concurrent.CompletableFuture as the return type.
Use a org.springframework.util.concurrent.ListenableFuture as the return type.

创建Repository 实例

定义完repository接口后, 需要定义该接口实例 。 一种方法是使用spring data module提供的名称空间配置, 但我们建议使用java configuration 。

xml 配置

每个spring data module 都包含一个 repositories 元素 , 它定义一个将扫描的base package 。

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <repositories base-package="com.acme.repositories" />

</beans:beans>

上例中, spring 将会扫描com.acme.repositories 及其子package ,寻找repository及其子接口。 对于每个找到的接口 , spring 都会使用特定数据源的FactoryBean 来创建合适的代理。 每个bean都会注册一个同接口名的bean 。 例如 UserRepository 接口的bean名即为UserRepository 。 另外 base-package属性支持通配符。

使用过滤的例子:

<repositories base-package="com.acme.repositories">
  <context:exclude-filter type="regex" expression=".*SomeRepository" />
</repositories>

支持 <include-filter /> and <exclude-filter /> 过滤 。

javaConfig

在类上使用注解 @Enable${store}Repositories 可以同样实例化repository 。

如下例子 配置一个JPA:

@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

  @Bean
  EntityManagerFactory entityManagerFactory() {
    // …
  }
}

@Configuration @Bean

@Bean注解的角色与 xml中配置<bean>标签的角色一样。 其作用的方法上, 指示方法实例化、配置、初始化由Spring IoC容器管理的新对象。
@Bean可与Spring @Component一起使用,但是,它们最常用于@Configuration bean。

@Configuration 注解使用在类上 , 作为bean的定义源。在类中通过简单调用@Bean方法来表示bean间依赖关系。

如:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

等同于xml配置如下:
<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

当@Bean 与 @Configuration一起使用时, @Bean可以定义bean间的依赖。
但当@Bean不与@Configuration一起使用时, 只是使用了Bean 工厂,不定义依赖。

独立使用

可以在Spring 容器之外使用repository 。 例如, 在CDI环境 。 但仍然需要spring librries。 spring data模块提供特定数据源的RepositoryFactory 以支持repository。 例:

RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);

自定义实现repository

  1. 首选自定义一个接口 , 如
interface CustomizedUserRepository {
  void someCustomMethod(User user);
}
  1. 实现该接口 , 接口实现类必须以Impl结尾
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}
  1. 定义repository时继承标准的repository和该自定义的
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {

  // Declare query methods here
}

这样客户端即可使用自定义实现的方法了 。

(当然也 同时支持多个自定义repository)

优先级

自定义方法的优先级高于通用repository 和 特定存储库提供的。

可以在自定义中覆盖通用或特定存储库的方法。

xml配置

当使用xml配置时, 同样遵循impl后缀原则 。 spring会扫描base-package下的自定义实现。

<repositories base-package="com.acme.repository" />

<repositories base-package="com.acme.repository" repository-impl-postfix="MyPostfix" />

可以通过配置修改默认后缀, 如上。

歧义处理

若多个相同类名的 实现在不同的package中发现 , spring data 通过bean名称来确定使用哪一个。
例:

package com.acme.impl.one;

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  // Your custom implementation
}

package com.acme.impl.two;

@Component("specialCustomImpl")
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  // Your custom implementation
}

如上有CustomizedUserRepository 的两个实现, 则第一个实现的bean名符合约定, 使用之。

自定义base repository

如果需要为所有的repository添加base实现, 可以通过实现特定数据源的repository来实现。

class MyRepositoryImpl<T, ID extends Serializable>
  extends SimpleJpaRepository<T, ID> {

  private final EntityManager entityManager;

  MyRepositoryImpl(JpaEntityInformation entityInformation,
                          EntityManager entityManager) {
    super(entityInformation, entityManager);

    // Keep the EntityManager around to used from the newly introduced methods.
    this.entityManager = entityManager;
  }

  @Transactional
  public <S extends T> S save(S entity) {
    // implementation goes here
  }
}

配置时需要使用

@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

<repositories base-package="com.acme.repository"
     base-class="….MyRepositoryImpl" />

Aggregate roots 事件发布

在Domain-Driven Design 应用中, 聚合根发布domain 事件。
在domain类的方法中使用。
使用@DomainEvents 发布事件
使用@AfterDomainEventPublication 清理事件

这些方法会在调用save时被调用。

Spring data 扩展

目前,大多数扩展都是扩展spring mvc。

Querydsl扩展

Querydsl是一个框架,可以通过其流畅的API构建静态类型的SQL类查询。

有几款spring data module 通过 QuerydslPredicateExecutor 提供 Querydsl的整合。 如下:

public interface QuerydslPredicateExecutor<T> {

  Optional<T> findById(Predicate predicate);  

  Iterable<T> findAll(Predicate predicate);   

  long count(Predicate predicate);            

  boolean exists(Predicate predicate);        

  // … more functionality omitted.
}
  • Finds and returns a single entity matching the Predicate.
  • Finds and returns all entities matching the Predicate.
  • Returns the number of entities matching the Predicate.
  • Returns whether an entity that matches the Predicate exists.

然后通过继承QuerydslPredicateExecutor 来使用之:

interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}

使用如下:

Predicate predicate = user.firstname.equalsIgnoreCase("dave")
    .and(user.lastname.startsWithIgnoreCase("mathews"));

userRepository.findAll(predicate);

Web 支持

通过在configurationclass 上添加注解 @EnableSpringDataWebSupport 来支持:

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration {}

@EnableSpringDataWebSupport 会自动添加几个组件。 另外他还会检测classpath上是否有HATEOAS , 有则自动注册之。

若使用基于xml的配置, 见下:

<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />

<!-- If you use Spring HATEOAS, register this one *instead* of the former -->
<bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />

基础web 支持

  • DomainClassConverter : 让Spring MVC从请求参数或路径变量中解析domain类的实例。
  • HandlerMethodArgumentResolver : 让spring mvc 从请求参数中解析Pageable 和Sort实例。

DomainClassConverter

DomainClassConverter 允许在spring mvc中直接使用domain类, 而不用手工查找:

@Controller
@RequestMapping("/users")
class UserController {

  @RequestMapping("/{id}")
  String showUserForm(@PathVariable("id") User user, Model model) {

    model.addAttribute("user", user);
    return "userForm";
  }
}

该方法接收一个User实例,spring mvc 通过解析请求信息为id类型,然后调用findById方法。

HandlerMethodArgumentResolvers

web支持会同时注册PageableHandlerMethodArgumentResolver 和 SortHandlerMethodArgumentResolver 。 这样controller就可以使用Pageable 和sort。

例 :

@Controller
@RequestMapping("/users")
class UserController {

  private final UserRepository repository;

  UserController(UserRepository repository) {
    this.repository = repository;
  }

  @RequestMapping
  String showUsers(Model model, Pageable pageable) {

    model.addAttribute("users", repository.findAll(pageable));
    return "users";
  }
}

例中, spring mvc 将请求参数转换为Pageable ,涉及如下参数:

  • page : 想要获取的页码,从0开始 ,默认为0
  • size : 每页条数 , 默认20
  • sort : 排序属性 ,以property的格式 property,property(,ASC|DESC) 。默认是升序 。 支持多个参数,如: ?sort=firstname&sort=lastname,asc

要自定义该行为, 通过实现接口 PageableHandlerMethodArgumentResolverCustomizer 和 SortHandlerMethodArgumentResolverCustomizer , 实现其中的customize()方法。

若修改MethodArgumentResolver 满足不了需求, 可以通过继承SpringDataWebConfiguration 或 HATEOAS-enabled equivalent , 重写 pageableResolver() or sortResolver() 方法 ,导入自定义配置而不是使用@Enable注解。

若需要从request中解析出多组Pageable 或sort ,如多table情况, 可以使用spring 的@Qualifier标签。 同时request的参数前需要加${qualifier}_前缀 。 例:

String showUsers(Model model,
      @Qualifier("thing1") Pageable first,
      @Qualifier("thing2") Pageable second) { … }

默认传递到方法中的pageable 等同于 new PageRequest(0, 20) , 可以通过在Pageable参数上加注解@PageableDefault 来自定义 。

超媒体分页

Spring HATEOAS 提供PagedResources类来丰富分页信息。
将Page转换为PagedResources 由PagedResourcesAssembler 实现。

@Controller
class PersonController {

  @Autowired PersonRepository repository;

  @RequestMapping(value = "/persons", method = RequestMethod.GET)
  HttpEntity<PagedResources<Person>> persons(Pageable pageable,
    PagedResourcesAssembler assembler) {

    Page<Person> persons = repository.findAll(pageable);
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
  }
}

web 数据绑定

spring data 的projections(预测)可以用来绑定请求数据, 例子:

@ProjectedPayload
public interface UserPayload {

  @XBRead("//firstname")
  @JsonPath("$..firstname")
  String getFirstname();

  @XBRead("/lastname")
  @JsonPath({ "$.lastname", "$.user.lastname" })
  String getLastname();
}

对于spring mvc , 只要@EnableSpringDataWebSupport注解激活并且类路径上有相关的依赖, 则必要的转换器会自动被注册 。 要使用RestTemplate ,则需要手工注册 ProjectingJackson2HttpMessageConverter (JSON) or XmlBeamHttpMessageConverter 。

repository populators

当使用spring data jdbc模块时, 我们熟悉使用sql语言来与datasource交互 。 作为repository 级别的抽象, 不使用sql作为定义语言,因为他们是依赖数据源的。
populator 是支持json 和xml的 。

如有如下data.json文件:

[ { "_class" : "com.acme.Person",
 "firstname" : "Dave",
  "lastname" : "Matthews" },
  { "_class" : "com.acme.Person",
 "firstname" : "Carter",
  "lastname" : "Beauford" } ]

你可以使用repository命名空间的populator来操作repository 。 声明如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    http://www.springframework.org/schema/data/repository/spring-repository.xsd">

  <repository:jackson2-populator locations="classpath:data.json" />

</beans>

上述data.json文件将由Jackson的ObjectMapper来读取并序列化。

Json根据文件中的_class 来确定序列化的对象。

下例显示如何声明使用JAXB来处理xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xmlns:oxm="http://www.springframework.org/schema/oxm"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    http://www.springframework.org/schema/data/repository/spring-repository.xsd
    http://www.springframework.org/schema/oxm
    http://www.springframework.org/schema/oxm/spring-oxm.xsd">

  <repository:unmarshaller-populator locations="classpath:data.json"
    unmarshaller-ref="unmarshaller" />

  <oxm:jaxb2-marshaller contextPath="com.acme" />

</beans>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容