modified the approach to avoid circular dependencies.

This commit is contained in:
linpeilie 2024-06-26 16:47:08 +08:00
parent b12cd2b14a
commit 9bfe62a0c2
4 changed files with 103 additions and 26 deletions

View File

@ -180,6 +180,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
// 根据配置生成适配类生成器
switch (AutoMapperProperties.getComponentModel()) {
case MappingConstants.ComponentModel.SPRING:
case ContextConstants.ComponentModelConfig.springLazy:
this.adapterMapperGenerator = new SpringAdapterMapperGenerator();
break;
case ComponentModelConstant.SOLON:

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;
@ -55,7 +55,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 +79,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 +150,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 +189,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

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