一、背景
在开发过程中,经常涉及到对象之间的拷贝,我记得以前用得最多的Spring
框架中自带的一个API
方法是:
BeanUtils.copyProperties(Object source, Object target)
BeanUtils.copyProperties(Object source, Object target)
的含义是将source
的属性值复制到target
,属性为null时也会进行复制。但是据测试,发现BeanUtils.copyProperties
效率还是比较低的,在大数据量情况下会占用大量资源(性能对比)。因此,给大家介绍一款Java Bean
之间对象拷贝的工具Orika
,来提高效率,接下来给大家介绍下Orika相关特性。
二、Orika简介
Orika是一个简单、快速的JavaBean拷贝框架,它能够递归地将数据从一个JavaBean复制到另一个JavaBean,这在多层应用开发中涉及到数据的传输、转换是非常有用的。
- 示例:
Order order = mapper.map(OrderDTO, Order.class);
解释: 将OrderDTO对象拷贝至Order对象中。
三、SpringBoot整合Orika
1. 在SpringBoot项目中,添加Orika依赖
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.4.2</version><!-- or latest version -->
</dependency>
2. OrikaConfig配置
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description: orkia配置
* @author: thinkingcao
* @Date: 2021/04/23 22:56:26
*
*/
@Configuration
public class OrikaConfig {
@Bean
public MapperFactory mapperFactory(){
return new DefaultMapperFactory.Builder().build();
}
@Bean
public MapperFacade mapperFacade(){
return mapperFactory().getMapperFacade();
}
}
3. 完整pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.thinkingcao</groupId>
<artifactId>springboot-orika</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-orika</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.4.2</version><!-- or latest version -->
</dependency>
<!-- swagger2依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- swagger-ui依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
四、如何使用Orika
1. 首先创建两个Bean对象,用于复制使用
- OrderDTO
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class OrderDTO {
@ApiModelProperty(value = "订单ID")
private String orderId;
@ApiModelProperty(value = "订单名称")
private String orderName;
@ApiModelProperty(value = "订单金额")
private BigDecimal orderMoney;
@ApiModelProperty(value = "订单时间")
private Date orderTime;
@ApiModelProperty(value = "订单地址")
private String orderAddress;
}
- OrderResp
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.Date;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class OrderResp {
private String orderId;
private String orderName;
private BigDecimal orderMoney;
private Date orderTime;
private String orderAddress;
}
2. OrderController
import com.thinkingcao.springboot.orika.dto.OrderDTO;
import com.thinkingcao.springboot.orika.entity.OrderResp;
import com.thinkingcao.springboot.orika.result.RestResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
/**
* @desc: 查询订单列表
* @author: cao_wencao
* @date: 2021-04-23 22:59
*/
@Api(value = "查询订单列表API接口",tags="查询订单列表API接口实现")
@Slf4j
@RestController
@RequestMapping(value = "/api/order", produces = "application/json;charset=UTF-8")
public class OrderController {
@Resource
private MapperFacade mapperFacade;
@ApiOperation("查询订单列表")
@GetMapping("queryOrder/{orderId}")
public RestResponse queryOrder(@PathVariable("orderId") String orderId){
OrderDTO orderDTO = new OrderDTO();
orderDTO.setOrderId("20210424231016");
orderDTO.setOrderName("点一杯杨枝甘露");
orderDTO.setOrderTime(new Date());
orderDTO.setOrderMoney(new BigDecimal(10));
orderDTO.setOrderAddress("上海市徐汇区");
//假设上面的orderDTO是通过这一步查询出来的OrderDTO orderDTO = orderService.getOrder(orderId);
OrderResp orderResp = mapperFacade.map(orderDTO,OrderResp.class);
return RestResponse.success("订单列表查询成功",orderResp);
}
}
五、测试接口
- 访问swagger接口: http://127.0.0.1:8080/swagger-ui.html#/
- 访问接口: http://127.0.0.1:8080/api/order/queryOrder/0210424231016
六、项目其他相关类
1. application.yml
spring:
profiles:
active: pro
2. SwaggerConfig
package com.thinkingcao.springboot.orika.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @desc: swagger自动配置类
* @author: cao_wencao
* @date: 2021-03-24 15:54
*/
@Slf4j
@EnableSwagger2
@AllArgsConstructor
@Configuration
@Profile({"dev","test","uat","pro"}) //通过spring.active激活某个环境
public class SwaggerConfig implements WebMvcConfigurer {
@Bean
public Docket apiConfig() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(true)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.thinkingcao.springboot.orika.controller"))
.paths(PathSelectors.any())
.build();
}
//构建api文档的详细信息
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SpringBoot 系列教程(一百零八):SpringBoot整合Orika实体映射工具") //页面标题
.description("restful风格的API接口") //描述
.termsOfServiceUrl("https://thinkingcao.blog.csdn.net/") //url
.version("1.0") //版本号
.build();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(new String[]{"/static/**"}).addResourceLocations(new String[]{"classpath:/static/"});
registry.addResourceHandler(new String[]{"swagger-ui.html"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/"});
registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"});
}
}
3. RestResponse
package com.thinkingcao.springboot.orika.result;
import java.io.Serializable;
/**
* REST接口统一返回数据工具类封装RestResponse
* @param <T>
*/
public class RestResponse<T> implements Serializable {
private static final long serialVersionUID = 3728877563912075885L;
private int code;
private String msg;
private T data;
public RestResponse(){
}
public RestResponse(int code, String message, T data) {
this.code = code;
this.setMsg(message);
this.data = data;
}
public RestResponse(int code, T data) {
this.code = code;
this.data = data;
}
public RestResponse(int code, String message) {
this.code = code;
this.setMsg(message);
}
/**
* 成功时-返回data
* @param <T>
* @return
*/
public static <T> RestResponse<T> success(T data){
return new RestResponse<T>(200, null, data);
}
/**
* 成功-不返回data
* @param <T>
* @return
*/
public static <T> RestResponse<T> success(String msg){
return new RestResponse<T>(200, msg);
}
/**
* 成功-返回data+msg
* @param <T>
* @return
*/
public static <T> RestResponse<T> success(String msg, T data){
return new RestResponse<T>(200, msg, data);
}
/**
* 失败
* @param <T>
* @return
*/
public static <T> RestResponse<T> fail(String msg){
return new RestResponse<T>(500, msg,null);
}
/**
* 失败-code
* @param <T>
* @return
*/
public static <T> RestResponse<T> fail(int code, String msg){
return new RestResponse<T>(code, msg,null);
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
public T getData() {
return data;
}
public void setCode(int code) {
this.code = code;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "RestResponse{" + "code=" + code + ", msg='" + msg + '\'' +", data=" + data +'}';
}
}
七、扩展Orika工具类封装
- OrikaUtil
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import java.util.List;
/**
*
* Orika是一个简单、快速的JavaBean拷贝框架,Orika使用字节代码生成来创建具有最小开销的快速映射器。
*
*/
public class OrikaUtil {
private static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
public static <A, B> ClassMapBuilder<A, B> classMap(Class<A> source, Class<B> target) {
return mapperFactory.classMap(source, target);
}
public static <T> T convert(Object source, Class<T> target) {
return mapperFactory.getMapperFacade().map(source, target);
}
public static <S, D> List<D> convertList(Iterable<S> source, Class<D> target) {
return mapperFactory.getMapperFacade().mapAsList(source, target);
}
}
八、源码
GitHub: https://github.com/Thinkingcao/SpringBootLearning/tree/master/springboot-orika