diff --git a/README.md b/README.md
index 91cf813be86a9d12b84763b57bccb1fbee9b9f55..110479ab3ea7171e4f40900c603cfb73c08df748 100644
--- a/README.md
+++ b/README.md
@@ -188,7 +188,7 @@ public class User {
```xml
- 1.4.2
+ 1.4.3
diff --git a/docs/README.md b/docs/README.md
index aa779a7cf84ea9b8da3255ea4ca379fdb4fa5f1f..8ebe737b0fc2a4c0613a7cc7b0c22d6d17d0ff2e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -62,18 +62,24 @@ footer:
io.github.linpeilie
mapstruct-plus-spring-boot-starter
- 1.4.2
+ 1.4.3
```
- gradle
```groovy
-implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.4.2'
+implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.4.3'
```
## 更新日志
+### 1.4.3
+
+- feat: `ComponentModel` 增加 `spring-lazy` 可选项,懒加载 Spring Bean,解决互相依赖的问题,并将默认配置改为该选项;
+- fix: 解决 `unmappedTargetPolicy` 默认配置不生效的问题;
+- enhance: 优化 IDEA 本地开发构建效率,一定程度上缩短构建时间、减小元空间占用;[Issue #89](https://github.com/linpeilie/mapstruct-plus/issues/89)
+
### 1.4.2
- feat: `AutoMapper` 注解增加 `mapperNameSuffix` 属性,支持配置生成的转换接口名称增加后缀,默认规则下生成的反向转换接口同时生效;
@@ -101,13 +107,6 @@ implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-s
> 当然,这个问题在之前也会有,几率可能低一些,所以多模块下,务必配置 `adapterPackage` 来避免该问题。
> - Map 与对象的转换,还是依赖 hutool 中的类转换实现,如果需要该功能,需要额外引入 `hutool-core` 依赖包。
-### 1.3.6
-
-- 兼容内部类转换
-- feature : AutoMapping 注解中的 targetClass 支持配置父类
-- [issue#I8QPRO](https://gitee.com/easii/mapstruct-plus/issues/I8QPRO) : 框架自动生成的 AutoMapperConfig 和 AutoMapMapper 包和类名支持配置
-- [issue#I8T7EF](https://gitee.com/easii/mapstruct-plus/issues/I8T7EF) : 支持在父类中配置的 AutoMapping 注解
-
……
## 代码仓库
diff --git a/docs/en/README.md b/docs/en/README.md
index 14cc8feef1908dec00d8c5633a2121094a3843bb..5bfff1a5efa3cfcb61821e708b99257ae7d635b3 100644
--- a/docs/en/README.md
+++ b/docs/en/README.md
@@ -58,25 +58,25 @@ fotter:
io.github.linpeilie
mapstruct-plus-spring-boot-starter
- 1.4.0
+ 1.4.3
```
- gradle
```groovy
-implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.4.0'
+implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.4.3'
```
## Change Log
-### 1.4.2
-
-Sure, here is the translated update document:
+### 1.4.3
----
+- **feat**: Added `spring-lazy` option to `ComponentModel` for lazy loading Spring Beans, resolving mutual dependency issues, and set this option as the default configuration.
+- **fix**: Fixed the issue where the default configuration for `unmappedTargetPolicy` was not effective.
+- **enhance**: Optimized IDEA local development build efficiency, reducing build time and metaspace usage to some extent.[Issue #89](https://github.com/linpeilie/mapstruct-plus/issues/89)
-### Updates
+### 1.4.2
- **feat**: Added the `mapperNameSuffix` attribute to the `AutoMapper` annotation. This supports adding a suffix to the generated conversion interface name, and the reverse conversion interface will be effective under the default rules.
- **feat**: Adapted the `Mapper` annotation to support the following attributes: `unmappedSourcePolicy`, `unmappedTargetPolicy`, `typeConversionPolicy`, `collectionMappingStrategy`, `nullValueMappingStrategy`, `nullValueIterableMappingStrategy`, `nullValuePropertyMappingStrategy`, `nullValueCheckStrategy`, and `mappingControl`.
diff --git a/docs/en/release/log.md b/docs/en/release/log.md
index 4e1682054173373231d055357b11865649070b53..e5294371291b5d8d68eaf65b6e4d2d89925c8db0 100644
--- a/docs/en/release/log.md
+++ b/docs/en/release/log.md
@@ -6,6 +6,12 @@ category:
description: MapStructPlus release log
---
+### 1.4.3
+
+- **feat**: Added `spring-lazy` option to `ComponentModel` for lazy loading Spring Beans, resolving mutual dependency issues, and set this option as the default configuration.
+- **fix**: Fixed the issue where the default configuration for `unmappedTargetPolicy` was not effective.
+- **enhance**: Optimized IDEA local development build efficiency, reducing build time and metaspace usage to some extent.[Issue #89](https://github.com/linpeilie/mapstruct-plus/issues/89)
+
### 1.4.2
- **feat**: Added the `mapperNameSuffix` attribute to the `AutoMapper` annotation. This supports adding a suffix to the generated conversion interface name, and the reverse conversion interface will be effective under the default rules.
diff --git a/docs/release/log.md b/docs/release/log.md
index f69f9f019c16c120ebac9c445915a685127aff9a..2d5c40ff9bd4d5f4cc14a0b263f93a9333c577b0 100644
--- a/docs/release/log.md
+++ b/docs/release/log.md
@@ -6,6 +6,12 @@ category:
description: MapStructPlus release log
---
+### 1.4.3
+
+- feat: `ComponentModel` 增加 `spring-lazy` 可选项,懒加载 Spring Bean,解决互相依赖的问题,并将默认配置改为该选项;
+- fix: 解决 `unmappedTargetPolicy` 默认配置不生效的问题;
+- enhance: 优化 IDEA 本地开发构建效率,一定程度上缩短构建时间、减小元空间占用;[Issue #89](https://github.com/linpeilie/mapstruct-plus/issues/89)
+
### 1.4.2
- feat: `AutoMapper` 注解增加 `mapperNameSuffix` 属性,支持配置生成的转换接口名称增加后缀,默认规则下生成的反向转换接口同时生效;
diff --git a/example/pom.xml b/example/pom.xml
index cb33ca75625f4802a157770ae189652b77d62049..d5d2a39ddb9ed74dc9b9dee85b0128836d616209 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -18,7 +18,7 @@
UTF-8
1.5.1.Final
- 1.4.2
+ 1.4.3
1.18.22
5.8.26
32.1.3-jre
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 c3e9e4abe320b0e95db8a86ff9b9d477d8e730c5..426b5c3b987415d841ee850c1e830df78f2e4bda 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
@@ -12,6 +12,7 @@ import io.github.linpeilie.processor.metadata.AbstractAdapterMethodMetadata;
import io.github.linpeilie.processor.metadata.AdapterMapMethodMetadata;
import io.github.linpeilie.processor.metadata.AdapterMethodMetadata;
import io.github.linpeilie.utils.ClassUtil;
+import io.github.linpeilie.utils.CollectionUtils;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
@@ -61,9 +62,6 @@ public abstract class AbstractAdapterMapperGenerator {
.map(method -> buildProxyMethod(method, cycleAvoiding))
.flatMap(Collection::stream)
.collect(Collectors.toList());
- if (methods.isEmpty()) {
- return null;
- }
return createTypeSpec(methods, adapterClassName,
cycleAvoiding ? ClassName.get(adapterPackage(), AutoMapperProperties.getAdapterClassName()) : null);
}
@@ -77,7 +75,9 @@ public abstract class AbstractAdapterMapperGenerator {
}
// adapter methods
- adapterBuilder.addMethods(methods);
+ if (CollectionUtils.isNotEmpty(methods)) {
+ adapterBuilder.addMethods(methods);
+ }
return adapterBuilder.build();
}
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AdapterMapperGeneratorFactory.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AdapterMapperGeneratorFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..30c4196b69281053f6bea90df633758c69fd8397
--- /dev/null
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AdapterMapperGeneratorFactory.java
@@ -0,0 +1,23 @@
+package io.github.linpeilie.processor;
+
+import io.github.linpeilie.ComponentModelConstant;
+import io.github.linpeilie.processor.generator.DefaultAdapterMapperGenerator;
+import io.github.linpeilie.processor.generator.SolonAdapterMapperGenerator;
+import io.github.linpeilie.processor.generator.SpringAdapterMapperGenerator;
+import org.mapstruct.MappingConstants;
+
+public class AdapterMapperGeneratorFactory {
+
+ public static AbstractAdapterMapperGenerator instance(String componentModel) {
+ switch (AutoMapperProperties.getComponentModel()) {
+ case MappingConstants.ComponentModel.SPRING:
+ case ContextConstants.ComponentModelConfig.springLazy:
+ return new SpringAdapterMapperGenerator();
+ case ComponentModelConstant.SOLON:
+ return new SolonAdapterMapperGenerator();
+ default:
+ return new DefaultAdapterMapperGenerator();
+ }
+ }
+
+}
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 9d7150baf973134c3169dc687f9c951f83cdd5fe..a5d4c158bf136265dcfc804ee603d1c50a0db8e0 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,8 @@
package io.github.linpeilie.processor;
+import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import io.github.linpeilie.ComponentModelConstant;
import io.github.linpeilie.annotations.AutoEnumMapper;
@@ -117,6 +119,10 @@ public class AutoMapperProcessor extends AbstractProcessor {
private final Set mapperSet = new HashSet<>();
+ private static final Map AUTO_MAPPER_INDEX = new HashMap<>();
+
+ private final Map> typeRelationMappers = new HashMap<>();
+
private Messager messager;
public AutoMapperProcessor() {
@@ -178,16 +184,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
refreshProperties(annotations, roundEnv);
// 根据配置生成适配类生成器
- switch (AutoMapperProperties.getComponentModel()) {
- case MappingConstants.ComponentModel.SPRING:
- this.adapterMapperGenerator = new SpringAdapterMapperGenerator();
- break;
- case ComponentModelConstant.SOLON:
- this.adapterMapperGenerator = new SolonAdapterMapperGenerator();
- break;
- default:
- this.adapterMapperGenerator = new DefaultAdapterMapperGenerator();
- }
+ this.adapterMapperGenerator = AdapterMapperGeneratorFactory.instance(AutoMapperProperties.getComponentModel());
// AutoMapMapper
final TypeElement autoMapMapperAnnotation =
@@ -381,7 +378,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
}
AutoMapperProperties.setUnmappedSourcePolicy(mapperConfigGem.unmappedSourcePolicy().getValue());
// 重定义 MapStruct 中 unmappedTargetPolicy 的默认值 WARN ---> IGNORE
- AutoMapperProperties.setUnmappedTargetPolicy(mapperConfigGem.unmappedTargetPolicy().getValue());
+ AutoMapperProperties.setUnmappedTargetPolicy(mapperConfigGem.unmappedTargetPolicy().get());
AutoMapperProperties.setTypeConversionPolicy(mapperConfigGem.typeConversionPolicy().getValue());
AutoMapperProperties.setCollectionMappingStrategy(mapperConfigGem.collectionMappingStrategy().getValue());
AutoMapperProperties.setNullValueMappingStrategy(mapperConfigGem.nullValueMappingStrategy().getValue());
@@ -507,7 +504,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
mapperList.addAll(autoMapperMetadata);
}
- private void generateMapper() {
+ private List generateReverseConverters() {
List reverseMapperMetadataList = new ArrayList<>();
mapperList.forEach(autoMapperMetadata -> {
@@ -524,19 +521,44 @@ public class AutoMapperProcessor extends AbstractProcessor {
reverseMapperMetadataList.add(reverseMapperMetadata);
});
- mapperList.addAll(reverseMapperMetadataList);
+ return reverseMapperMetadataList;
+ }
+
+ private void typeRelationMapper(AutoMapperMetadata metadata) {
+ String source = metadata.getSourceClassName().reflectionName();
+ if (!typeRelationMappers.containsKey(source)) {
+ typeRelationMappers.put(source, new ArrayList<>());
+ }
+ typeRelationMappers.get(source).add(metadata.mapperClass());
+
+ String target = metadata.getTargetClassName().reflectionName();
+ if (!typeRelationMappers.containsKey(target)) {
+ typeRelationMappers.put(target, new ArrayList<>());
+ }
+ typeRelationMappers.get(target).add(metadata.mapperClass());
+ }
+
+ private void generateMapper() {
+ mapperList.addAll(generateReverseConverters());
+
+ mapperList.removeIf(metadata -> !metadata.isConvertGenerate());
mapperList.forEach(metadata -> {
- if (!metadata.isConvertGenerate()) {
- return;
+ // 兼容同模块下,同名不同包的场景
+ this.mapperNameAddSuffix(metadata);
+
+ if (metadata.isCycleAvoiding()) {
+ addAdapterMethod(metadata);
+ } else {
+ typeRelationMapper(metadata);
}
- this.writeAutoMapperClassFile(metadata);
- addAdapterMethod(metadata);
});
- if (methodMap.isEmpty()) {
- return;
- }
+ mapperList.forEach(metadata -> {
+ this.relationDependencies(metadata);
+ this.usesRemoveItself(metadata);
+ this.writeAutoMapperClassFile(metadata);
+ });
adapterMapperGenerator.write(processingEnv,
methodMap.values(),
@@ -563,6 +585,88 @@ public class AutoMapperProcessor extends AbstractProcessor {
}
}
+ private void mapperNameAddSuffix(AutoMapperMetadata metadata) {
+ String mapperName = metadata.mapperName();
+ // 同名类时,增加后缀
+ Integer index = AUTO_MAPPER_INDEX.getOrDefault(mapperName, 0);
+ if (index > 0) {
+ mapperName = mapperName + "__" + index;
+ }
+ AUTO_MAPPER_INDEX.put(metadata.mapperName(), ++index);
+ metadata.setMapperName(mapperName);
+ }
+
+ private void relationDependencies(AutoMapperMetadata metadata) {
+ Set dependencies = metadata.getDependencies();
+ if (CollectionUtils.isNotEmpty(dependencies)) {
+ List dependencyMappers = dependencies.stream().map(dependency ->
+ typeRelationMappers.get(dependency.toString())
+ ).filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
+
+ if (CollectionUtils.isNotEmpty(dependencyMappers)) {
+ metadata.addUseList(dependencyMappers);
+ }
+ }
+ // source
+ List sourceDependencies =
+ typeRelationMappers.get(metadata.getSourceClassName().reflectionName());
+
+ if (CollectionUtils.isNotEmpty(sourceDependencies)) {
+ metadata.addUseList(sourceDependencies);
+ }
+ }
+
+ private void usesRemoveItself(AutoMapperMetadata metadata) {
+ // remove itself
+ if (CollectionUtils.isNotEmpty(metadata.getUsesClassNameList())) {
+ metadata.getUsesClassNameList()
+ .removeIf(use -> use.reflectionName().equals(metadata.mapperClass().reflectionName()));
+ }
+ }
+
+ private Set listDependencies(TypeElement autoMapperEle) {
+ Set set = new HashSet<>();
+
+ if (!autoMapperEle.getKind().isClass() && !autoMapperEle.getKind().isInterface()) {
+ return set;
+ }
+
+ for (Element ele : autoMapperEle.getEnclosedElements()) {
+ if (ele.getKind() != ElementKind.FIELD) {
+ continue;
+ }
+ TypeName typeName = ClassName.get(ele.asType());
+ if (typeName instanceof ArrayTypeName) {
+ ArrayTypeName arrayTypeName = (ArrayTypeName) typeName;
+ typeName = arrayTypeName.componentType;
+ } else if (typeName instanceof ParameterizedTypeName) {
+ ParameterizedTypeName parameterizedTypeName = (ParameterizedTypeName) typeName;
+ List typeArguments = parameterizedTypeName.typeArguments;
+ set.addAll(typeArguments);
+ continue;
+ }
+ set.add(typeName);
+ }
+
+ // add super class dependencies
+ getSuperClass(autoMapperEle).ifPresent(superClass -> set.addAll(listDependencies(superClass)));
+
+ set.removeIf(ele -> {
+ if (ele == null) {
+ return true;
+ }
+ try {
+ if (ele.box().isBoxedPrimitive()) {
+ return true;
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ return false;
+ });
+ return set;
+ }
+
private AutoMapperMetadata reverseMapper(AutoMapperMetadata autoMapperMetadata) {
AutoMapperMetadata reverseMapperMetadata =
initAutoMapperMetadata(autoMapperMetadata.getTargetClassName(),
@@ -761,6 +865,9 @@ public class AutoMapperProcessor extends AbstractProcessor {
MapperUtils.getEnumMapperClassName(enumClass.simpleName())))
.collect(Collectors.toList());
+ // dependencies
+ Set dependencies = listDependencies((TypeElement) ele);
+
usesClassNameList.addAll(useEnumClassNameList);
metadata.setUsesClassNameList(usesClassNameList);
@@ -790,6 +897,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
metadata.setMapperName(autoMapperGem.mapperName().getValue());
}
metadata.setMapperNameSuffix(autoMapperGem.mapperNameSuffix().getValue());
+ metadata.setDependencies(dependencies);
addMapper(metadata, true);
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProperties.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProperties.java
index ddb68fea9fd44b2e955e35415d884847d29af761..65e08046bc720c3caa0c15561e463f0ce9317aa7 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProperties.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProperties.java
@@ -15,7 +15,7 @@ public class AutoMapperProperties {
private static String unmappedSourcePolicy;
- private static String unmappedTargetPolicy;
+ private static String unmappedTargetPolicy = "IGNORE";
private static String typeConversionPolicy;
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/ContextConstants.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/ContextConstants.java
index 7344696cb4873b586c0d69d398ff7f02e0fba36e..14013b492cdd585881a3ece4be2660a64ee36490 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/ContextConstants.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/ContextConstants.java
@@ -65,7 +65,8 @@ public interface ContextConstants {
interface ComponentModelConfig {
String qualifiedClassName = "io.github.linpeilie.annotations.ComponentModelConfig";
- String defaultComponentModel = MappingConstants.ComponentModel.SPRING;
+ String springLazy = "spring-lazy";
+ String defaultComponentModel = springLazy;
}
interface ConvertAdapter {
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/model/SpringDelayInjectMapperReference.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/model/SpringDelayInjectMapperReference.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2b5d2dd9f54d237349b28f1d727d74d546dd2e9
--- /dev/null
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/model/SpringDelayInjectMapperReference.java
@@ -0,0 +1,25 @@
+package io.github.linpeilie.processor.enhance.model;
+
+import io.github.linpeilie.processor.enhance.processor.SpringComponentProcessor;
+import java.util.HashSet;
+import java.util.Set;
+import org.mapstruct.ap.internal.model.MapperReference;
+import org.mapstruct.ap.internal.model.common.Type;
+
+public class SpringDelayInjectMapperReference extends MapperReference {
+
+ private final Type springContextUtil;
+
+ public SpringDelayInjectMapperReference(Type type, String variableName, boolean isUsed, Type springContextUtil) {
+ super(type, variableName, isUsed);
+ this.springContextUtil = springContextUtil;
+ }
+
+ @Override
+ public Set getImportTypes() {
+ Set importTypes = new HashSet<>();
+ importTypes.add(getType());
+ importTypes.add(springContextUtil);
+ return importTypes;
+ }
+}
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/solon/SolonComponentProcessor.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/processor/SolonComponentProcessor.java
similarity index 95%
rename from mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/solon/SolonComponentProcessor.java
rename to mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/processor/SolonComponentProcessor.java
index 4c6aa638c2b26b33ab21d21ba999125da6549255..1c4fd8ce421f2923edbdf75583342ddee720c333 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/solon/SolonComponentProcessor.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/processor/SolonComponentProcessor.java
@@ -1,4 +1,4 @@
-package io.github.linpeilie.processor.solon;
+package io.github.linpeilie.processor.enhance.processor;
import io.github.linpeilie.ComponentModelConstant;
import io.github.linpeilie.utils.CollectionUtils;
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/processor/SpringComponentProcessor.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/processor/SpringComponentProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d74245953b18f11303039b526b9b95ddb5db5f3
--- /dev/null
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/enhance/processor/SpringComponentProcessor.java
@@ -0,0 +1,48 @@
+package io.github.linpeilie.processor.enhance.processor;
+
+import io.github.linpeilie.processor.ContextConstants;
+import io.github.linpeilie.processor.enhance.model.SpringDelayInjectMapperReference;
+import io.github.linpeilie.utils.CollectionUtils;
+import java.util.Collections;
+import java.util.List;
+import org.mapstruct.ap.internal.gem.InjectionStrategyGem;
+import org.mapstruct.ap.internal.model.Annotation;
+import org.mapstruct.ap.internal.model.Field;
+import org.mapstruct.ap.internal.model.Mapper;
+import org.mapstruct.ap.internal.processor.AnnotationBasedComponentModelProcessor;
+
+public class SpringComponentProcessor extends AnnotationBasedComponentModelProcessor {
+
+ private Annotation component() {
+ return new Annotation(getTypeFactory().getType("org.springframework.stereotype.Component"));
+ }
+
+ @Override
+ protected String getComponentModelIdentifier() {
+ return ContextConstants.ComponentModelConfig.springLazy;
+ }
+
+ @Override
+ protected List getTypeAnnotations(Mapper mapper) {
+ return CollectionUtils.newArrayList(component());
+ }
+
+ @Override
+ protected List getMapperReferenceAnnotations() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ protected boolean requiresGenerationOfDecoratorClass() {
+ return true;
+ }
+
+ @Override
+ protected Field replacementMapperReference(Field originalReference,
+ List annotations,
+ InjectionStrategyGem injectionStrategy) {
+ return new SpringDelayInjectMapperReference(originalReference.getType(), originalReference.getVariableName(),
+ originalReference.isUsed(),
+ getTypeFactory().getType("io.github.linpeilie.mapstruct.SpringContextUtils"));
+ }
+}
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 19deb351b0aa4a44b5235945771195f08c90a454..f53bb4841c4a4ed09dc6a4b59d75759f5bd80312 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
@@ -7,12 +7,12 @@ import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import io.github.linpeilie.processor.ContextConstants;
import io.github.linpeilie.processor.metadata.AutoMapperMetadata;
import io.github.linpeilie.processor.metadata.AutoMappingMetadata;
import io.github.linpeilie.utils.CollectionUtils;
-import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
@@ -32,22 +32,9 @@ public class AutoMapperGenerator {
public static final String CONVERT_METHOD_NAME = "convert";
- private static final Map AUTO_MAPPER_INDEX = new HashMap<>();
-
public void write(AutoMapperMetadata metadata, ProcessingEnvironment processingEnv) {
String mapperPackage = metadata.mapperPackage();
-
- /*
- 当前处理方式,本地使用 IDEA 开发时,当修改 Source/Target 类时,可能还会出现类名冲突的问题,
- 当出现该问题时,需要执行 clean 把之前构建的类清掉。
- */
String mapperName = metadata.mapperName();
- // 同名类时,增加后缀
- Integer index = AUTO_MAPPER_INDEX.getOrDefault(mapperName, 0);
- if (index > 0) {
- mapperName = mapperName + "__" + index;
- }
- AUTO_MAPPER_INDEX.put(metadata.mapperName(), ++index);
try (final Writer writer = processingEnv.getFiler()
.createSourceFile(mapperPackage + "." + mapperName)
@@ -55,7 +42,7 @@ public class AutoMapperGenerator {
JavaFile.builder(metadata.mapperPackage(), createTypeSpec(processingEnv, metadata, mapperName))
.build()
.writeTo(writer);
- } catch (IOException e) {
+ } catch (Exception e) {
processingEnv.getMessager()
.printMessage(ERROR,
"Error while opening " + metadata.mapperName() + " output file: " + e.getMessage());
@@ -79,19 +66,69 @@ public class AutoMapperGenerator {
ParameterSpec target = ParameterSpec.builder(targetClassName, "target")
.addAnnotation(AnnotationSpec.builder(ClassName.get("org.mapstruct", "MappingTarget")).build())
.build();
+ ParameterSpec sourceList = ParameterSpec.builder(
+ ParameterizedTypeName.get(
+ ClassName.get("java.util", "List"),
+ metadata.getSourceClassName()
+ ), "sourceList").build();
ParameterSpec context =
ParameterSpec.builder(ClassName.get("io.github.linpeilie", "CycleAvoidingMappingContext"), "context")
.addAnnotation(ClassName.get("org.mapstruct", "Context"))
.build();
- if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) {
+ ParameterizedTypeName targetList = ParameterizedTypeName.get(
+ ClassName.get("java.util", "List"),
+ targetClassName
+ );
+
+ // 如果需要避免循环依赖,则把 BaseMapper 中的实现,全部添加 DoIgnore 防止使用该方法进行转换
+ if (metadata.isCycleAvoiding()) {
+ // convert(source)
+ builder.addMethod(
+ addCallSuperConvertMethodSpec(
+ metadata.getSuperClass(),
+ CollectionUtils.newArrayList(source),
+ targetClassName,
+ CONVERT_METHOD_NAME)
+ );
+ // convert(source, target)
+ builder.addMethod(
+ addCallSuperConvertMethodSpec(
+ metadata.getSuperClass(), CollectionUtils.newArrayList(source, target),
+ targetClassName,
+ CONVERT_METHOD_NAME
+ )
+ );
+ // convert(sourceList)
+ builder.addMethod(
+ addCallSuperConvertMethodSpec(
+ metadata.getSuperClass(), CollectionUtils.newArrayList(sourceList),
+ targetList,
+ CONVERT_METHOD_NAME
+ )
+ );
+ // convert(sourceList, context)
+ builder.addMethod(
+ addCallSuperConvertMethodSpec(
+ metadata.getSuperClass(), CollectionUtils.newArrayList(sourceList, context),
+ targetList,
+ CONVERT_METHOD_NAME
+ )
+ );
+ }
+
+ // convert(source) | convert(source, context)
+ if (CollectionUtils.isNotEmpty(metadata.getFieldMappingList()) || metadata.isCycleAvoiding()) {
builder.addMethod(addConvertMethodSpec(
- metadata.isCycleAvoiding() ? CollectionUtils.newArrayList(source, context) : Collections.singletonList(
- source),
+ metadata.isCycleAvoiding()
+ ? CollectionUtils.newArrayList(source, context)
+ : Collections.singletonList(source),
metadata.getFieldMappingList(),
targetClassName,
- CONVERT_METHOD_NAME));
+ CONVERT_METHOD_NAME,
+ metadata.isCycleAvoiding()));
}
+ // convert(source, target)
boolean targetIsImmutable = classIsImmutable(processingEnv, targetClassName);
if (targetIsImmutable) {
builder.addMethod(
@@ -100,13 +137,15 @@ public class AutoMapperGenerator {
context) : CollectionUtils.newArrayList(source, target),
targetClassName,
CONVERT_METHOD_NAME));
- } else if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) {
+ } else if (CollectionUtils.isNotEmpty(metadata.getFieldMappingList()) || metadata.isCycleAvoiding()) {
builder.addMethod(addConvertMethodSpec(
- metadata.isCycleAvoiding() ? CollectionUtils.newArrayList(source, target,
- context) : CollectionUtils.newArrayList(source, target),
+ metadata.isCycleAvoiding()
+ ? CollectionUtils.newArrayList(source, target, context)
+ : CollectionUtils.newArrayList(source, target),
metadata.getFieldMappingList(),
targetClassName,
- CONVERT_METHOD_NAME));
+ CONVERT_METHOD_NAME,
+ metadata.isCycleAvoiding()));
}
return builder.build();
@@ -137,15 +176,50 @@ public class AutoMapperGenerator {
private MethodSpec addConvertMethodSpec(List parameterSpecs,
List autoMappingMetadataList,
- ClassName target, String methodName) {
+ ClassName target,
+ String methodName,
+ boolean cycleAvoiding) {
final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodName)
.addParameters(parameterSpecs)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
- .addAnnotation(ClassName.get(ContextConstants.DoIgnore.packageName, ContextConstants.DoIgnore.className))
.returns(target);
if (CollectionUtils.isNotEmpty(autoMappingMetadataList)) {
methodSpecBuilder.addAnnotations(buildMappingAnnotations(autoMappingMetadataList));
}
+ if (cycleAvoiding) {
+ methodSpecBuilder.addAnnotation(
+ ClassName.get(ContextConstants.DoIgnore.packageName, ContextConstants.DoIgnore.className));
+ }
+ return methodSpecBuilder.build();
+ }
+
+ private ClassName doIgnore() {
+ return ClassName.get(ContextConstants.DoIgnore.packageName, ContextConstants.DoIgnore.className);
+ }
+
+ private MethodSpec addCallSuperConvertMethodSpec(ClassName superClass,
+ List parameterSpecs,
+ TypeName target,
+ String methodName) {
+ MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodName)
+ .addParameters(parameterSpecs)
+ .addModifiers(Modifier.DEFAULT, Modifier.PUBLIC)
+ .addAnnotation(doIgnore())
+ .returns(target);
+
+ // return super.convert( *** );
+ CodeBlock.Builder codeBlock = CodeBlock.builder();
+ codeBlock.add("return $T.super.$L(", superClass, methodName);
+ for (int i = 0; i < parameterSpecs.size(); i++) {
+ codeBlock.add("$N", parameterSpecs.get(i));
+ if (i != parameterSpecs.size() -1) {
+ codeBlock.add(",");
+ }
+ }
+ codeBlock.add(");\n");
+
+ methodSpecBuilder.addCode(codeBlock.build());
+
return methodSpecBuilder.build();
}
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/IocAdapterMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/IocAdapterMapperGenerator.java
index e6d49303a7a28fdd793d81f5552aa258b45f0a1b..540edbdff2efa24025122a8a7e1cc3ef49b0f5f3 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/IocAdapterMapperGenerator.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/IocAdapterMapperGenerator.java
@@ -8,6 +8,7 @@ import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import io.github.linpeilie.processor.AbstractAdapterMapperGenerator;
import io.github.linpeilie.processor.metadata.AbstractAdapterMethodMetadata;
+import io.github.linpeilie.utils.CollectionUtils;
import java.util.List;
import javax.lang.model.element.Modifier;
@@ -27,7 +28,9 @@ public abstract class IocAdapterMapperGenerator extends AbstractAdapterMapperGen
adapterBuilder.addField(buildConverterField());
- adapterBuilder.addMethods(methods);
+ if (CollectionUtils.isNotEmpty(methods)) {
+ adapterBuilder.addMethods(methods);
+ }
if (superClass != null) {
adapterBuilder.superclass(superClass);
diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java
index 34c65325bf9714e2fc3f3b7cae46992ce1cba6a5..e8b248eec98ddb29d48b6bed83d8ae0151d5cab7 100644
--- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java
+++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java
@@ -1,9 +1,12 @@
package io.github.linpeilie.processor.metadata;
import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
import io.github.linpeilie.processor.utils.MapperUtils;
import io.github.linpeilie.utils.StrUtil;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import org.mapstruct.NullValueMappingStrategy;
import org.mapstruct.SubclassExhaustiveStrategy;
@@ -65,10 +68,19 @@ public class AutoMapperMetadata extends AbstractMapperMetadata {
private ClassName mappingControl;
+ private Set dependencies;
+
public String qualifiedMapperName() {
return mapperPackage() + "." + mapperName();
}
+ public boolean addUseList(List uses) {
+ if (this.usesClassNameList == null) {
+ this.usesClassNameList = new ArrayList<>();
+ }
+ return usesClassNameList.addAll(uses);
+ }
+
/*************** getter/setter ***************/
public String mapperName() {
@@ -79,10 +91,6 @@ public class AutoMapperMetadata extends AbstractMapperMetadata {
this.mapperName = mapperName;
}
- public String getMapperName() {
- return mapperName;
- }
-
public String getMapperNameSuffix() {
return mapperNameSuffix;
}
@@ -277,4 +285,12 @@ public class AutoMapperMetadata extends AbstractMapperMetadata {
public void setMappingControl(ClassName mappingControl) {
this.mappingControl = mappingControl;
}
+
+ public Set getDependencies() {
+ return dependencies;
+ }
+
+ public void setDependencies(Set dependencies) {
+ this.dependencies = dependencies;
+ }
}
diff --git a/mapstruct-plus-processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor b/mapstruct-plus-processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor
index a1449122d9e2a15da94620a0d548753d1714e5e6..42fded8ff2610f654e118729f79eddbfde81f8dc 100644
--- a/mapstruct-plus-processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor
+++ b/mapstruct-plus-processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor
@@ -11,4 +11,5 @@ org.mapstruct.ap.internal.processor.MapperRenderingProcessor
org.mapstruct.ap.internal.processor.MethodRetrievalProcessor
org.mapstruct.ap.internal.processor.SpringComponentProcessor
org.mapstruct.ap.internal.processor.MapperServiceProcessor
-io.github.linpeilie.processor.solon.SolonComponentProcessor
+io.github.linpeilie.processor.enhance.processor.SolonComponentProcessor
+io.github.linpeilie.processor.enhance.processor.SpringComponentProcessor
diff --git a/mapstruct-plus-processor/src/main/resources/io/github/linpeilie/processor/enhance/model/SpringDelayInjectMapperReference.ftl b/mapstruct-plus-processor/src/main/resources/io/github/linpeilie/processor/enhance/model/SpringDelayInjectMapperReference.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..f84aa7d2ad91099d56f00778e58daf4730942ead
--- /dev/null
+++ b/mapstruct-plus-processor/src/main/resources/io/github/linpeilie/processor/enhance/model/SpringDelayInjectMapperReference.ftl
@@ -0,0 +1,2 @@
+<#-- @ftlvariable name="" type="io.github.linpeilie.processor.enhance.model.SpringDelayInjectMapperReference" -->
+private <@includeModel object=type/> ${variableName} = SpringContextUtils.getBean("${variableName}", <@includeModel object=type/>.class);
\ No newline at end of file
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 23743f506990930621e8c65b5afd9964dbed1d49..28c7a06cb5ac248ac50546dfba2c0dae77962831 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
@@ -24,4 +24,9 @@ public class MapstructAutoConfiguration {
return new Converter(converterFactory);
}
+ @Bean
+ public SpringContextUtils springContextUtils() {
+ return new SpringContextUtils();
+ }
+
}
diff --git a/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/SpringContextUtils.java b/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/SpringContextUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdb201aeaa34766a0da493687575cd60026c0b6b
--- /dev/null
+++ b/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/SpringContextUtils.java
@@ -0,0 +1,68 @@
+package io.github.linpeilie.mapstruct;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public class SpringContextUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
+
+ private static ConfigurableListableBeanFactory beanFactory;
+ private static ApplicationContext applicationContext;
+
+ public SpringContextUtils() {
+ }
+
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ SpringContextUtils.beanFactory = beanFactory;
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ SpringContextUtils.applicationContext = applicationContext;
+ }
+
+ public static ApplicationContext getApplicationContext() {
+ return applicationContext;
+ }
+
+ public static ListableBeanFactory getBeanFactory() {
+ ListableBeanFactory factory = null == beanFactory ? applicationContext : beanFactory;
+ if (null == factory) {
+ throw new RuntimeException("No ConfigurableListableBeanFactory or ApplicationContext injected, maybe not in the Spring environment?");
+ } else {
+ return (ListableBeanFactory)factory;
+ }
+ }
+
+ public static ConfigurableListableBeanFactory getConfigurableBeanFactory() {
+ ConfigurableListableBeanFactory factory;
+ if (null != beanFactory) {
+ factory = beanFactory;
+ } else {
+ if (!(applicationContext instanceof ConfigurableApplicationContext)) {
+ throw new RuntimeException("No ConfigurableListableBeanFactory from context!");
+ }
+
+ factory = ((ConfigurableApplicationContext)applicationContext).getBeanFactory();
+ }
+
+ return factory;
+ }
+
+ public static T getBean(Class clazz) {
+ return getBeanFactory().getBean(clazz);
+ }
+
+ public static T getBean(String name, Class clazz) {
+ try {
+ return getBean(clazz);
+ } catch (NoUniqueBeanDefinitionException e) {
+ return getBeanFactory().getBean(name, clazz);
+ }
+ }
+
+}
diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java
index 75ba1f62f14766d05ae0e1ac7546fbeed46f1966..48c7773a946001b55fcc91e0bddbdac195bf034e 100644
--- a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java
+++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java
@@ -1,6 +1,5 @@
package io.github.linpeilie;
-import io.github.linpeilie.annotations.DoIgnore;
import java.util.List;
import java.util.stream.Collectors;
import org.mapstruct.Context;
@@ -8,13 +7,10 @@ import org.mapstruct.MappingTarget;
public interface BaseCycleAvoidingMapper extends BaseMapper {
- @DoIgnore
T convert(S source, @Context CycleAvoidingMappingContext context);
- @DoIgnore
T convert(S source, @MappingTarget T target, @Context CycleAvoidingMappingContext context);
- @DoIgnore
default List convert(List sourceList, @Context CycleAvoidingMappingContext context) {
return sourceList.stream()
.map(item -> convert(item, context))
@@ -22,20 +18,17 @@ public interface BaseCycleAvoidingMapper extends BaseMapper {
}
@Override
- @DoIgnore
default T convert(S source) {
return convert(source, new CycleAvoidingMappingContext());
}
@Override
- @DoIgnore
default T convert(S source, @MappingTarget T target) {
return convert(source, new CycleAvoidingMappingContext());
}
@Override
- @DoIgnore
default List convert(List sourceList) {
return convert(sourceList, new CycleAvoidingMappingContext());
}
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 a89fa3e36f6082ecc9144e54022a6c0cc9a20c7c..eec5536c2b627fa7d60ef2d13485c21abd8271a8 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,5 @@
package io.github.linpeilie;
-import io.github.linpeilie.annotations.DoIgnore;
import io.github.linpeilie.utils.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
@@ -9,13 +8,10 @@ import org.mapstruct.MappingTarget;
public interface BaseMapper {
- @DoIgnore
T convert(S source);
- @DoIgnore
T convert(S source, @MappingTarget T target);
- @DoIgnore
default List convert(List sourceList) {
if (CollectionUtils.isEmpty(sourceList)) {
return new ArrayList<>();
diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/ComponentModelConfig.java b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/ComponentModelConfig.java
index d63c97c8a0bc99db9c9232d76a9b1388bc4a1dc0..4e64a3e728e6885646231473c5d4adcd3665857c 100644
--- a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/ComponentModelConfig.java
+++ b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/ComponentModelConfig.java
@@ -9,6 +9,6 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentModelConfig {
- String componentModel() default "spring";
+ String componentModel() default "spring-lazy";
}
diff --git a/pom.xml b/pom.xml
index 37086e05a3f07998de26935e4d317e72ddb65799..f155c129bb9adcdf994166721473ef165a492d66 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
- 1.4.2
+ 1.4.3
8
8
UTF-8