文章目录
- 1 工具应用场景
- 2 使用效果
- 3 使用方法
- 3.1 引入jar包
- 3.2 指定lombok与MapStruct执行顺序
- 3.3 定义@Mapper接口
- 3.4 重新打包
- 4 练习手段
1 工具应用场景
BeanUtils.copyProperties拷贝对象同名属性,利用反射原理,耗时长,在对性能要求高的服务中不建议使用;
MapStruct在编译阶段,生成对象拷贝的实现类,和手写拷贝代码相同,不存在反射耗时长问题;
2 使用效果
本次使用在springboot项目中,在定义好mapStruct接口后,直接在使用的类中注入该对象,即可进行同名属性拷贝;
FoodMapStructMapper为定义的接口类;
调用点使用方法如下:
package maplegam.com.gateway.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@Autowired
private FoodMapStructMapper foodMapStructMapper;
@RequestMapping(value="/index", method= RequestMethod.POST)
public String index(@RequestBody String msgBody) {
Milk milk = new Milk();
milk.setName("aaa");
milk.setTaste("good");
milk.setColor("white");
Orange orange = foodMapStructMapper.toOrange(milk);
System.out.println("orange:"+orange.toString());
orange.setPrice("3.3");
orange.setWeight("10kg");
Apple apple = foodMapStructMapper.toApple(orange);
System.out.println("apple:"+apple.toString());
return msgBody;
}
}
打印结果为:
可以看到,orange从milk中拷贝得到了同名属性“name”,apple从orange中拷贝得到了同名属性“name”、“price”;
3 使用方法
3.1 引入jar包
Springboot应用,使用MapStruct工具,需要引入mapStruct包,以及mapStruct-processor预编译包;
引入方法如下:
在pom.xml文件添加依赖包:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
这里的lombok放出来,是因为@Data注解太常用了,同样也是在编译阶段生成代码(主要是Get、Set方法),与MapStruct工具的@Mapper注解存在依赖关系,同时使用时,必须先指定执行@Data注解,生成Get、Set方法后,才能执行@Mapper注解,否则会出现异常。
3.2 指定lombok与MapStruct执行顺序
在pom.xml文件的maven-compiler-plugin插件配置中,添加annotationProcessorPaths标签,来实现指定先后顺序效果;
mapstruct-processor包也在这时指定;
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
3.3 定义@Mapper接口
MapStruct工具的功能很丰富,不仅仅能实现对象间同名属性的拷贝,由于本文只需要能做到替换BeanUtils.copyProperties,所以不对复杂场景进行举例;
同名属性拷贝时,只需要定义接口方法即可,接口类内容如下:
package maplegam.com.gateway.controller;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public interface FoodMapStructMapper {
Apple toApple(Milk milk);
Apple toApple(Orange orange);
Milk toMilk(Apple apple);
Milk toMilk(Orange orange);
Orange toOrange(Apple apple);
Orange toOrange(Milk milk);
}
方法仅需要指定输入的类、输出的类即可,MapStruct工具会生成具体的impl实现类;
componentModel需要指定为“spring”,这样生成的impl类,才会带@Component注解,否则无法通过@Autowired直接注入使用;
接口中的对应的Milk、Apple、Orange类分别为:
(1)Milk类
package maplegam.com.gateway.controller;
import lombok.Data;
@Data
public class Milk {
public String name;
public String taste;
public String color;
}
(2)Apple类
package maplegam.com.gateway.controller;
import lombok.Data;
@Data
public class Apple {
public String name;
public String price;
public String taste;
}
(3)Orange类
package maplegam.com.gateway.controller;
import lombok.Data;
@Data
public class Orange {
public String name;
public String price;
public String weight;
}
3.4 重新打包
需要注意,每次修改MapStruct工具的@Mapper接口类文件,都需要重新编译出包,这样生成的impl类才能和接口内容保持一致;
impl类直接打包至最终的jar包中,在接口类的同级目录下,反编译后能看到内容如下:
package maplegam.com.gateway.controller;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2024-08-06T15:27:27+0800",
comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_371 (Oracle Corporation)"
)
@Component
public class FoodMapStructMapperImpl implements FoodMapStructMapper {
@Override
public Apple toApple(Milk milk) {
if ( milk == null ) {
return null;
}
Apple apple = new Apple();
apple.setName( milk.getName() );
apple.setTaste( milk.getTaste() );
return apple;
}
@Override
public Apple toApple(Orange orange) {
if ( orange == null ) {
return null;
}
Apple apple = new Apple();
apple.setName( orange.getName() );
apple.setPrice( orange.getPrice() );
return apple;
}
@Override
public Milk toMilk(Apple apple) {
if ( apple == null ) {
return null;
}
Milk milk = new Milk();
milk.setName( apple.getName() );
milk.setTaste( apple.getTaste() );
return milk;
}
@Override
public Milk toMilk(Orange orange) {
if ( orange == null ) {
return null;
}
Milk milk = new Milk();
milk.setName( orange.getName() );
return milk;
}
@Override
public Orange toOrange(Apple apple) {
if ( apple == null ) {
return null;
}
Orange orange = new Orange();
orange.setName( apple.getName() );
orange.setPrice( apple.getPrice() );
return orange;
}
@Override
public Orange toOrange(Milk milk) {
if ( milk == null ) {
return null;
}
Orange orange = new Orange();
orange.setName( milk.getName() );
return orange;
}
}
可以看到,impl类带着@Component注解,至此操作结束。
4 练习手段
可以参考“3 使用方法”章节,在自己的项目中尝试用MapStruct工具,替换原本使用BeanUtils.copyProperties的地方,提升系统运行效率。