SpringBoot2.0實戰 | 第十章:整合MapStruct優雅複製屬性

在之前章節的例子中,我們在接收參數的時候都是使用的 User 類對象,這是一個 DO 對象,
但是這個對象與前端業務通常不能對等,所以更多時候,會創建一個 BO 類用來接收入參。
那麼這時就會涉及到 BO 屬性值複製至 DO,然後代碼裏就會出現大量的 do.set(bo.get())
這時,對反射比較瞭解的同學應該就會嘗試使用反射來解決這個問題,雖然可以解決問題,但是反射會降低程序性能,
而 MapStruct 則是另外一種解決方案。

相關知識

MapStruct官網:http://mapstruct.org

目標

整合 MapStruce 以及 Lombok

準備工作

  • Idea 集成開發環境

安裝 MapStruct 插件

File -> settings,打開 Idea 的設置界面,從左側欄選擇 Plugins 選項,再在右側查詢 MapStruce,點擊安裝。

設置

File -> Settings 打開設置界面,
選擇 Build,Execution,Deployment -> Compiler -> Annotation Processors 進入設置界面,
勾選 enable annotation processing

操作步驟

添加依賴

引入 Spring Boot Starter 父工程

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.5.RELEASE</version>
</parent>

添加 mapstruct 的依賴

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.3.0.Final</version>
</dependency>

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.3.0.Final</version>
    <scope>provided</scope>
</dependency>

添加 mapstructlombok 的插件依賴

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.3.0.Final</version>
            </path>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

整體依賴如下

<dependencies>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
       <exclusions>
           <exclusion>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-tomcat</artifactId>
           </exclusion>
       </exclusions>
   </dependency>

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-undertow</artifactId>
   </dependency>

   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <scope>provided</scope>
   </dependency>
   
   <dependency>
       <groupId>org.mapstruct</groupId>
       <artifactId>mapstruct-jdk8</artifactId>
       <version>1.3.0.Final</version>
   </dependency>
   
   <dependency>
       <groupId>org.mapstruct</groupId>
       <artifactId>mapstruct-processor</artifactId>
       <version>1.3.0.Final</version>
       <scope>provided</scope>
   </dependency>

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <scope>test</scope>
   </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.3.0.Final</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>    
    </plugins>
</build>

編碼

  1. BO 層代碼,用於接收前端入參
@Getter
@Setter
public class UserBO {

    private String mobile;
    private String upwd;

}
  1. MapStruct 接口
  • 類上添加 @Mapper 註解,項目編譯時,MapStruct 將自動爲該接口生成實現類
  • 方法上添加 @Mapping 註解,用於設置規則
  • 設置常量 INSTANCE,供其它類調用,也可以通過 Spring 的 IOC 進行注入
@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "mobile", target = "name")
    User bo2Do(UserBO bo);

}
  1. Controller 層代碼
@RestController
@Slf4j
@AllArgsConstructor
public class UserController {

    @PostMapping("/register")
    public User register(@RequestBody UserBO userBo) {
        User user = UserMapper.INSTANCE.bo2Do(userBo);
        // ... 執行數據庫操作
        return user;
    }

}
  1. 啓動類
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

驗證結果

測試用例

@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = Application.class)
public class UserTest {

    private MockMvc mvc;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Before
    public void setUp() {
        mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void test1() throws Exception {
        MvcResult mvcResult = mvc.perform(
                MockMvcRequestBuilders
                .post("/register")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content("{\"mobile\":\"13700000001\",\"upwd\":\"123456\"}")
        )
        .andDo(MockMvcResultHandlers.print())
        .andReturn();

        Assert.assertEquals(200, mvcResult.getResponse().getStatus());
    }

}

輸出如下

{"id":null,"name":"13700000001","sex":null,"birthday":null,"level":null}

編譯項目

在項目的 target 子目錄下可以找到 UserMapperImpl.class 文件,這個就是 MapStruct 在編譯期自動生成實現類

public class UserMapperImpl implements UserMapper {
    public UserMapperImpl() {
    }

    public User bo2Do(UserBO bo) {
        if (bo == null) {
            return null;
        } else {
            User user = new User();
            user.setName(bo.getMobile());
            return user;
        }
    }
}

源碼地址

本章源碼 : https://gitee.com/gongm_24/spring-boot-tutorial.git

結束語

MapStruct 釋放掉大量的屬性複製的代碼,改爲編譯期自動生成,精簡了項目代碼同時也保證了高性能

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章