diff --git a/README.md b/README.md
index 98718a9..1139880 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,7 @@ public class User {
```xml
- 1.3.1
+ 1.3.2
diff --git a/docs/README.md b/docs/README.md
index 32924a9..eb5c9c2 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -56,27 +56,22 @@ copyright: false
io.github.linpeilie
mapstruct-plus-spring-boot-starter
- 1.3.1
+ 1.3.2
```
- 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 部分编译问题,使用更加流畅丝滑
……
diff --git a/docs/guide/class-convert.md b/docs/guide/class-convert.md
index e63e758..1bb569d 100644
--- a/docs/guide/class-convert.md
+++ b/docs/guide/class-convert.md
@@ -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;
+}
+```
\ No newline at end of file
diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md
index dd9d126..77e46bb 100644
--- a/docs/guide/configuration.md
+++ b/docs/guide/configuration.md
@@ -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
diff --git a/docs/release/log.md b/docs/release/log.md
index 75e4883..ab64483 100644
--- a/docs/release/log.md
+++ b/docs/release/log.md
@@ -6,6 +6,11 @@ category:
description: MapStructPlus release log
---
+### 1.3.2
+
+- 不可变对象支持,可以使用任意包下的 `Immutable` 标注类型为不可变类
+- 全面适配 IDEA 部分编译问题,使用更加流畅丝滑
+
### 1.3.1
- 增加编译参数中指定配置类的功能
diff --git a/example/pom.xml b/example/pom.xml
index 0c1b64a..78fc488 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -18,7 +18,7 @@
UTF-8
1.5.1.Final
- 1.3.1
+ 1.3.2
1.18.22
diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Goods.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Goods.java
index ff94496..c8b85b9 100644
--- a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Goods.java
+++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Goods.java
@@ -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;
+
}
diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/GoodsDto.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/GoodsDto.java
index d073bb9..0418d7d 100644
--- a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/GoodsDto.java
+++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/GoodsDto.java
@@ -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;
+
}
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AbstractAdapterMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AbstractAdapterMapperGenerator.java
index cae3ff7..889ff3e 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AbstractAdapterMapperGenerator.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AbstractAdapterMapperGenerator.java
@@ -23,12 +23,6 @@ public abstract class AbstractAdapterMapperGenerator {
public void write(ProcessingEnvironment processingEnv,
Collection 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)
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProcessor.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProcessor.java
index 4a345c9..2c1ff6b 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProcessor.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProcessor.java
@@ -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 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 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 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 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 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 uses = Arrays.asList(ClassName.get("io.github.linpeilie.map", "MapObjectConvert"));
+ if (annotation == null) {
+ return;
+ }
+
+ final List 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 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 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 autoMapperMetadataList = roundEnv.getElementsAnnotatedWith(annotation)
+ if (annotation == null) {
+ return;
+ }
+ final List elements = getElementAndMergeHistory(roundEnv, annotation,
+ new BuildCollator(processingEnv, Constants.AUTO_MAPPER_FILE_NAME));
+
+ final List 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 = roundEnv.getElementsAnnotatedWith(annotation)
+ if (annotation == null) {
+ return;
+ }
+ final List 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) {
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/BuildCollator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/BuildCollator.java
new file mode 100644
index 0000000..2754530
--- /dev/null
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/BuildCollator.java
@@ -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 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 typeElements) {
+ final Set names = typeElements.stream()
+ .map(typeElement -> typeElement.getQualifiedName().toString())
+ .collect(Collectors.toSet());
+ write(names);
+ }
+
+ private void write(Collection 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 loadRecords() {
+ return records.stream()
+ .map(ele -> ele.getQualifiedName().toString())
+ .collect(Collectors.toList());
+ }
+
+ public List getRecords() {
+ return records;
+ }
+
+ public void consumeRecords(Consumer consumer) {
+ final List typeElements = getRecords();
+ if (CollectionUtil.isNotEmpty(typeElements)) {
+ typeElements.forEach(consumer);
+ }
+ }
+
+ public void appendNonexistent(Collection extends Element> newRecords) {
+ final List lines = loadRecords();
+ newRecords.forEach(ele -> {
+ final String classQualifiedName = ((TypeElement) ele).getQualifiedName().toString();
+ lines.add(classQualifiedName);
+ });
+ write(new HashSet<>(lines));
+ }
+
+}
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/Constants.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/Constants.java
index f45493a..f755211 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/Constants.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/Constants.java
@@ -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";
}
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/AutoMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/AutoMapperGenerator.java
index 94783f7..db0768e 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/AutoMapperGenerator.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/AutoMapperGenerator.java
@@ -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 parameterSpecs,
List 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 buildMappingAnnotations(final List autoMappingMetadataList) {
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/MapperConfigGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/MapperConfigGenerator.java
index fe192aa..0e086f3 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/MapperConfigGenerator.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/MapperConfigGenerator.java
@@ -20,12 +20,6 @@ import static javax.tools.Diagnostic.Kind.ERROR;
public class MapperConfigGenerator {
public void write(ProcessingEnvironment processingEnv, String mapstructConfigName, String adapterClassName, List 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()) {
diff --git a/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/MapstructAutoConfiguration.java b/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/MapstructAutoConfiguration.java
index 03b7a28..23743f5 100644
--- a/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/MapstructAutoConfiguration.java
+++ b/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/MapstructAutoConfiguration.java
@@ -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);
}
diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/AbstractCachedConverterFactory.java b/mapstruct-plus/src/main/java/io/github/linpeilie/AbstractCachedConverterFactory.java
index 4a23056..84c82dd 100644
--- a/mapstruct-plus/src/main/java/io/github/linpeilie/AbstractCachedConverterFactory.java
+++ b/mapstruct-plus/src/main/java/io/github/linpeilie/AbstractCachedConverterFactory.java
@@ -11,11 +11,13 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
@Override
@SuppressWarnings("unchecked")
public BaseMapper getMapper(final Class sourceType, final Class 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 BaseMapMapper getMapMapper(final Class 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 BaseMapper findMapper(final Class source, final Class target);
protected abstract BaseMapMapper findMapMapper(final Class> source);
diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java
index c300546..e031b5a 100644
--- a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java
+++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java
@@ -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 {
default List convert(List sourceList) {
if (CollectionUtil.isEmpty(sourceList)) {
- return Collections.emptyList();
+ return new ArrayList<>();
}
return sourceList.stream().map(this::convert).collect(Collectors.toList());
}
diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/Converter.java b/mapstruct-plus/src/main/java/io/github/linpeilie/Converter.java
index c3c4b0e..459c22e 100644
--- a/mapstruct-plus/src/main/java/io/github/linpeilie/Converter.java
+++ b/mapstruct-plus/src/main/java/io/github/linpeilie/Converter.java
@@ -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 List convert(List source, Class targetType) {
if (source == null || source.size() == 0) {
- return Collections.emptyList();
+ return new ArrayList<>();
}
return source.stream().map(item -> convert(item, targetType)).collect(Collectors.toList());
}
diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/Immutable.java b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/Immutable.java
new file mode 100644
index 0000000..51bbd76
--- /dev/null
+++ b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/Immutable.java
@@ -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 {
+}
diff --git a/pom.xml b/pom.xml
index 50d5ad8..f9b34e3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
- 1.3.1
+ 1.3.2
8
8
UTF-8