mirror of
https://gitee.com/easii/mapstruct-plus.git
synced 2025-12-07 01:28:31 +08:00
commit
3a9206cdeb
@ -61,7 +61,7 @@ public class User {
|
||||
|
||||
```xml
|
||||
<properties>
|
||||
<mapstruct-plus.version>1.3.1</mapstruct-plus.version>
|
||||
<mapstruct-plus.version>1.3.2</mapstruct-plus.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
@ -56,27 +56,22 @@ copyright: false
|
||||
<dependency>
|
||||
<groupId>io.github.linpeilie</groupId>
|
||||
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
|
||||
<version>1.3.1</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- gradle
|
||||
|
||||
```groovy
|
||||
implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.3.1'
|
||||
implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.3.2'
|
||||
```
|
||||
|
||||
## 更新日志
|
||||
|
||||
### 1.3.1
|
||||
### 1.3.2
|
||||
|
||||
- 增加编译参数中指定配置类的功能
|
||||
- 更好地适配 IDEA 部分编译场景
|
||||
|
||||
### 1.3.0
|
||||
|
||||
- fix: 解决本地开发时 IDEA 编译或者运行时报错等与预期不一致的问题
|
||||
- feature: AutoMapper 注解增加 imports 属性支持
|
||||
- 不可变对象支持,可以使用任意包下的 `Immutable` 标注类型为不可变类
|
||||
- 全面适配 IDEA 部分编译问题,使用更加流畅丝滑
|
||||
|
||||
……
|
||||
|
||||
|
||||
@ -514,3 +514,17 @@ public class CarDtoToCarMapperImpl implements CarDtoToCarMapper {
|
||||
|
||||
如果还是不理解,这里可以认为,该注解就是本该应用在目标类中的 `@AutoMapping` 注解,原封不动拷贝到当前类,再修改注解名称即可。
|
||||
|
||||
## 不可变类型设计
|
||||
|
||||
> since 1.3.2
|
||||
|
||||
当一个类型是不可变类型时,之前默认的规则,生成的 `T convert(S source, @MappingTarget T target)` 可能会存在问题。
|
||||
|
||||
所以,可以使用任意包下的 `Immutable` 注解,标识一个类为不可变类型,
|
||||
当为不可变类型时,`@MappingTarget` 没有意义,上面的方法最终生成如下:
|
||||
|
||||
```java
|
||||
public T convert(S source, @MappingTarget T target) {
|
||||
return target;
|
||||
}
|
||||
```
|
||||
@ -23,10 +23,13 @@ public class MapStructPlusConfiguration {
|
||||
}
|
||||
```
|
||||
|
||||
:::warning
|
||||
**当使用该方式配置时,强烈建议,在编译参数中,指定配置类为当前类,以解决IDEA部分编译场景时出现的各种问题,该功能从 1.3.1 开始支持**
|
||||
:::info
|
||||
|
||||
配置时,需要在启动参数中添加 `-Amapstruct.plus.mapperConfigClass` 参数,该参数的值为配置类的全路径名称:
|
||||
1.3.2 已彻底适配 IDEA 部分编译,无需再添加如下配置。
|
||||
|
||||
~~当使用该方式配置时,强烈建议,在编译参数中,指定配置类为当前类,以解决IDEA部分编译场景时出现的各种问题,该功能从 1.3.1 开始支持
|
||||
|
||||
配置时,需要在启动参数中添加 `-Amapstruct.plus.mapperConfigClass` 参数,该参数的值为配置类的全路径名称~~:
|
||||
|
||||
```xml
|
||||
<plugin>
|
||||
|
||||
@ -6,6 +6,11 @@ category:
|
||||
description: MapStructPlus release log
|
||||
---
|
||||
|
||||
### 1.3.2
|
||||
|
||||
- 不可变对象支持,可以使用任意包下的 `Immutable` 标注类型为不可变类
|
||||
- 全面适配 IDEA 部分编译问题,使用更加流畅丝滑
|
||||
|
||||
### 1.3.1
|
||||
|
||||
- 增加编译参数中指定配置类的功能
|
||||
|
||||
@ -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.3.1</mapstruct-plus.version>
|
||||
<mapstruct-plus.version>1.3.2</mapstruct-plus.version>
|
||||
<lombok.version>1.18.22</lombok.version>
|
||||
</properties>
|
||||
|
||||
|
||||
@ -20,4 +20,14 @@ public class Goods {
|
||||
|
||||
private GoodsTypeEnum type;
|
||||
|
||||
private String f1;
|
||||
|
||||
private String f2;
|
||||
|
||||
private String f3;
|
||||
|
||||
private String f4;
|
||||
|
||||
private String f5;
|
||||
|
||||
}
|
||||
|
||||
@ -19,4 +19,14 @@ public class GoodsDto {
|
||||
|
||||
private int type;
|
||||
|
||||
private String f1;
|
||||
|
||||
private String f2;
|
||||
|
||||
private String f3;
|
||||
|
||||
private String f4;
|
||||
|
||||
private String f5;
|
||||
|
||||
}
|
||||
|
||||
@ -23,12 +23,6 @@ public abstract class AbstractAdapterMapperGenerator {
|
||||
public void write(ProcessingEnvironment processingEnv,
|
||||
Collection<AbstractAdapterMethodMetadata> adapterMethods,
|
||||
String adapterClassName) {
|
||||
final TypeElement typeElement =
|
||||
processingEnv.getElementUtils().getTypeElement(adapterPackage() + "." + adapterClassName);
|
||||
if (typeElement != null) {
|
||||
System.out.println("adapter class existed");
|
||||
return;
|
||||
}
|
||||
// write Adapter
|
||||
try (final Writer writer = processingEnv.getFiler()
|
||||
.createSourceFile(adapterPackage() + "." + adapterClassName)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package io.github.linpeilie.processor;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
@ -39,10 +40,14 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.Filer;
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedOptions;
|
||||
@ -55,7 +60,6 @@ import javax.lang.model.type.MirroredTypeException;
|
||||
import javax.lang.model.type.MirroredTypesException;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.tools.Diagnostic;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mapstruct.MappingConstants;
|
||||
|
||||
import static io.github.linpeilie.processor.Constants.AUTO_ENUM_MAPPER_ANNOTATION;
|
||||
@ -65,8 +69,18 @@ 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 io.github.linpeilie.processor.ProcessorOptions.ADAPTER_CLASS_NAME;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.ADAPTER_PACKAGE;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.BUILDER_BUILD_METHOD;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.BUILDER_DISABLE_BUILDER;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.MAPPER_CONFIG_CLASS;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.MAPPER_PACKAGE;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.MAP_ADAPTER_CLASS_NAME;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.NULL_VALUE_MAPPING_STRATEGY;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.NULL_VALUE_PROPERTY_MAPPING_STRATEGY;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.UNMAPPED_SOURCE_POLICY;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.UNMAPPED_TARGET_POLICY;
|
||||
import static javax.tools.Diagnostic.Kind.ERROR;
|
||||
import static io.github.linpeilie.processor.ProcessorOptions.*;
|
||||
|
||||
@SupportedAnnotationTypes({AUTO_MAPPER_ANNOTATION, AUTO_MAPPERS_ANNOTATION, AUTO_MAP_MAPPER_ANNOTATION,
|
||||
AUTO_ENUM_MAPPER_ANNOTATION, MAPPER_CONFIG_ANNOTATION, COMPONENT_MODEL_CONFIG_ANNOTATION,
|
||||
@ -104,6 +118,10 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
|
||||
private final Set<String> mapperSet = new HashSet<>();
|
||||
|
||||
private Messager messager;
|
||||
|
||||
private Filer filer;
|
||||
|
||||
public AutoMapperProcessor() {
|
||||
this.mapperGenerator = new AutoMapperGenerator();
|
||||
this.mapperConfigGenerator = new MapperConfigGenerator();
|
||||
@ -137,14 +155,32 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
return COMPONENT_MODEL_CONFIG_ANNOTATION.contentEquals(annotation.getQualifiedName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void init(final ProcessingEnvironment processingEnv) {
|
||||
super.init(processingEnv);
|
||||
messager = processingEnv.getMessager();
|
||||
filer = processingEnv.getFiler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
|
||||
try {
|
||||
doProcess(annotations, roundEnv);
|
||||
} catch (Exception e) {
|
||||
messager.printMessage(ERROR, ExceptionUtil.stacktraceToString(e));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void doProcess(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
|
||||
boolean hasAutoMapper = annotations.stream().anyMatch(this::isAutoMapperAnnotation);
|
||||
final boolean hasAutoMapMapper = annotations.stream().anyMatch(this::isAutoMapMapperAnnotation);
|
||||
final boolean hasAutoEnumMapper = annotations.stream().anyMatch(this::isAutoEnumMapperAnnotation);
|
||||
final boolean hasAutoMapMappers = annotations.stream().anyMatch(this::isAutoMappersAnnotation);
|
||||
if (!hasAutoMapper && !hasAutoMapMapper && !hasAutoEnumMapper && !hasAutoMapMappers) {
|
||||
return false;
|
||||
final boolean hasMapperConfig = annotations.stream().anyMatch(this::isMapperConfigAnnotation);
|
||||
if (!hasAutoMapper && !hasAutoMapMapper && !hasAutoEnumMapper && !hasAutoMapMappers && !hasMapperConfig) {
|
||||
return;
|
||||
}
|
||||
// 刷新配置
|
||||
refreshProperties(annotations, roundEnv);
|
||||
@ -162,49 +198,57 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
// AutoMapMapper
|
||||
annotations.stream()
|
||||
.filter(this::isAutoMapMapperAnnotation)
|
||||
.findFirst()
|
||||
.ifPresent(annotation -> processAutoMapMapperAnnotation(roundEnv, annotation));
|
||||
final TypeElement autoMapMapperAnnotation =
|
||||
processingEnv.getElementUtils().getTypeElement(AUTO_MAP_MAPPER_ANNOTATION);
|
||||
processAutoMapMapperAnnotation(roundEnv, autoMapMapperAnnotation);
|
||||
|
||||
// AutoEnumMapper
|
||||
annotations.stream()
|
||||
.filter(this::isAutoEnumMapperAnnotation)
|
||||
.findFirst()
|
||||
.ifPresent(annotation -> processAutoEnumMapperAnnotation(roundEnv, annotation));
|
||||
final TypeElement autoEnumMapperAnnotation =
|
||||
processingEnv.getElementUtils().getTypeElement(AUTO_ENUM_MAPPER_ANNOTATION);
|
||||
processAutoEnumMapperAnnotation(roundEnv, autoEnumMapperAnnotation);
|
||||
|
||||
// AutoMapper
|
||||
annotations.stream()
|
||||
.filter(this::isAutoMapperAnnotation)
|
||||
.findFirst()
|
||||
.ifPresent(annotation -> processAutoMapperAnnotation(roundEnv, annotation));
|
||||
final TypeElement autoMapperAnnotation = processingEnv.getElementUtils().getTypeElement(AUTO_MAPPER_ANNOTATION);
|
||||
processAutoMapperAnnotation(roundEnv, autoMapperAnnotation);
|
||||
|
||||
// AutoMappers
|
||||
annotations.stream()
|
||||
.filter(this::isAutoMappersAnnotation)
|
||||
.findFirst()
|
||||
.ifPresent(annotation -> processAutoMappersAnnotation(roundEnv, annotation));
|
||||
final TypeElement autoMappersAnnotation =
|
||||
processingEnv.getElementUtils().getTypeElement(AUTO_MAPPERS_ANNOTATION);
|
||||
processAutoMappersAnnotation(roundEnv, autoMappersAnnotation);
|
||||
|
||||
// custom mapper
|
||||
annotations.stream()
|
||||
.filter(this::isMapperAnnotation)
|
||||
.findFirst()
|
||||
.ifPresent(annotation -> processMapperAnnotation(roundEnv, annotation));
|
||||
final TypeElement mapperAnnotation = processingEnv.getElementUtils().getTypeElement(MAPPER_ANNOTATION);
|
||||
processMapperAnnotation(roundEnv, mapperAnnotation);
|
||||
|
||||
// 生成类
|
||||
generateMapper();
|
||||
}
|
||||
|
||||
return false;
|
||||
private List<TypeElement> getElementAndMergeHistory(final RoundEnvironment roundEnv,
|
||||
TypeElement annotation,
|
||||
BuildCollator buildCollator) {
|
||||
buildCollator.appendNonexistent(roundEnv.getElementsAnnotatedWith(annotation));
|
||||
return buildCollator.getRecords();
|
||||
}
|
||||
|
||||
private void processMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||
roundEnv.getElementsAnnotatedWith(annotation)
|
||||
.forEach(element -> customMapperList.add(element.asType()));
|
||||
if (annotation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
|
||||
new BuildCollator(processingEnv, Constants.MAPPERS_FILE_NAME));
|
||||
|
||||
elements.forEach(element -> customMapperList.add(element.asType()));
|
||||
}
|
||||
|
||||
private void processAutoEnumMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||
roundEnv.getElementsAnnotatedWith(annotation)
|
||||
.stream()
|
||||
if (annotation == null) {
|
||||
return;
|
||||
}
|
||||
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
|
||||
new BuildCollator(processingEnv, Constants.ENUM_MAPPERS_FILE_NAME));
|
||||
elements.stream()
|
||||
.map(this::buildAutoEnumMapperMetadata)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(this::writeAutoEnumMapperFile);
|
||||
@ -225,6 +269,9 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
private void addAdapterMethodMetadata(final AutoEnumMapperMetadata autoEnumMapperMetadata) {
|
||||
if (autoEnumMapperMetadata == null) {
|
||||
return;
|
||||
}
|
||||
// toValue
|
||||
final AdapterEnumMethodMetadata toValueProxyMethod =
|
||||
new AdapterEnumMethodMetadata(autoEnumMapperMetadata.getSourceClassName(),
|
||||
@ -237,7 +284,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
ClassName.get(autoEnumMapperMetadata.mapperPackage(), autoEnumMapperMetadata.mapperName()),
|
||||
autoEnumMapperMetadata.toEnumMethodName(),
|
||||
autoEnumMapperMetadata.getSourceClassName());
|
||||
methodMap.put(
|
||||
methodMap.putIfAbsent(
|
||||
autoEnumMapperMetadata.getSourceClassName().simpleName() + toValueProxyMethod.getMapperMethodName(),
|
||||
toValueProxyMethod);
|
||||
methodMap.put(
|
||||
@ -278,32 +325,21 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
private void processAutoMapMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||
final List<AutoMapperMetadata> autoMapMapperMetadataList =
|
||||
roundEnv.getElementsAnnotatedWith(annotation).stream().map(ele -> {
|
||||
if (ele.getAnnotation(AutoMapMapper.class) == null) {
|
||||
return null;
|
||||
}
|
||||
ClassName source = ClassName.get("java.util", "Map");
|
||||
ClassName target = ClassName.get((TypeElement) ele);
|
||||
List<ClassName> uses = Arrays.asList(ClassName.get("io.github.linpeilie.map", "MapObjectConvert"));
|
||||
if (annotation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
|
||||
new BuildCollator(processingEnv, Constants.AUTO_MAP_MAPPERS_FILE_NAME));
|
||||
|
||||
elements.stream()
|
||||
.map(ele -> buildAutoMapMapperMetadata((TypeElement) ele))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(metadata -> {
|
||||
this.writeAutoMapperClassFile(metadata);
|
||||
addAdapterMapMethod(metadata);
|
||||
});
|
||||
|
||||
final AutoMapperMetadata autoMapperMetadata = new AutoMapMapperMetadata();
|
||||
autoMapperMetadata.setTargetClassName(target);
|
||||
autoMapperMetadata.setSourceClassName(source);
|
||||
autoMapperMetadata.setUsesClassNameList(uses);
|
||||
autoMapperMetadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseMapMapper"));
|
||||
autoMapperMetadata.setSuperGenerics(new ClassName[] {target});
|
||||
autoMapperMetadata.setMapstructConfigClass(ClassName.get(AutoMapperProperties.getConfigPackage(),
|
||||
AutoMapperProperties.getMapConfigClassName()));
|
||||
return autoMapperMetadata;
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
autoMapMapperMetadataList.forEach(metadata -> {
|
||||
this.writeAutoMapperClassFile(metadata);
|
||||
addAdapterMapMethod(metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass(),
|
||||
false);
|
||||
addAdapterMapMethod(ClassName.get("java.lang", "Object"), metadata.getTargetClassName(),
|
||||
metadata.mapperClass(), true);
|
||||
});
|
||||
adapterMapperGenerator.write(processingEnv, mapMethodMap.values(),
|
||||
AutoMapperProperties.getMapAdapterClassName());
|
||||
|
||||
@ -311,6 +347,35 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
AutoMapperProperties.getMapAdapterClassName(), null);
|
||||
}
|
||||
|
||||
private AutoMapperMetadata buildAutoMapMapperMetadata(TypeElement element) {
|
||||
if (element.getAnnotation(AutoMapMapper.class) == null) {
|
||||
return null;
|
||||
}
|
||||
ClassName source = ClassName.get("java.util", "Map");
|
||||
ClassName target = ClassName.get(element);
|
||||
List<ClassName> uses = Arrays.asList(ClassName.get("io.github.linpeilie.map", "MapObjectConvert"));
|
||||
|
||||
final AutoMapperMetadata autoMapperMetadata = new AutoMapMapperMetadata();
|
||||
autoMapperMetadata.setTargetClassName(target);
|
||||
autoMapperMetadata.setSourceClassName(source);
|
||||
autoMapperMetadata.setUsesClassNameList(uses);
|
||||
autoMapperMetadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseMapMapper"));
|
||||
autoMapperMetadata.setSuperGenerics(new ClassName[] {target});
|
||||
autoMapperMetadata.setMapstructConfigClass(ClassName.get(AutoMapperProperties.getConfigPackage(),
|
||||
AutoMapperProperties.getMapConfigClassName()));
|
||||
return autoMapperMetadata;
|
||||
}
|
||||
|
||||
private void addAdapterMapMethod(AutoMapperMetadata metadata) {
|
||||
if (metadata == null) {
|
||||
return;
|
||||
}
|
||||
addAdapterMapMethod(metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass(),
|
||||
false);
|
||||
addAdapterMapMethod(ClassName.get("java.lang", "Object"), metadata.getTargetClassName(),
|
||||
metadata.mapperClass(), true);
|
||||
}
|
||||
|
||||
private void loadMapperConfig(MapperConfig mapperConfig) {
|
||||
if (mapperConfig == null) {
|
||||
return;
|
||||
@ -336,12 +401,30 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
private void refreshProperties(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
|
||||
final BuildCollator buildCollator = new BuildCollator(processingEnv, Constants.MAPPER_CONFIG_FILE_NAME);
|
||||
|
||||
// load previous mapper config
|
||||
final List<TypeElement> typeElements = buildCollator.getRecords();
|
||||
if (CollectionUtil.isNotEmpty(typeElements)) {
|
||||
messager.printMessage(Diagnostic.Kind.NOTE,
|
||||
"The previous Mapper Config Class was read , class name : " + typeElements.get(0));
|
||||
loadMapperConfig(typeElements.get(0).getAnnotation(MapperConfig.class));
|
||||
}
|
||||
|
||||
// annotation --> MapperConfig
|
||||
annotations.stream()
|
||||
.filter(this::isMapperConfigAnnotation)
|
||||
.findFirst()
|
||||
.flatMap(annotation -> roundEnv.getElementsAnnotatedWith(annotation).stream().findFirst())
|
||||
.ifPresent(element -> loadMapperConfig(element.getAnnotation(MapperConfig.class)));
|
||||
final TypeElement mapperConfigAnnotation =
|
||||
processingEnv.getElementUtils().getTypeElement(MAPPER_CONFIG_ANNOTATION);
|
||||
if (mapperConfigAnnotation != null) {
|
||||
final Optional<? extends Element> mapperConfigOptional =
|
||||
roundEnv.getElementsAnnotatedWith(mapperConfigAnnotation)
|
||||
.stream()
|
||||
.findFirst();
|
||||
if (mapperConfigOptional.isPresent()) {
|
||||
loadMapperConfig(mapperConfigOptional.get().getAnnotation(MapperConfig.class));
|
||||
// record
|
||||
buildCollator.writeTypeElements(CollectionUtil.newArrayList((TypeElement) mapperConfigOptional.get()));
|
||||
}
|
||||
}
|
||||
|
||||
// special MapperConfig Class
|
||||
final String mapperConfigClass = processingEnv.getOptions().get(MAPPER_CONFIG_CLASS);
|
||||
@ -376,7 +459,13 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
private void processAutoMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||
final List<AutoMapperMetadata> autoMapperMetadataList = roundEnv.getElementsAnnotatedWith(annotation)
|
||||
if (annotation == null) {
|
||||
return;
|
||||
}
|
||||
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
|
||||
new BuildCollator(processingEnv, Constants.AUTO_MAPPER_FILE_NAME));
|
||||
|
||||
final List<AutoMapperMetadata> autoMapperMetadataList = elements
|
||||
.stream()
|
||||
.map(this::buildAutoMapperMetadata)
|
||||
.filter(Objects::nonNull)
|
||||
@ -386,7 +475,11 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
private void processAutoMappersAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||
final List<AutoMapperMetadata> autoMapperMetadata = roundEnv.getElementsAnnotatedWith(annotation)
|
||||
if (annotation == null) {
|
||||
return;
|
||||
}
|
||||
final List<AutoMapperMetadata> autoMapperMetadata = getElementAndMergeHistory(roundEnv, annotation,
|
||||
new BuildCollator(processingEnv, Constants.AUTO_MAPPERS_FILE_NAME))
|
||||
.stream()
|
||||
.map(this::buildAutoMapperMetadataByAutoMappers)
|
||||
.filter(Objects::nonNull)
|
||||
@ -420,7 +513,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
return;
|
||||
}
|
||||
this.writeAutoMapperClassFile(metadata);
|
||||
addAdapterMethod(metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass());
|
||||
addAdapterMethod(metadata);
|
||||
});
|
||||
|
||||
adapterMapperGenerator.write(processingEnv, methodMap.values(), AutoMapperProperties.getAdapterClassName());
|
||||
@ -459,7 +552,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
try (final Writer writer = processingEnv.getFiler()
|
||||
.createSourceFile(mapperPackage + "." + mapperClassName)
|
||||
.openWriter()) {
|
||||
mapperGenerator.write(metadata, writer);
|
||||
mapperGenerator.write(metadata, processingEnv, writer);
|
||||
} catch (IOException e) {
|
||||
processingEnv.getMessager()
|
||||
.printMessage(ERROR,
|
||||
@ -467,15 +560,19 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private void addAdapterMethod(ClassName source, ClassName target, ClassName mapper) {
|
||||
AdapterMethodMetadata adapterMethodMetadata = AdapterMethodMetadata.newInstance(source, target, mapper);
|
||||
methodMap.put(adapterMethodMetadata.getMethodName(), adapterMethodMetadata);
|
||||
private void addAdapterMethod(AutoMapperMetadata metadata) {
|
||||
if (metadata == null) {
|
||||
return;
|
||||
}
|
||||
AdapterMethodMetadata adapterMethodMetadata = AdapterMethodMetadata.newInstance(
|
||||
metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass());
|
||||
methodMap.putIfAbsent(adapterMethodMetadata.getMethodName(), adapterMethodMetadata);
|
||||
}
|
||||
|
||||
private void addAdapterMapMethod(ClassName source, ClassName target, ClassName mapper, boolean objectConverter) {
|
||||
final AdapterMapMethodMetadata adapterMapMethodMetadata =
|
||||
new AdapterMapMethodMetadata(source, target, mapper, objectConverter);
|
||||
mapMethodMap.put(adapterMapMethodMetadata.getMethodName(), adapterMapMethodMetadata);
|
||||
mapMethodMap.putIfAbsent(adapterMapMethodMetadata.getMethodName(), adapterMapMethodMetadata);
|
||||
}
|
||||
|
||||
private AutoMapperMetadata initAutoMapperMetadata(ClassName source, ClassName target) {
|
||||
|
||||
@ -0,0 +1,104 @@
|
||||
package io.github.linpeilie.processor;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.processing.Filer;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
/**
|
||||
* 构建校对器
|
||||
*
|
||||
* 兼容 IDEA 部分构建的问题
|
||||
*/
|
||||
public class BuildCollator {
|
||||
|
||||
private final ProcessingEnvironment processingEnv;
|
||||
|
||||
private final File collatorFile;
|
||||
|
||||
private List<TypeElement> records;
|
||||
|
||||
public BuildCollator(ProcessingEnvironment processingEnv, String fileName) {
|
||||
this.processingEnv = processingEnv;
|
||||
Filer filer = processingEnv.getFiler();
|
||||
try {
|
||||
FileObject fileObject = filer.getResource(StandardLocation.CLASS_OUTPUT, "",
|
||||
Constants.MAPSTRUCT_PLUS_META_INF + File.separator + fileName);
|
||||
this.collatorFile = new File(fileObject.getName());
|
||||
if (collatorFile.exists()) {
|
||||
records = FileUtil.readUtf8Lines(collatorFile)
|
||||
.stream()
|
||||
.map(line -> processingEnv.getElementUtils().getTypeElement(line))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
records = new ArrayList<>();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTypeElements(Collection<TypeElement> typeElements) {
|
||||
final Set<String> names = typeElements.stream()
|
||||
.map(typeElement -> typeElement.getQualifiedName().toString())
|
||||
.collect(Collectors.toSet());
|
||||
write(names);
|
||||
}
|
||||
|
||||
private void write(Collection<String> lines) {
|
||||
if (CollectionUtil.isEmpty(lines)) {
|
||||
return;
|
||||
}
|
||||
|
||||
records = lines.stream()
|
||||
.map(line -> processingEnv.getElementUtils().getTypeElement(line))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
FileUtil.mkParentDirs(collatorFile);
|
||||
FileUtil.writeUtf8Lines(lines, collatorFile);
|
||||
}
|
||||
|
||||
private List<String> loadRecords() {
|
||||
return records.stream()
|
||||
.map(ele -> ele.getQualifiedName().toString())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<TypeElement> getRecords() {
|
||||
return records;
|
||||
}
|
||||
|
||||
public void consumeRecords(Consumer<TypeElement> consumer) {
|
||||
final List<TypeElement> typeElements = getRecords();
|
||||
if (CollectionUtil.isNotEmpty(typeElements)) {
|
||||
typeElements.forEach(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
public void appendNonexistent(Collection<? extends Element> newRecords) {
|
||||
final List<String> lines = loadRecords();
|
||||
newRecords.forEach(ele -> {
|
||||
final String classQualifiedName = ((TypeElement) ele).getQualifiedName().toString();
|
||||
lines.add(classQualifiedName);
|
||||
});
|
||||
write(new HashSet<>(lines));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package io.github.linpeilie.processor;
|
||||
|
||||
import java.io.File;
|
||||
import org.mapstruct.MappingConstants;
|
||||
|
||||
public class Constants {
|
||||
@ -30,15 +31,22 @@ public class Constants {
|
||||
|
||||
public static final String COMPONENT_MODEL_CONFIG_ANNOTATION = "io.github.linpeilie.annotations.ComponentModelConfig";
|
||||
|
||||
public static final String BASE_MAPPER_PACKAGE = "io.github.linpeilie";
|
||||
|
||||
public static final String BASE_MAPPER_CLASS_NAME = "BaseMapper";
|
||||
|
||||
public static final String BASE_MAP_MAPPER_CLASS_NAME = "BaseMapMapper";
|
||||
|
||||
public static final String MAPSTRUCT_MAPPER_PACKAGE = "org.mapstruct";
|
||||
|
||||
public static final String MAPSTRUCT_MAPPER_CLASS_NAME = "Mapper";
|
||||
|
||||
public static final String MAPSTRUCT_PLUS_META_INF = "META-INF" + File.separator + "mps";
|
||||
|
||||
public static final String MAPPER_CONFIG_FILE_NAME = "config";
|
||||
|
||||
public static final String MAPPERS_FILE_NAME = "mappers";
|
||||
|
||||
public static final String AUTO_MAPPER_FILE_NAME = "autoMapper";
|
||||
|
||||
public static final String AUTO_MAPPERS_FILE_NAME = "autoMappers";
|
||||
|
||||
public static final String AUTO_MAP_MAPPERS_FILE_NAME = "autoMapMappers";
|
||||
|
||||
public static final String ENUM_MAPPERS_FILE_NAME = "enumMappers";
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package io.github.linpeilie.processor.generator;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.squareup.javapoet.AnnotationSpec;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
@ -9,7 +10,7 @@ import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.github.linpeilie.processor.AutoMapperProperties;
|
||||
import io.github.linpeilie.annotations.Immutable;
|
||||
import io.github.linpeilie.processor.metadata.AutoMapperMetadata;
|
||||
import io.github.linpeilie.processor.metadata.AutoMappingMetadata;
|
||||
import java.io.IOException;
|
||||
@ -21,17 +22,21 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
|
||||
import static io.github.linpeilie.processor.Constants.*;
|
||||
|
||||
public class AutoMapperGenerator {
|
||||
|
||||
public void write(AutoMapperMetadata metadata, Writer writer) {
|
||||
public static final String CONVERT_METHOD_NAME = "convert";
|
||||
|
||||
public void write(AutoMapperMetadata metadata, final ProcessingEnvironment processingEnv, Writer writer) {
|
||||
try {
|
||||
JavaFile.builder(metadata.mapperPackage(), createTypeSpec(metadata)).build().writeTo(writer);
|
||||
JavaFile.builder(metadata.mapperPackage(), createTypeSpec(processingEnv, metadata)).build().writeTo(writer);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} catch (Exception e) {
|
||||
@ -39,36 +44,73 @@ public class AutoMapperGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private TypeSpec createTypeSpec(AutoMapperMetadata metadata) {
|
||||
private TypeSpec createTypeSpec(ProcessingEnvironment processingEnv, AutoMapperMetadata metadata) {
|
||||
ParameterizedTypeName converterName =
|
||||
ParameterizedTypeName.get(metadata.getSuperClass(), metadata.getSuperGenerics());
|
||||
|
||||
final ClassName targetClassName = metadata.getTargetClassName();
|
||||
|
||||
|
||||
TypeSpec.Builder builder = TypeSpec.interfaceBuilder(metadata.mapperName())
|
||||
.addSuperinterface(converterName)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotation(buildGeneratedMapperAnnotationSpec(metadata));
|
||||
|
||||
final ParameterSpec source = ParameterSpec.builder(metadata.getSourceClassName(), "source").build();
|
||||
final ParameterSpec target = ParameterSpec.builder(targetClassName, "target")
|
||||
.addAnnotation(AnnotationSpec.builder(ClassName.get("org.mapstruct", "MappingTarget")).build())
|
||||
.build();
|
||||
if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) {
|
||||
final ParameterSpec source = ParameterSpec.builder(metadata.getSourceClassName(), "source").build();
|
||||
final ParameterSpec target = ParameterSpec.builder(metadata.getTargetClassName(), "target")
|
||||
.addAnnotation(AnnotationSpec.builder(ClassName.get("org.mapstruct", "MappingTarget")).build())
|
||||
.build();
|
||||
builder.addMethod(addConvertMethodSpec(Collections.singletonList(source), metadata.getFieldMappingList(),
|
||||
metadata.getTargetClassName()));
|
||||
builder.addMethod(addConvertMethodSpec(Arrays.asList(source, target), metadata.getFieldMappingList(),
|
||||
metadata.getTargetClassName()));
|
||||
targetClassName));
|
||||
}
|
||||
|
||||
boolean targetIsImmutable = classIsImmutable(processingEnv, targetClassName);
|
||||
if (targetIsImmutable) {
|
||||
builder.addMethod(addEmptyConvertMethodForImmutableEntity(source, target, targetClassName));
|
||||
} else {
|
||||
builder.addMethod(addConvertMethodSpec(Arrays.asList(source, target), metadata.getFieldMappingList(),
|
||||
targetClassName));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private MethodSpec addEmptyConvertMethodForImmutableEntity(ParameterSpec source,
|
||||
ParameterSpec target,
|
||||
ClassName targetClassName) {
|
||||
return MethodSpec.methodBuilder(CONVERT_METHOD_NAME)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||
.addParameter(source)
|
||||
.addParameter(target)
|
||||
.returns(targetClassName)
|
||||
.addCode("return target;")
|
||||
.build();
|
||||
}
|
||||
|
||||
private boolean classIsImmutable(ProcessingEnvironment processingEnv, ClassName className) {
|
||||
final TypeElement targetElement = processingEnv.getElementUtils()
|
||||
.getTypeElement(className.packageName() + "." + className.simpleName());
|
||||
final List<? extends AnnotationMirror> annotationMirrors = targetElement.getAnnotationMirrors();
|
||||
for (AnnotationMirror annotationMirror : annotationMirrors) {
|
||||
if (annotationMirror.getAnnotationType().asElement().getSimpleName().contentEquals("Immutable")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MethodSpec addConvertMethodSpec(List<ParameterSpec> parameterSpecs,
|
||||
List<AutoMappingMetadata> autoMappingMetadataList,
|
||||
ClassName target) {
|
||||
return MethodSpec.methodBuilder("convert")
|
||||
final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(CONVERT_METHOD_NAME)
|
||||
.addParameters(parameterSpecs)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
|
||||
.addAnnotations(buildMappingAnnotations(autoMappingMetadataList))
|
||||
.returns(target)
|
||||
.build();
|
||||
.returns(target);
|
||||
if (CollectionUtil.isNotEmpty(autoMappingMetadataList)) {
|
||||
methodSpecBuilder.addAnnotations(buildMappingAnnotations(autoMappingMetadataList));
|
||||
}
|
||||
return methodSpecBuilder.build();
|
||||
}
|
||||
|
||||
private List<AnnotationSpec> buildMappingAnnotations(final List<AutoMappingMetadata> autoMappingMetadataList) {
|
||||
|
||||
@ -20,12 +20,6 @@ import static javax.tools.Diagnostic.Kind.ERROR;
|
||||
public class MapperConfigGenerator {
|
||||
|
||||
public void write(ProcessingEnvironment processingEnv, String mapstructConfigName, String adapterClassName, List<TypeMirror> uses) {
|
||||
final TypeElement typeElement =
|
||||
processingEnv.getElementUtils().getTypeElement(AutoMapperProperties.getConfigPackage() + "." + adapterClassName);
|
||||
if (typeElement != null) {
|
||||
System.out.println("mapperConfig class existed");
|
||||
return;
|
||||
}
|
||||
try (final Writer writer = processingEnv.getFiler()
|
||||
.createSourceFile(AutoMapperProperties.getConfigPackage() + "." + mapstructConfigName)
|
||||
.openWriter()) {
|
||||
|
||||
@ -2,6 +2,7 @@ package io.github.linpeilie.mapstruct;
|
||||
|
||||
import io.github.linpeilie.Converter;
|
||||
import io.github.linpeilie.ConverterFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
@ -12,11 +13,13 @@ import org.springframework.context.annotation.Configuration;
|
||||
public class MapstructAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConverterFactory converterFactory(ApplicationContext applicationContext) {
|
||||
return new SpringConverterFactory(applicationContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Converter converter(ConverterFactory converterFactory) {
|
||||
return new Converter(converterFactory);
|
||||
}
|
||||
|
||||
@ -11,11 +11,13 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S, T> BaseMapper<S, T> getMapper(final Class<S> sourceType, final Class<T> targetType) {
|
||||
final String key = key(sourceType, targetType);
|
||||
final Class<?> source = wrapperClass(sourceType);
|
||||
final Class<?> target = wrapperClass(targetType);
|
||||
final String key = key(source, target);
|
||||
if (mapperMap.containsKey(key)) {
|
||||
return mapperMap.get(key);
|
||||
}
|
||||
final BaseMapper mapper = findMapper(sourceType, targetType);
|
||||
final BaseMapper mapper = findMapper(source, target);
|
||||
if (mapper != null) {
|
||||
mapperMap.put(key, mapper);
|
||||
return mapper;
|
||||
@ -25,11 +27,12 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
|
||||
|
||||
@Override
|
||||
public <S> BaseMapMapper<S> getMapMapper(final Class<S> sourceType) {
|
||||
final String key = sourceType.getName();
|
||||
final Class<?> source = wrapperClass(sourceType);
|
||||
final String key = source.getName();
|
||||
if (mapMapperMap.containsKey(key)) {
|
||||
return mapMapperMap.get(key);
|
||||
}
|
||||
final BaseMapMapper mapper = findMapMapper(sourceType);
|
||||
final BaseMapMapper mapper = findMapMapper(source);
|
||||
if (mapper != null) {
|
||||
mapMapperMap.put(key, mapper);
|
||||
return mapper;
|
||||
@ -37,6 +40,10 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Class<?> wrapperClass(final Class<?> clazz) {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
protected abstract <S, T> BaseMapper findMapper(final Class<S> source, final Class<T> target);
|
||||
|
||||
protected abstract <S> BaseMapMapper findMapMapper(final Class<?> source);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package io.github.linpeilie;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@ -14,7 +15,7 @@ public interface BaseMapper<S, T> {
|
||||
|
||||
default List<T> convert(List<S> sourceList) {
|
||||
if (CollectionUtil.isEmpty(sourceList)) {
|
||||
return Collections.emptyList();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return sourceList.stream().map(this::convert).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package io.github.linpeilie;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -49,7 +49,7 @@ public class Converter {
|
||||
|
||||
public <S, T> List<T> convert(List<S> source, Class<T> targetType) {
|
||||
if (source == null || source.size() == 0) {
|
||||
return Collections.emptyList();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return source.stream().map(item -> convert(item, targetType)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package io.github.linpeilie.annotations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
public @interface Immutable {
|
||||
}
|
||||
2
pom.xml
2
pom.xml
@ -17,7 +17,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<mapstruct-plus.version>1.3.1</mapstruct-plus.version>
|
||||
<mapstruct-plus.version>1.3.2</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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user