在项目中,常常会遇到从数据库读取数据后不能直接返回给前端展示的情况,因为还需要对字段进行加工,比如去除时间戳记录、隐藏敏感数据等。传统的处理方式是创建一个新类,然后编写大量的 get/set 方法进行赋值,若字段很多,这无疑是一场噩梦,而且还容易出现遗漏的情况。
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>17</java.version> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <lombok.version>1.18.34</lombok.version> <mapstruct.version>1.6.2</mapstruct.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${mapstruct.version}</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${mapstruct.version}</version> <scope>provider</scope> </dependency> </dependencies>org.mapstruct:mapstruct
这个依赖是MapStruct的注解处理器(Annotation Processor)。在Java编译过程中,它会查找带有MapStruct注解(来自org.mapstruct:mapstruct库)的接口或抽象类。一旦找到这样的接口或抽象类,它会根据定义的映射关系(通过@Mapper、@Mapping等注解)生成具体的映射实现类。这个生成过程是在编译时进行的,生成的代码会被编译到最终的字节码中。
import java.util.Date; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class User { private long id; private String name; private int age; private String password; private Date createTime; }
import lombok.Data; @Data public class UserVO { private Long id; private String name; private Integer age; private String code; private String hello; private String createTime; }定义转换的mapper
package com.zxy.demo; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { public static final UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); // 堆代码 duidaima.com static String hello(User user) { return "hello " + user.getName(); } @Mapping(target = "createTime", source = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss") @Mapping(target = "code", expression="java(\"xx-\" + user.getId())") @Mapping(target = "hello", expression = "java(UserMapper.hello(user))") UserVO toUserVO(User user); }
如果所有的字段都一样,用@Mappings({})。不一样的用target+source,需要特殊处理的可以用expression。在 MapStruct 中,expression是一个强大的功能,用于在对象映射过程中执行自定义的表达式。它允许开发人员在映射规则中使用 Java 表达式来处理复杂的映射逻辑,而不仅仅是简单的属性到属性的映射。
这在源对象和目标对象的属性之间存在复杂关系,或者需要进行额外的计算、逻辑判断等情况时非常有用。
package com.zxy.demo; import java.util.Date; import org.junit.Assert; import org.junit.Test; public class UserTest { @Test public void ok() { User u = new User(); u.setId(1); u.setAge(10); u.setName("zxy"); u.setPassword("123456"); u.setCreateTime(new Date()); Assert.assertEquals(10, u.getAge()); Assert.assertEquals("zxy", u.getName()); System.out.println(new User(1, "zxy", 12, "123456", new Date())); UserVO vo = UserMapper.INSTANCE.toUserVO(u); System.out.println("vo: "+vo); Assert.assertEquals("zxy", vo.getName()); Assert.assertEquals("xx-1", vo.getCode()); Assert.assertEquals("hello zxy", vo.getHello()); } }简单看一下生成的代码
// Source code is unavailable, and was generated by the Fernflower decompiler. package com.zxy.demo; import java.text.SimpleDateFormat; // 堆代码 duidaima.com public class UserMapperImpl implements UserMapper { public UserVO toUserVO(User user) { if (user == null) { return null; } else { UserVO userVO = new UserVO(); if (user.getCreateTime() != null) { userVO.setCreateTime((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(user.getCreateTime())); } userVO.setId(user.getId()); userVO.setName(user.getName()); userVO.setAge(user.getAge()); userVO.setCode("xx-" + user.getId()); userVO.setHello(UserMapper.hello(user)); return userVO; } } }