MapStruct通過一些接口定義,能自動生成實現類,將一個類轉換為另一個類。
引用
<properties>
<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
這個依賴有兩個問題
不支持lombok
的@Data
等注解
如果接口引用的類通過@Data
標記,由於標記生成getter/setter
等方法,但是在生成mapstruct mapper
類時看不到,會導致編譯找不到屬性的問題。
解決方法:引入mapstruct-processor
,同時去掉plugin
中的annotationProcessorPaths
:
...
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.1.Final</version>
<scope>provided</scope>
</dependency>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
...
Spring中無法通過@Autowired
注入接口
這是因為生成的Mapper實現類,沒有被標注@Component
,通過添加maven編譯參數defaultComponentModel
可以解決:
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-Amapstruct.defaultComponentModel=spring</arg>
</compilerArgs>
</configuration>
</plugin>
...
修改以后,我在eclipse中沒有自動重新編譯,需要clean再重新編譯才生效。
引用Mapper
INSTANCE方式
接口中定義一個單例,其他地方引用,官方講的比較清楚:
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
CarDto carToCarDto(Car car);
}
//引用Mapper
Car car = ...;
CarDto dto = CarMapper.INSTANCE.carToCarDto( car );
通過@Autowired
依賴注入
需要前面配置的defaultComponentModel=spring
,才能在代碼中注入:
@Autowired
CarMapper carMapper;
其實上面的配置時全局的,也可以在mapper定義中,增加標記:
@Mapper(componentModel = "spring")
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
CarDto carToCarDto(Car car);
}
這樣也行,只不過需要一個一個的Mapper都添加,不是那么方便了。
使用
可以轉換復雜的結構類型,默認是同名的屬性進行轉換的。
按照官方文檔的說明,生成的Mapper代碼也是盡量符合手寫代碼,通過getter/setter
設置,沒有使用反射,這也是為什么通過lombok
添加getter/setter
會生成不成功的原因。
也支持通過注解,將不同名字的類型進行自動轉換。
我實驗的代碼:
//源類型
@Data
public class TbUserCustomDO {
private List<TbUser> users;
private String fieldA;
private String field2;
}
//TbUser也是一個自定義類
@Data
public class TbUser implements Serializable {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
private Date created;
private Date updated
}
//目的類
@Data
public class TbUserDemoDTO {
private List<TbUserDTO> users;
private String field1;
private String field2;
}
//這里TbUserDTO與源類中的TbUser不完全相同(少一些字段),但是注意每個已有字段名稱是相同的
public class TbUserDTO {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
}
//Mapper類
@Mapper
public interface TbUserCustomConverter {
//這里把fieldA對應到field1,因為名稱不同
@Mapping(source = "fieldA", target = "field1")
TbUserDemoDTO doToDTO(TbUserCustomDO cDo);
}
//注入Mapper
@Autowired
TbUserCustomConverter tbUserCustomConverter;
...
//使用,這里List和屬性都能成功轉換
TbUserCustomDO cDo = ...
TbUserDemoDTO dto = tbUserCustomConverter.doToDTO(cDo);