项目中对象与对象赋值转换使用的频率非常的高,比如数据库表实体对象(Entity)与业务类对象(Model)之间的赋值传递,或者模型对象(Model)与视图对象(ViewModel)之间的赋值传递。如果我们一个一个字段的赋值,将是非常繁琐并且毫无价值的重复工作,此时虽然我们可以自己通过反射提取个公共的方法来处理,但是更高效的方式是查看是否有第三方已经提供了比较成熟稳定的工具包,避免重复造轮子的工作。

在C#中我们一般使用AutoMapper作为对象转换工具(AutoMapper配置使用参考:https://blog.csdn.net/fly_duck/article/details/102605046),Java中也有类似的转换工具,比如:Dozer,Orika,MapStruct,JMapper,ModelMapper以及BeanUtils等,都可以实现对象之间的转换。 本问主要介绍SpringBoot项目中ModelMapper的配置以及使用,使用ModelMapper我们可以快速的解决对象与对象之间的映射转换。官方的介绍如下:

The goal of ModelMapper is to make object mapping easy, by automatically determining how one object model maps to another, based on conventions, in the same way that a human would - while providing a simple, refactoring-safe API for handling specific use cases.

 ModelMapper的Github地址: https://github.com/modelmapper/modelmapper,官方地址:http://modelmapper.org/,官方使用文档:http://modelmapper.org/user-manual/

pom.xml配置

首先在pom.xml引入ModelMapper依赖:

        <!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>2.3.9</version>
        </dependency>

 ModelMapper配置

在SpringBoot项目中将ModelMapper配置好后注册为JavaBean交给Spring来管理,使用的时候直接通过注解获取ModelMapper实例来对对象进行转换。项目中强烈建议将setFullTypeMatchingRequired设置为true使用完全匹配模式,将setMatchingStrategy设置为MatchingStrategies.STRICT使用严格匹配模式,避免字段名缺失被相似字段转换错误的情况。

package com.flyduck.mybatis.config;

import com.flyduck.mybatis.entity.User;
import com.flyduck.mybatis.model.UserModel;
import org.modelmapper.AbstractConverter;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.text.SimpleDateFormat;
import java.util.Date;


@Configuration
public class ModelMapperConfig {

    private Converter<Date, String> dateToStringConverter = new AbstractConverter<Date, String>() {
        @Override
        protected String convert(Date date) {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            return date == null ? null : simpleDateFormat.format(date);
        }
    };

    /**
     * 将ModelMapper注册到Spring中
     * 可以添加一些定制化的配置,官方文档:http://modelmapper.org/user-manual/property-mapping/
     *
     * @author flyduck
     * @date 2020/11/25 21:35
     * @param []
     * @return org.modelmapper.ModelMapper
    */
    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();

        // 官方配置说明: http://modelmapper.org/user-manual/configuration/
        // 完全匹配
        modelMapper.getConfiguration().setFullTypeMatchingRequired(true);

        // 匹配策略使用严格模式
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

        modelMapper.addConverter(dateToStringConverter);

        configureUser(modelMapper);

        return modelMapper;
    }

    private void configureUser(ModelMapper modelMapper) {
        modelMapper.typeMap(UserModel.class, User.class)
                .addMappings(mapper -> mapper.skip(User::setPassword))
                .addMappings(mapper -> mapper.skip(User::setCreateTime))
                .addMappings(mapper -> mapper.skip(User::setUpdateTime))
                .addMappings(mapper -> mapper.map(UserModel::getName, User::setRealName));

        modelMapper.typeMap(User.class, UserModel.class)
                .addMappings(mapper -> mapper.skip(UserModel::setPassword))
                .addMappings(mapper -> mapper.map(User::getRealName, UserModel::setName));
//                .addMappings(mapper -> mapper.using(dateToStringConverter).map(User::getCreateTime, UserModel::setCreateTime))
//                .addMappings(mapper -> mapper.using(dateToStringConverter).map(User::getUpdateTime, UserModel::setUpdateTime));
    }
}

默认情况下,ModelMapper会通过反射按照字段名自动进行对象转换。但是一些特殊情况,需要我们自定义配置,示例配置中我们单独为User和UserModel类之间的转换做了一些常用的配置。 当某个字段不需要转换可以通过skip来设置,当字段名不一致可以通过map来设置,当字段值需要处理后再转换可以通过Converter结合map来设置。User类的定义:

public class User implements Serializable {
    private Long id;
    private String code;
    private Date createTime;
    private Date updateTime;
    private Boolean isDelete;
    private String userName;
    private String email;
    private String phoneNumber;
    private String password;
    private String realName;
    private String nickName;
    private String avatar;

    // 省略getter, setter方法
}

UserModel的定义:

public class UserModel {
    private Long id;
    private String createTime;
    private String updateTime;
    private String userName;
    private String password;
    private String email;
    private String phoneNumber;
    private String name;
    private String nickName;
    private String avatar;

    // 省略 getter,setter方法
}

对象转换

在Controller控制器中,通过@Autowired注解得到ModelMapper实例,然后直接通过modelMapper.map(user, UserModel.class)转换得到VO层的UserModel对象,然后直接传给前端接口。

@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ModelMapper modelMapper;

    @GetMapping("{id}")
    public UserModel get(@PathVariable Long id) {
        User user  = userMapper.selectByPrimaryKey(id);

        if (user == null || user.getIsDelete()) {
            return null;
        }

        UserModel userModel = modelMapper.map(user, UserModel.class);

        return userModel;
    }
}

总结

以上就是SpringBoot项目中ModelMapper的常规使用,示例源码:https://gitee.com/flyduck128/springboot-demo/tree/master/flyduck-mybatis。使用ModelMapper可以大大提高开发人员对于对象转换的开发效率,特别适用于微服务中对象之间的转换。对于ModelMapper的详细配置以及高级使用可以参考官方文档。就如开篇所说的Java中对象转换的工具很多,ModelMapper常规使用配置简单,上手容易,同时也提供一些高级用法,同时由于使用的反射进行转换所以效率不是很高,对于效率要求不高的项目推荐使用ModelMapper。

拓展

国外朋友专门对Dozer,Orika,MapStruct,JMapper和ModelMapper进行了性能测试,测试地址:https://www.baeldung.com/java-performance-mapping-frameworks。测试结果为JMapper和MapStruct性能表现最好。如下是测试结果的部分截图:

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐