lombok与mapstruct冲突的问题
现象
在生成的mapper方法中,没有调用实体类的getter和setter
解决方案
配置plugin时需要加入lombok-mapstruct-binding
依赖
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<!-- additional annotation processor required as of Lombok 1.18.16 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
原因分析
lombok基于AST(抽象语法树)修改实现。
mapstruct基于生成代码文件实现。
实体类中使用了lombok注解,mapstruct需要在lombok的AST(抽象语法树)修改完成之后生成代码文件才行。
lombok-mapstruct-binding
定义了事件通知器,在lombok修改完成后才会开始执行mapstruct的处理。
代码分析
class NotifierHider {
public static class AstModificationNotifier implements AstModifyingAnnotationProcessor {
private static Field lombokInvoked;
//该方法判断lombok的处理是否完成
@Override public boolean isTypeComplete(TypeMirror type) {
if (System.getProperty("lombok.disable") != null) return true;
return isLombokInvoked();
}
private static boolean isLombokInvoked() {
if (lombokInvoked != null) {
try {
return lombokInvoked.getBoolean(null);
} catch (Exception e) {}
return true;
}
try {
Class<?> data = Class.forName("lombok.launch.AnnotationProcessorHider$AstModificationNotifierData");
lombokInvoked = data.getField("lombokInvoked");
return lombokInvoked.getBoolean(null);
} catch (Exception e) {}
return true;
}
}
}
在mapstruct 注解解析器生成mapper代码时会调用org.mapstruct.ap.internal.model.common.TypeFactory#canBeProcessed
来判断lombok是否处理完成,如果未处理完成会抛出TypeHierarchyErroneousException
异常,代码如下:
if ( !canBeProcessed( mirror ) ) {
throw new TypeHierarchyErroneousException( mirror );
}
在代码外层捕获到这个异常后会进行如下操作:
try {
//省略,处理mapper代码的生成
}
catch ( TypeHierarchyErroneousException thie ) {
TypeMirror erroneousType = thie.getType();
Element erroneousElement = erroneousType != null ? roundContext.getAnnotationProcessorContext()
.getTypeUtils()
.asElement( erroneousType ) : null;
if ( options.isVerbose() ) {
processingEnv.getMessager().printMessage(
Kind.NOTE, "MapStruct: referred types not available (yet), deferring mapper: "
+ mapperElement );
}
deferredMappers.add( new DeferredMapper( mapperElement, erroneousElement ) );
}
将未处理的mapper放入deferredMappers集合中,在processor的下一轮process时会处理deferredMappers中的元素。