Orika JavaBean映射工具使用
字数 991 2025-08-11 08:36:13

Orika JavaBean映射工具使用指南

1. Orika简介

Orika是一个简单、快速的JavaBean拷贝框架,能够递归地将数据从一个JavaBean复制到另一个JavaBean,这在多层应用开发中非常有用。

1.1 为什么要使用Orika

在开发中,我们经常需要:

  • 将对象转换成不同形式以适应不同API
  • 在不同业务层中传输对象而不同分层的对象存在不同格式

传统方式的问题:

  • Java反射:性能问题、无法解决嵌套JavaBean映射复制
  • 手工映射:硬编码、代码量大
  • 序列化方式:通过JSON/XML序列化和反序列化,字段参数不同时需要硬编码赋值

2. 常用JavaBean映射技术对比

2.1 BeanUtils

  • Apache和Spring的BeanUtils
  • 原理:使用java.beans.Introspector获取属性信息及get/set方法,通过反射进行赋值
  • 特点:
    • Apache支持名称相同但类型不同的属性转换
    • Spring支持忽略某些属性不进行映射
    • 都设置了缓存保存已解析过的BeanInfo信息

2.2 BeanCopier

  • Cglib的BeanCopier
  • 原理:使用ASM的MethodVisitor直接编写各属性的get/set方法生成class文件
  • 特点:性能比反射方式的BeanUtils高

2.3 Orika

  • 底层使用javassist类库生成Bean映射的字节码
  • 特点:
    • 速度比反射快很多
    • 支持递归映射
    • 能处理对象中的递归引用

3. Orika使用详解

3.1 Maven依赖

<dependency>
    <groupId>ma.glasnost.orika</groupId>
    <artifactId>orika-core</artifactId>
    <version>1.5.2</version>
</dependency>

3.2 基本映射

3.2.1 字段名相同映射

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
// 字段名相同映射
mapperFactory.classMap(Person.class, PersonInfo.class).byDefault().register();
MapperFacade mapperFacade = mapperFactory.getMapperFacade();

Person person = new Person();
person.setFirstName("张");
person.setLastName("三");
PersonInfo personInfo = mapperFacade.map(person, PersonInfo.class);

3.2.2 字段名不同映射

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(Person.class, PersonDesc.class)
    .field("firstName", "givenName")
    .field("lastName", "sirName")
    .byDefault()
    .register();

MapperFacade mapperFacadeDesc = mapperFactory.getMapperFacade();
Person personNew = new Person();
personNew.setFirstName("王");
personNew.setLastName("五");
PersonDesc personDesc = mapperFacadeDesc.map(personNew, PersonDesc.class);

3.3 复杂映射

3.3.1 数组和List映射

public static class PersonNameList {
    private List<String> nameList;
    // 构造方法和getter/setter
}

mapperFactory.classMap(PersonNameList.class, Person.class)
    .field("nameList[0]", "firstName")
    .field("nameList[1]", "lastName")
    .register();

List<String> nameParts = Arrays.asList(new String[]{"李", "四"});
Person person = mapperFactory.getMapperFacade().map(new PersonNameList(nameParts), Person.class);

3.3.2 相同类型JavaBean复制(原型模式)

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(Person.class, Person.class).byDefault().register();

Person person = new Person();
person.setFirstName("张");
person.setLastName("三");
Person personNew = mapperFactory.getMapperFacade().map(person, Person.class);

3.3.3 嵌套JavaBean映射

mapperFactory.classMap(Flight.class, Flight.class).byDefault().register();

Cabin cabinY = new Cabin("Y", "8", 1200);
Cabin cabinF = new Cabin("C", "6", 1900);
Cabin cabinC = new Cabin("F", "A", 2400);
Flight flight = new Flight("CA1831", Lists.newArrayList(cabinY, cabinF, cabinC));

Flight flightCopy = mapperFactory.getMapperFacade().map(flight, Flight.class);

3.3.4 List类型映射

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(Cabin.class, CabinDesc.class).byDefault().register();

Cabin cabinY = new Cabin("Y", "8", 1200);
Cabin cabinF = new Cabin("C", "6", 1900);
Cabin cabinC = new Cabin("F", "A", 2400);
List<Cabin> cabinList = Lists.newArrayList(cabinY, cabinF, cabinC);

List<CabinDesc> cabinDescList = mapperFactory.getMapperFacade().mapAsList(cabinList, CabinDesc.class);

3.4 自定义数据类型转换器

3.4.1 String转Date

public static class StringToDateConverter extends CustomConverter<String, Date> {
    private String dateFormat;
    
    public StringToDateConverter(String dateFormat) {
        this.dateFormat = dateFormat;
    }
    
    public Date convert(String s, Type<? extends Date> type, MappingContext mappingContext) {
        try {
            return DateUtils.parseDate(s, dateFormat);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

// 使用
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.getConverterFactory().registerConverter(new StringToDateConverter("yyyy-MM-dd"));

mapperFactory.classMap(ArrayMapper.PersonNameList.class, Person.class)
    .field("nameList[0]", "firstName")
    .field("nameList[1]", "lastName")
    .field("nameList[2]", "birthDate")
    .register();

List<String> nameParts = Arrays.asList(new String[]{"李", "四", "1989-10-11"});
Person person = mapperFactory.getMapperFacade().map(new ArrayMapper.PersonNameList(nameParts), Person.class);

4. 性能对比

测试方法:

  1. 每种方法先预热执行20次
  2. 再执行100000次获取每次执行的平均时间

性能排序(从快到慢):

  1. Orika(字节码生成)
  2. BeanCopier(ASM生成字节码)
  3. BeanUtils(反射)

5. 参考资料

Orika JavaBean映射工具使用指南 1. Orika简介 Orika是一个简单、快速的JavaBean拷贝框架,能够递归地将数据从一个JavaBean复制到另一个JavaBean,这在多层应用开发中非常有用。 1.1 为什么要使用Orika 在开发中,我们经常需要: 将对象转换成不同形式以适应不同API 在不同业务层中传输对象而不同分层的对象存在不同格式 传统方式的问题: Java反射:性能问题、无法解决嵌套JavaBean映射复制 手工映射:硬编码、代码量大 序列化方式:通过JSON/XML序列化和反序列化,字段参数不同时需要硬编码赋值 2. 常用JavaBean映射技术对比 2.1 BeanUtils Apache和Spring的BeanUtils 原理:使用 java.beans.Introspector 获取属性信息及get/set方法,通过反射进行赋值 特点: Apache支持名称相同但类型不同的属性转换 Spring支持忽略某些属性不进行映射 都设置了缓存保存已解析过的BeanInfo信息 2.2 BeanCopier Cglib的BeanCopier 原理:使用ASM的MethodVisitor直接编写各属性的get/set方法生成class文件 特点:性能比反射方式的BeanUtils高 2.3 Orika 底层使用javassist类库生成Bean映射的字节码 特点: 速度比反射快很多 支持递归映射 能处理对象中的递归引用 3. Orika使用详解 3.1 Maven依赖 3.2 基本映射 3.2.1 字段名相同映射 3.2.2 字段名不同映射 3.3 复杂映射 3.3.1 数组和List映射 3.3.2 相同类型JavaBean复制(原型模式) 3.3.3 嵌套JavaBean映射 3.3.4 List类型映射 3.4 自定义数据类型转换器 3.4.1 String转Date 4. 性能对比 测试方法: 每种方法先预热执行20次 再执行100000次获取每次执行的平均时间 性能排序(从快到慢): Orika(字节码生成) BeanCopier(ASM生成字节码) BeanUtils(反射) 5. 参考资料 官网: http://orika-mapper.github.io/orika-docs/intro.html