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()) { switch (AutoMapperProperties.getComponentModel()) {
case MappingConstants.ComponentModel.SPRING: case MappingConstants.ComponentModel.SPRING:
case ContextConstants.ComponentModelConfig.springLazy:
this.adapterMapperGenerator = new SpringAdapterMapperGenerator(); this.adapterMapperGenerator = new SpringAdapterMapperGenerator();
break; break;
case ComponentModelConstant.SOLON: case ComponentModelConstant.SOLON:

View File

@ -7,12 +7,12 @@ import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeSpec;
import io.github.linpeilie.processor.ContextConstants; import io.github.linpeilie.processor.ContextConstants;
import io.github.linpeilie.processor.metadata.AutoMapperMetadata; import io.github.linpeilie.processor.metadata.AutoMapperMetadata;
import io.github.linpeilie.processor.metadata.AutoMappingMetadata; import io.github.linpeilie.processor.metadata.AutoMappingMetadata;
import io.github.linpeilie.utils.CollectionUtils; import io.github.linpeilie.utils.CollectionUtils;
import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -55,7 +55,7 @@ public class AutoMapperGenerator {
JavaFile.builder(metadata.mapperPackage(), createTypeSpec(processingEnv, metadata, mapperName)) JavaFile.builder(metadata.mapperPackage(), createTypeSpec(processingEnv, metadata, mapperName))
.build() .build()
.writeTo(writer); .writeTo(writer);
} catch (IOException e) { } catch (Exception e) {
processingEnv.getMessager() processingEnv.getMessager()
.printMessage(ERROR, .printMessage(ERROR,
"Error while opening " + metadata.mapperName() + " output file: " + e.getMessage()); "Error while opening " + metadata.mapperName() + " output file: " + e.getMessage());
@ -79,19 +79,69 @@ public class AutoMapperGenerator {
ParameterSpec target = ParameterSpec.builder(targetClassName, "target") ParameterSpec target = ParameterSpec.builder(targetClassName, "target")
.addAnnotation(AnnotationSpec.builder(ClassName.get("org.mapstruct", "MappingTarget")).build()) .addAnnotation(AnnotationSpec.builder(ClassName.get("org.mapstruct", "MappingTarget")).build())
.build(); .build();
ParameterSpec sourceList = ParameterSpec.builder(
ParameterizedTypeName.get(
ClassName.get("java.util", "List"),
metadata.getSourceClassName()
), "sourceList").build();
ParameterSpec context = ParameterSpec context =
ParameterSpec.builder(ClassName.get("io.github.linpeilie", "CycleAvoidingMappingContext"), "context") ParameterSpec.builder(ClassName.get("io.github.linpeilie", "CycleAvoidingMappingContext"), "context")
.addAnnotation(ClassName.get("org.mapstruct", "Context")) .addAnnotation(ClassName.get("org.mapstruct", "Context"))
.build(); .build();
if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) { ParameterizedTypeName targetList = ParameterizedTypeName.get(
builder.addMethod(addConvertMethodSpec( ClassName.get("java.util", "List"),
metadata.isCycleAvoiding() ? CollectionUtils.newArrayList(source, context) : Collections.singletonList( targetClassName
source), );
metadata.getFieldMappingList(),
targetClassName, // 如果需要避免循环依赖则把 BaseMapper 中的实现全部添加 DoIgnore 防止使用该方法进行转换
CONVERT_METHOD_NAME)); 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); boolean targetIsImmutable = classIsImmutable(processingEnv, targetClassName);
if (targetIsImmutable) { if (targetIsImmutable) {
builder.addMethod( builder.addMethod(
@ -100,13 +150,15 @@ public class AutoMapperGenerator {
context) : CollectionUtils.newArrayList(source, target), context) : CollectionUtils.newArrayList(source, target),
targetClassName, targetClassName,
CONVERT_METHOD_NAME)); CONVERT_METHOD_NAME));
} else if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) { } else if (CollectionUtils.isNotEmpty(metadata.getFieldMappingList()) || metadata.isCycleAvoiding()) {
builder.addMethod(addConvertMethodSpec( builder.addMethod(addConvertMethodSpec(
metadata.isCycleAvoiding() ? CollectionUtils.newArrayList(source, target, metadata.isCycleAvoiding()
context) : CollectionUtils.newArrayList(source, target), ? CollectionUtils.newArrayList(source, target, context)
: CollectionUtils.newArrayList(source, target),
metadata.getFieldMappingList(), metadata.getFieldMappingList(),
targetClassName, targetClassName,
CONVERT_METHOD_NAME)); CONVERT_METHOD_NAME,
metadata.isCycleAvoiding()));
} }
return builder.build(); return builder.build();
@ -137,15 +189,50 @@ public class AutoMapperGenerator {
private MethodSpec addConvertMethodSpec(List<ParameterSpec> parameterSpecs, private MethodSpec addConvertMethodSpec(List<ParameterSpec> parameterSpecs,
List<AutoMappingMetadata> autoMappingMetadataList, List<AutoMappingMetadata> autoMappingMetadataList,
ClassName target, String methodName) { ClassName target,
String methodName,
boolean cycleAvoiding) {
final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodName) final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodName)
.addParameters(parameterSpecs) .addParameters(parameterSpecs)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addAnnotation(ClassName.get(ContextConstants.DoIgnore.packageName, ContextConstants.DoIgnore.className))
.returns(target); .returns(target);
if (CollectionUtils.isNotEmpty(autoMappingMetadataList)) { if (CollectionUtils.isNotEmpty(autoMappingMetadataList)) {
methodSpecBuilder.addAnnotations(buildMappingAnnotations(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(); return methodSpecBuilder.build();
} }

View File

@ -1,6 +1,5 @@
package io.github.linpeilie; package io.github.linpeilie;
import io.github.linpeilie.annotations.DoIgnore;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.mapstruct.Context; import org.mapstruct.Context;
@ -8,13 +7,10 @@ import org.mapstruct.MappingTarget;
public interface BaseCycleAvoidingMapper<S, T> extends BaseMapper<S, T> { public interface BaseCycleAvoidingMapper<S, T> extends BaseMapper<S, T> {
@DoIgnore
T convert(S source, @Context CycleAvoidingMappingContext context); T convert(S source, @Context CycleAvoidingMappingContext context);
@DoIgnore
T convert(S source, @MappingTarget T target, @Context CycleAvoidingMappingContext context); T convert(S source, @MappingTarget T target, @Context CycleAvoidingMappingContext context);
@DoIgnore
default List<T> convert(List<S> sourceList, @Context CycleAvoidingMappingContext context) { default List<T> convert(List<S> sourceList, @Context CycleAvoidingMappingContext context) {
return sourceList.stream() return sourceList.stream()
.map(item -> convert(item, context)) .map(item -> convert(item, context))
@ -22,20 +18,17 @@ public interface BaseCycleAvoidingMapper<S, T> extends BaseMapper<S, T> {
} }
@Override @Override
@DoIgnore
default T convert(S source) { default T convert(S source) {
return convert(source, new CycleAvoidingMappingContext()); return convert(source, new CycleAvoidingMappingContext());
} }
@Override @Override
@DoIgnore
default T convert(S source, @MappingTarget T target) { default T convert(S source, @MappingTarget T target) {
return convert(source, new CycleAvoidingMappingContext()); return convert(source, new CycleAvoidingMappingContext());
} }
@Override @Override
@DoIgnore
default List<T> convert(List<S> sourceList) { default List<T> convert(List<S> sourceList) {
return convert(sourceList, new CycleAvoidingMappingContext()); return convert(sourceList, new CycleAvoidingMappingContext());
} }

View File

@ -1,6 +1,5 @@
package io.github.linpeilie; package io.github.linpeilie;
import io.github.linpeilie.annotations.DoIgnore;
import io.github.linpeilie.utils.CollectionUtils; import io.github.linpeilie.utils.CollectionUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -9,13 +8,10 @@ import org.mapstruct.MappingTarget;
public interface BaseMapper<S, T> { public interface BaseMapper<S, T> {
@DoIgnore
T convert(S source); T convert(S source);
@DoIgnore
T convert(S source, @MappingTarget T target); T convert(S source, @MappingTarget T target);
@DoIgnore
default List<T> convert(List<S> sourceList) { default List<T> convert(List<S> sourceList) {
if (CollectionUtils.isEmpty(sourceList)) { if (CollectionUtils.isEmpty(sourceList)) {
return new ArrayList<>(); return new ArrayList<>();