- MapStructPlus 版本由 1.5.3.Final 升级为 1.5.5.Final
- 增加自定义 ConvertMapperAdapter 和 MapConvertMapperAdapter 类名和包名的功能
- 生成的转换接口,自动接入自定义转换接口
This commit is contained in:
linpeilie 2023-04-24 16:18:25 +08:00
parent 52a7952297
commit 34fa59d985
18 changed files with 358 additions and 26 deletions

View File

@ -50,7 +50,7 @@ copyright: false
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>1.2.2</version>
<version>1.2.3</version>
</dependency>
```
@ -62,10 +62,11 @@ implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-s
## 更新日志
### 1.2.2
### 1.2.3
- fixbug: 定义多个uses时的问题
- feature: 增加 `@AutoEnumMapper` 注解,可以在类型转换时,自动转换枚举
- MapStructPlus 版本由 `1.5.3.Final` 升级为 `1.5.5.Final`
- 增加自定义 `ConvertMapperAdapter``MapConvertMapperAdapter` 类名和包名的功能
- 生成的转换接口,自动接入自定义转换接口
……

View File

@ -349,6 +349,140 @@ public class User {
}
```
## 自动接入自定义转换接口
::: info
since `1.2.3`
:::
当有的类型转换逻辑比较复杂,可以通过自定义转换接口来实现,即使用 MapStruct 原生的方式。
当使用这种方式时,默认生成的类型转换中,如果有前面提供的类型转换时,会自动引用。
例如:
::: code-tabs#java
@tab Car
```java
@AutoMapper(target = CarDto.class)
@Data
public class Car {
private Tyre tyre;
}
```
@tab CarDto
```java
@Data
public class CarDto {
private TyreDTO tyre;
}
```
:::
这里定义 `Tyre``TyreDTO` 之间的转换接口:
```java
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface TyreMapper {
TyreDTO tyreToTyreDTO(Tyre tyre);
Tyre tyreDtoToTyre(TyreDTO tyreDTO);
}
```
生成的 `Car``CarDto` 转换接口的实现类如下:
::: code-tabs#java
@tab CarToCarDtoMapperImpl
```java
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-04-24T15:38:48+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
@Component
public class CarToCarDtoMapperImpl implements CarToCarDtoMapper {
@Autowired
private TyreMapper tyreMapper;
@Override
public CarDto convert(Car source) {
if ( source == null ) {
return null;
}
CarDto carDto = new CarDto();
carDto.setTyre( tyreMapper.tyreToTyreDTO( source.getTyre() ) );
return carDto;
}
@Override
public CarDto convert(Car source, CarDto target) {
if ( source == null ) {
return target;
}
target.setTyre( tyreMapper.tyreToTyreDTO( source.getTyre() ) );
return target;
}
}
```
@tab CarDtoToCarMapperImpl
```java
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-04-24T15:38:49+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
@Component
public class CarDtoToCarMapperImpl implements CarDtoToCarMapper {
@Autowired
private TyreMapper tyreMapper;
@Override
public Car convert(CarDto source) {
if ( source == null ) {
return null;
}
Car car = new Car();
car.setTyre( tyreMapper.tyreDtoToTyre( source.getTyre() ) );
return car;
}
@Override
public Car convert(CarDto source, Car target) {
if ( source == null ) {
return target;
}
target.setTyre( tyreMapper.tyreDtoToTyre( source.getTyre() ) );
return target;
}
}
```
:::
## 反向属性映射配置
::: info

View File

@ -0,0 +1,86 @@
---
title: 配置项
order: 6
category:
- 指南
description: MapStructPlus MapStructPlus配置项 configuration
---
MapStructPlus 提供了多个配置项,来指定生成转换接口时的一些行为。
## 使用方式
在需要进行配置的模块中,新建配置类,在该类上面增加注解:`@MapperConfig`,在一个模块中,只能有一个有该注解的类。
例如:
```java
@MapperConfig(adapterClassName = "DemoConvertMapperAdapter",
adapterPackage = "io.github.linpeilie.adapter",
mapAdapterClassName = "DemoMapConvertMapperAdapter")
public class MapStructPlusConfiguration {
}
```
## 配置项
### mapperPackage
- **说明**:生成的 Mapper 转换接口的包名
- **类型**`String`
- **默认值**:默认生成在要转换的类同包名下
### unmappedSourcePolicy
- **说明**:当来源类中没有对应属性时的策略,默认忽略
- **类型**`ReportingPolicy`
- **可选项**
- `IGNORE`:忽略
- `WARN`:打印警告日志
- `ERROR`:抛出异常
- **默认值**`IGNORE`
### unmappedTargetPolicy
- **说明**:当目标类中没有对应属性时的策略,默认忽略
- **类型**`ReportingPolicy`
- **可选项**
- `IGNORE`:忽略
- `WARN`:打印警告日志
- `ERROR`:抛出异常
- **默认值**`IGNORE`
### builder
- **说明**构造者模式配置MapStruct 与 lombok 的 builder 一起使用时,会丢失父类属性,所以这里将默认使用构造者模式关闭
- **类型**`Builder`
- **支持配置项**
- `buildMethod`:构造器创建要构建类型时的构造方法
- `disableBuilder`:打开/关闭构造器,如果关闭,则只使用常规的 getters/setters
- **默认值**
- `buildMethod``build`
- `disableBuilder``true`
### adapterPackage
> since `1.2.3`
- **说明**ConvertAdapterClass 和 MapConvertMapperAdapter 的包名
- **类型**`String`
- **默认值**io.github.linpeilie
### adapterClassName
> since `1.2.3`
- **说明**ConvertAdapterClass 类名
- **类型**`String`
- **默认值**ConvertMapperAdapter
### mapAdapterClassName
> since `1.2.3`
- **说明**MapConvertMapperAdapter 类名
- **类型**`String`
- **默认值**MapConvertMapperAdapter

View File

@ -1,6 +1,6 @@
---
title: 常见问题
order: 6
order: 7
category:
- 指南
description: MapStructPlus MapStructPlus常见问题 faq

View File

@ -6,6 +6,12 @@ category:
description: MapStructPlus release log
---
### 1.2.3
- MapStructPlus 版本由 `1.5.3.Final` 升级为 `1.5.5.Final`
- 增加自定义 `ConvertMapperAdapter``MapConvertMapperAdapter` 类名和包名的功能
- 生成的转换接口,自动接入自定义转换接口,具体[详见](/guide/class-convert.html#自动接入自定义转换接口)
### 1.2.2
- fixbug: 定义多个uses时的问题

View File

@ -18,7 +18,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mapstruct.version>1.5.1.Final</mapstruct.version>
<mapstruct-plus.version>1.2.2</mapstruct-plus.version>
<mapstruct-plus.version>1.2.3</mapstruct-plus.version>
<lombok.version>1.18.22</lombok.version>
</properties>

View File

@ -0,0 +1,9 @@
package io.github.linpeilie;
import io.github.linpeilie.annotations.MapperConfig;
@MapperConfig(adapterClassName = "DemoConvertMapperAdapter",
adapterPackage = "io.github.linpeilie.adapter",
mapAdapterClassName = "DemoMapConvertMapperAdapter")
public class MapStructPlusConfiguration {
}

View File

@ -0,0 +1,15 @@
package io.github.linpeilie.mapper;
import io.github.linpeilie.model.Tyre;
import io.github.linpeilie.model.TyreDTO;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface TyreMapper {
TyreDTO tyreToTyreDTO(Tyre tyre);
Tyre tyreDtoToTyre(TyreDTO tyreDTO);
}

View File

@ -11,6 +11,7 @@ public class Car {
private String make;
private SeatConfiguration seatConfiguration;
private CarType type;
private Tyre tyre;
@AutoMapping(target = "wheels", ignore = true)
private Wheels wheels;

View File

@ -9,5 +9,6 @@ public class CarDto {
private SeatConfigurationDto seatConfiguration;
private String type;
private List<WheelDto> wheels;
private TyreDTO tyre;
}

View File

@ -0,0 +1,4 @@
package io.github.linpeilie.model;
public class Tyre {
}

View File

@ -0,0 +1,4 @@
package io.github.linpeilie.model;
public class TyreDTO {
}

View File

@ -29,7 +29,6 @@ import io.github.linpeilie.processor.metadata.AutoMapperMetadata;
import io.github.linpeilie.processor.metadata.AutoMappingMetadata;
import java.io.IOException;
import java.io.Writer;
import java.lang.annotation.ElementType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -44,12 +43,10 @@ import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.MirroredTypesException;
@ -57,18 +54,19 @@ import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.MappingConstants;
import org.mapstruct.ReportingPolicy;
import static io.github.linpeilie.processor.Constants.AUTO_ENUM_MAPPER_ANNOTATION;
import static io.github.linpeilie.processor.Constants.AUTO_MAPPERS_ANNOTATION;
import static io.github.linpeilie.processor.Constants.AUTO_MAPPER_ANNOTATION;
import static io.github.linpeilie.processor.Constants.AUTO_MAP_MAPPER_ANNOTATION;
import static io.github.linpeilie.processor.Constants.COMPONENT_MODEL_CONFIG_ANNOTATION;
import static io.github.linpeilie.processor.Constants.MAPPER_ANNOTATION;
import static io.github.linpeilie.processor.Constants.MAPPER_CONFIG_ANNOTATION;
import static javax.tools.Diagnostic.Kind.ERROR;
@SupportedAnnotationTypes({AUTO_MAPPER_ANNOTATION, AUTO_MAPPERS_ANNOTATION, AUTO_MAP_MAPPER_ANNOTATION,
AUTO_ENUM_MAPPER_ANNOTATION, MAPPER_CONFIG_ANNOTATION, COMPONENT_MODEL_CONFIG_ANNOTATION})
AUTO_ENUM_MAPPER_ANNOTATION, MAPPER_CONFIG_ANNOTATION, COMPONENT_MODEL_CONFIG_ANNOTATION,
MAPPER_ANNOTATION})
public class AutoMapperProcessor extends AbstractProcessor {
private static final ClassName MAPPING_DEFAULT_TARGET = ClassName.get("io.github.linpeilie", "DefaultMapping");
@ -85,6 +83,8 @@ public class AutoMapperProcessor extends AbstractProcessor {
private final List<AutoMapperMetadata> mapperList = new ArrayList<>();
private final List<TypeMirror> customMapperList = new ArrayList<>();
private final Set<String> mapperSet = new HashSet<>();
public AutoMapperProcessor() {
@ -112,6 +112,10 @@ public class AutoMapperProcessor extends AbstractProcessor {
return MAPPER_CONFIG_ANNOTATION.contentEquals(annotation.getQualifiedName());
}
private boolean isMapperAnnotation(TypeElement annotation) {
return MAPPER_ANNOTATION.contentEquals(annotation.getQualifiedName());
}
private boolean isComponentModelConfigAnnotation(TypeElement annotation) {
return COMPONENT_MODEL_CONFIG_ANNOTATION.contentEquals(annotation.getQualifiedName());
}
@ -156,12 +160,23 @@ public class AutoMapperProcessor extends AbstractProcessor {
.findFirst()
.ifPresent(annotation -> processAutoMappersAnnotation(roundEnv, annotation));
// custom mapper
annotations.stream()
.filter(this::isMapperAnnotation)
.findFirst()
.ifPresent(annotation -> processMapperAnnotation(roundEnv, annotation));
// 生成类
generateMapper();
return false;
}
private void processMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
roundEnv.getElementsAnnotatedWith(annotation)
.forEach(element -> customMapperList.add(element.asType()));
}
private void processAutoEnumMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
roundEnv.getElementsAnnotatedWith(annotation)
.stream()
@ -268,7 +283,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
AutoMapperProperties.getMapAdapterClassName());
mapperConfigGenerator.write(processingEnv, AutoMapperProperties.getMapConfigClassName(),
AutoMapperProperties.getMapAdapterClassName());
AutoMapperProperties.getMapAdapterClassName(), null);
}
private void refreshProperties(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
@ -286,6 +301,15 @@ public class AutoMapperProcessor extends AbstractProcessor {
AutoMapperProperties.setUnmappedTargetPolicy(mapperConfig.unmappedTargetPolicy());
AutoMapperProperties.setBuildMethod(mapperConfig.builder().buildMethod());
AutoMapperProperties.setDisableBuilder(mapperConfig.builder().disableBuilder());
if (StrUtil.isNotEmpty(mapperConfig.adapterPackage())) {
AutoMapperProperties.setAdapterPackage(mapperConfig.adapterPackage());
}
if (StrUtil.isNotEmpty(mapperConfig.adapterClassName())) {
AutoMapperProperties.setAdapterClassName(mapperConfig.adapterClassName());
}
if (StrUtil.isNotEmpty(mapperConfig.mapAdapterClassName())) {
AutoMapperProperties.setMapAdapterClassName(mapperConfig.mapAdapterClassName());
}
});
annotations.stream()
.filter(this::isComponentModelConfigAnnotation)
@ -355,7 +379,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
adapterMapperGenerator.write(processingEnv, methodMap.values(), AutoMapperProperties.getAdapterClassName());
mapperConfigGenerator.write(processingEnv, AutoMapperProperties.getConfigClassName(),
AutoMapperProperties.getAdapterClassName());
AutoMapperProperties.getAdapterClassName(), customMapperList);
}
private AutoMapperMetadata reverseMapper(AutoMapperMetadata autoMapperMetadata) {

View File

@ -19,20 +19,38 @@ public class AutoMapperProperties {
private static boolean disableBuilder = true;
private static String adapterPackage = DEFAULT_BASE_PACKAGE;
private static String adapterClassName = DEFAULT_ADAPTER_CLASS_NAME;
private static String mapAdapterClassName = DEFAULT_MAP_ADAPTER_CLASS_NAME;
public static String getMapperPackage() {
return mapperPackage;
}
public static String getAdapterPackage() {
return DEFAULT_BASE_PACKAGE;
return adapterPackage;
}
public static void setAdapterPackage(final String adapterPackage) {
AutoMapperProperties.adapterPackage = adapterPackage;
}
public static String getAdapterClassName() {
return DEFAULT_ADAPTER_CLASS_NAME;
return adapterClassName;
}
public static void setAdapterClassName(final String adapterClassName) {
AutoMapperProperties.adapterClassName = adapterClassName;
}
public static String getMapAdapterClassName() {
return DEFAULT_MAP_ADAPTER_CLASS_NAME;
return mapAdapterClassName;
}
public static void setMapAdapterClassName(final String mapAdapterClassName) {
AutoMapperProperties.mapAdapterClassName = mapAdapterClassName;
}
public static String getConfigPackage() {

View File

@ -26,6 +26,8 @@ public class Constants {
public static final String MAPPER_CONFIG_ANNOTATION = "io.github.linpeilie.annotations.MapperConfig";
public static final String MAPPER_ANNOTATION = "org.mapstruct.Mapper";
public static final String COMPONENT_MODEL_CONFIG_ANNOTATION = "io.github.linpeilie.annotations.ComponentModelConfig";
public static final String BASE_MAPPER_PACKAGE = "io.github.linpeilie";

View File

@ -1,5 +1,6 @@
package io.github.linpeilie.processor.generator;
import cn.hutool.core.collection.CollectionUtil;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
@ -8,18 +9,20 @@ import com.squareup.javapoet.TypeSpec;
import io.github.linpeilie.processor.AutoMapperProperties;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeMirror;
import static javax.tools.Diagnostic.Kind.ERROR;
public class MapperConfigGenerator {
public void write(ProcessingEnvironment processingEnv, String mapstructConfigName, String adapterClassName) {
public void write(ProcessingEnvironment processingEnv, String mapstructConfigName, String adapterClassName, List<TypeMirror> uses) {
try (final Writer writer = processingEnv.getFiler()
.createSourceFile(AutoMapperProperties.getConfigPackage() + "." + mapstructConfigName)
.openWriter()) {
JavaFile.builder(AutoMapperProperties.getConfigPackage(), createConfigTypeSpec(mapstructConfigName, adapterClassName)).build().writeTo(writer);
JavaFile.builder(AutoMapperProperties.getConfigPackage(), createConfigTypeSpec(mapstructConfigName, adapterClassName, uses)).build().writeTo(writer);
} catch (IOException e) {
processingEnv.getMessager()
.printMessage(ERROR,
@ -28,17 +31,23 @@ public class MapperConfigGenerator {
}
}
private TypeSpec createConfigTypeSpec(final String mapstructConfigName, final String adapterClassName) {
private TypeSpec createConfigTypeSpec(final String mapstructConfigName,
final String adapterClassName,
final List<TypeMirror> uses) {
return TypeSpec.interfaceBuilder(mapstructConfigName)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(buildMapperConfigAnnotationSpec(adapterClassName))
.addAnnotation(buildMapperConfigAnnotationSpec(adapterClassName, uses))
.build();
}
private AnnotationSpec buildMapperConfigAnnotationSpec(final String adapterClassName) {
private AnnotationSpec buildMapperConfigAnnotationSpec(final String adapterClassName, final List<TypeMirror> uses) {
CodeBlock.Builder usesCodeBuilder = CodeBlock.builder().add("{");
usesCodeBuilder.add("$T.class",
ClassName.get(AutoMapperProperties.getAdapterPackage(), adapterClassName));
usesCodeBuilder.add("$T.class", ClassName.get(AutoMapperProperties.getAdapterPackage(), adapterClassName));
if (CollectionUtil.isNotEmpty(uses)) {
uses.forEach(use -> {
usesCodeBuilder.add(", $T.class", use);
});
}
CodeBlock usesCodeBlock = usesCodeBuilder.add("}").build();
final AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassName.get("org.mapstruct", "MapperConfig"))
.addMember("componentModel",

View File

@ -31,7 +31,7 @@ public @interface MapperConfig {
ReportingPolicy unmappedSourcePolicy() default ReportingPolicy.IGNORE;
/**
* 来源类中没有对应属性时的策略默认忽略
* 目标类中没有对应属性时的策略默认忽略
* @return {@link ReportingPolicy}
*/
ReportingPolicy unmappedTargetPolicy() default ReportingPolicy.IGNORE;
@ -42,4 +42,22 @@ public @interface MapperConfig {
*/
Builder builder() default @Builder(disableBuilder = true);
/**
* 默认包名为io.github.linpeilie
* @return ConvertAdapterClass 包名
*/
String adapterPackage() default "";
/**
* 默认类名为ConvertMapperAdapter
* @return ConvertAdapterClass 类名
*/
String adapterClassName() default "";
/**
* 默认类名为MapConvertMapperAdapter
* @return MapConvertAdapterClass 类名
*/
String mapAdapterClassName() default "";
}

View File

@ -17,11 +17,11 @@
</modules>
<properties>
<mapstruct-plus.version>1.2.2</mapstruct-plus.version>
<mapstruct-plus.version>1.2.3</mapstruct-plus.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<hutool.version>5.8.15</hutool.version>
<projectUrl>https://github.com/linpeilie/mapstruct-plus.git</projectUrl>
</properties>