mirror of
https://gitee.com/easii/mapstruct-plus.git
synced 2025-12-07 01:28:31 +08:00
commit
027d868d11
@ -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>
|
||||
|
||||
@ -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 注解
|
||||
|
||||
……
|
||||
|
||||
## 代码仓库
|
||||
|
||||
@ -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`.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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` 属性,支持配置生成的转换接口名称增加后缀,默认规则下生成的反向转换接口同时生效;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ public class AutoMapperProperties {
|
||||
|
||||
private static String unmappedSourcePolicy;
|
||||
|
||||
private static String unmappedTargetPolicy;
|
||||
private static String unmappedTargetPolicy = "IGNORE";
|
||||
|
||||
private static String typeConversionPolicy;
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
@ -24,4 +24,9 @@ public class MapstructAutoConfiguration {
|
||||
return new Converter(converterFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringContextUtils springContextUtils() {
|
||||
return new SpringContextUtils();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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<>();
|
||||
|
||||
@ -9,6 +9,6 @@ import java.lang.annotation.Target;
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ComponentModelConfig {
|
||||
|
||||
String componentModel() default "spring";
|
||||
String componentModel() default "spring-lazy";
|
||||
|
||||
}
|
||||
|
||||
2
pom.xml
2
pom.xml
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user