Merge pull request #90 from linpeilie/1.4.3

1.4.3
This commit is contained in:
easii 2024-06-29 13:58:10 +08:00 committed by GitHub
commit 027d868d11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 467 additions and 93 deletions

View File

@ -188,7 +188,7 @@ public class User {
```xml
<properties>
<mapstruct-plus.version>1.4.2</mapstruct-plus.version>
<mapstruct-plus.version>1.4.3</mapstruct-plus.version>
</properties>
<dependencies>
<dependency>

View File

@ -62,18 +62,24 @@ footer:
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>1.4.2</version>
<version>1.4.3</version>
</dependency>
```
- 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 注解
……
## 代码仓库

View File

@ -58,26 +58,26 @@ fotter:
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>1.4.0</version>
<version>1.4.3</version>
</dependency>
```
- 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.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
Sure, here is the translated update document:
---
### Updates
- **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`.
- **feat**: Adapted the `Mapping` annotation to support the following attributes: `constant`, `qualifiedBy`, `nullValueCheckStrategy`, `nullValuePropertyMappingStrategy`, and `mappingControl`.

View File

@ -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.

View File

@ -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` 属性,支持配置生成的转换接口名称增加后缀,默认规则下生成的反向转换接口同时生效;

View File

@ -18,7 +18,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mapstruct.version>1.5.1.Final</mapstruct.version>
<mapstruct-plus.version>1.4.2</mapstruct-plus.version>
<mapstruct-plus.version>1.4.3</mapstruct-plus.version>
<lombok.version>1.18.22</lombok.version>
<hutool.version>5.8.26</hutool.version>
<guava.version>32.1.3-jre</guava.version>

View File

@ -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();
}

View File

@ -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();
}
}
}

View File

@ -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<String> mapperSet = new HashSet<>();
private static final Map<String, Integer> AUTO_MAPPER_INDEX = new HashMap<>();
private final Map<String, List<ClassName>> 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<AutoMapperMetadata> generateReverseConverters() {
List<AutoMapperMetadata> 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<TypeName> dependencies = metadata.getDependencies();
if (CollectionUtils.isNotEmpty(dependencies)) {
List<ClassName> 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<ClassName> 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<TypeName> listDependencies(TypeElement autoMapperEle) {
Set<TypeName> 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<TypeName> 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<TypeName> 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);

View File

@ -15,7 +15,7 @@ public class AutoMapperProperties {
private static String unmappedSourcePolicy;
private static String unmappedTargetPolicy;
private static String unmappedTargetPolicy = "IGNORE";
private static String typeConversionPolicy;

View File

@ -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 {

View File

@ -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<Type> getImportTypes() {
Set<Type> importTypes = new HashSet<>();
importTypes.add(getType());
importTypes.add(springContextUtil);
return importTypes;
}
}

View File

@ -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;

View File

@ -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<Annotation> getTypeAnnotations(Mapper mapper) {
return CollectionUtils.newArrayList(component());
}
@Override
protected List<Annotation> getMapperReferenceAnnotations() {
return Collections.emptyList();
}
@Override
protected boolean requiresGenerationOfDecoratorClass() {
return true;
}
@Override
protected Field replacementMapperReference(Field originalReference,
List<Annotation> annotations,
InjectionStrategyGem injectionStrategy) {
return new SpringDelayInjectMapperReference(originalReference.getType(), originalReference.getVariableName(),
originalReference.isUsed(),
getTypeFactory().getType("io.github.linpeilie.mapstruct.SpringContextUtils"));
}
}

View File

@ -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<String, Integer> 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()) {
builder.addMethod(addConvertMethodSpec(
metadata.isCycleAvoiding() ? CollectionUtils.newArrayList(source, context) : Collections.singletonList(
source),
metadata.getFieldMappingList(),
targetClassName,
CONVERT_METHOD_NAME));
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.getFieldMappingList(),
targetClassName,
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<ParameterSpec> parameterSpecs,
List<AutoMappingMetadata> 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<ParameterSpec> 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();
}

View File

@ -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);

View File

@ -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<TypeName> dependencies;
public String qualifiedMapperName() {
return mapperPackage() + "." + mapperName();
}
public boolean addUseList(List<ClassName> 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<TypeName> getDependencies() {
return dependencies;
}
public void setDependencies(Set<TypeName> dependencies) {
this.dependencies = dependencies;
}
}

View File

@ -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

View File

@ -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);

View File

@ -24,4 +24,9 @@ public class MapstructAutoConfiguration {
return new Converter(converterFactory);
}
@Bean
public SpringContextUtils springContextUtils() {
return new SpringContextUtils();
}
}

View File

@ -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> T getBean(Class<T> clazz) {
return getBeanFactory().getBean(clazz);
}
public static <T> T getBean(String name, Class<T> clazz) {
try {
return getBean(clazz);
} catch (NoUniqueBeanDefinitionException e) {
return getBeanFactory().getBean(name, clazz);
}
}
}

View File

@ -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<S, T> extends BaseMapper<S, T> {
@DoIgnore
T convert(S source, @Context CycleAvoidingMappingContext context);
@DoIgnore
T convert(S source, @MappingTarget T target, @Context CycleAvoidingMappingContext context);
@DoIgnore
default List<T> convert(List<S> sourceList, @Context CycleAvoidingMappingContext context) {
return sourceList.stream()
.map(item -> convert(item, context))
@ -22,20 +18,17 @@ public interface BaseCycleAvoidingMapper<S, T> extends BaseMapper<S, T> {
}
@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<T> convert(List<S> sourceList) {
return convert(sourceList, new CycleAvoidingMappingContext());
}

View File

@ -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<S, T> {
@DoIgnore
T convert(S source);
@DoIgnore
T convert(S source, @MappingTarget T target);
@DoIgnore
default List<T> convert(List<S> sourceList) {
if (CollectionUtils.isEmpty(sourceList)) {
return new ArrayList<>();

View File

@ -9,6 +9,6 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentModelConfig {
String componentModel() default "spring";
String componentModel() default "spring-lazy";
}

View File

@ -18,7 +18,7 @@
</modules>
<properties>
<mapstruct-plus.version>1.4.2</mapstruct-plus.version>
<mapstruct-plus.version>1.4.3</mapstruct-plus.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>