MapStruct介绍
mapstruct是一种实体类映射框架,能够通过Java注解将一个实体类的属性安全地赋值给另一个实体类。有了mapstruct,只需要定义一个映射器接口,声明需要映射的方法,在编译过程中,mapstruct会自动生成该接口的实现类,实现将源对象映射到目标对象的效果。
其他映射框架对比
实体类映射框架大致有两种:一种是运行期通过java反射机制动态映射,如使用BeanUtils, BeanCopier 等工具做JavaBean对象转换;另一种是编译期动态生成getter/setter,在运行期直接调用框架编译好的class类实现实体映射。
由于mapstruct映射是在编译期间实现的,因此相比运行期的映射框架有以下几个优点:
- 安全性高。因为是编译期就实现源对象到目标对象的映射, 如果编译器能够通过,运行期就不会报错。
- 速度快。速度快指的是运行期间直接调用实现类的方法,不会在运行期间使用反射进行转化。
Mapstruct 入门
maven坐标地址
首先我们要引入mapstruct 的jar 包,它的maven 坐标地址如下:
<!--mapStruct依赖 自动生成VO-DO的模型映射-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
<scope>provided</scope>
</dependency>
Start Mapstruct
mapstruct的用法很简单,假设我们有两个实体类BookCategoryDO和BookCategoryDTO,类定义如下:
- BookCategoryDO 定义
@Data
@TableName("book_category")
public class BookCategoryDO {
private Integer id;
/**
* 分类名称
*/
private String categoryName;
/**
* 书籍频道 1-男频;2-女频
*/
private Integer channelType;
/**
* 排序
*/
private Integer sort;
/**
* 状态(0启用,1停用)
*/
private Integer status;
/**
* 创建者
*/
private String creator;
/**
* 创建时间
*/
private Date createTime;
/**
* 最后编辑人
*/
private String updater;
/**
* 最后修改时间
*/
private Date updateTime;
/**
* 产品类型
*/
private Integer productType;
/**
* 父节点ID
*/
private Integer parentId;
}
- BookCategoryDTO 定义
/**
* 书籍类别定义
*/
@Data
public class BookCategoryDTO implements DTO {
private Integer id;
/**
* 分类名称
*/
private String categoryName;
/**
* 书籍频道 1-男频;2-女频
*/
private Integer channelType;
/**
* 排序
*/
private Integer sort;
/**
* 产品类型
*/
private Integer productType;
/**
* 父节点ID
*/
private Integer parentId;
}
-
Assembler<T, R> 转换接口
public interface Assembler<T, R> extends Function<T, R> {
List<R> toList(List<T> var1);
}
-
然后需要定义一个用例映射的接口BookCategoryDTOAssembler,接口如下:
/**
* 书籍类别转换器
*/
@Mapper(componentModel = "spring")
public interface BookCategoryDTOAssembler extends Assembler<BookCategoryDO, BookCategoryDTO> {
}
- 定义单元测试MapstructTest
public class MapstructTest extends BaseTest { @Resource private BookCategoryDTOAssembler bookCategoryDTOAssembler; @Test public void toBookCategoryDTO() { BookCategoryDO bookCategoryDO = new BookCategoryDO(); bookCategoryDO.setId(1); bookCategoryDO.setCategoryName("都市"); bookCategoryDO.setParentId(0); bookCategoryDO.setProductType(1); BookCategoryDTO dto = bookCategoryDTOAssembler.apply(bookCategoryDO); System.out.println(dto); } }
运行结果:BookCategoryDTO(id=1, categoryName=都市, channelType=null, sort=null, productType=1, parentId=0)
Mapper 获取
- 通过 Mapper 工厂获取
通过 Mappers.getMapper(xxx.class) 的方式来进行对应 Mapper 的获取。此种方法为通过 Mapper 工厂获取。@Mapping——一对映射关系
名称 | 描述 |
target | 目标属性,赋值的过程是把“源属性”赋值给“目标属性” |
source | 源属性,赋值的过程是把“源属性”赋值给“目标属性” |
dateFormat | 用于源属性是Date,转化为String |
numberFormat | 用户数值类型与String类型之间的转化 |
constant | 不管源属性,直接将“目标属性”置为常亮 |
expression | 使用表达式进行属性之间的转化 |
ignore | 忽略某个属性的赋值; |
qualifiedByName | 根据自定义的方法进行赋值; |
defaultValue | 默认值; |
expression 的使用
@Mapping(expression = "java(null == MyObjectDetailMapper.getLastOne(myObject) ? null : MyObjectDetailMapper.getLastOne(myObject).getNumber())", target = "number"),
/**
* 书籍推请求转换
*
*/
@Mapper(componentModel = "spring",imports = UUID.class)
public interface BookRecommendQueryAssembler extends Function<BookPopularityPageQuery, BookRecommendReq> {
@Mappings({
@Mapping(target = "reqId", expression = "java(UUID.randomUUID().toString())")
})
BookRecommendReq apply(BookPopularityPageQuery query);
}
constant 使用
/**
* 用户书架转换类
*/
@Mapper(componentModel = "spring", imports = {ChannelTypeEnum.class, BookSerialStatusEnum.class})
public interface UserBookShelfDTOAssembler extends Assembler<BookEs, UserBookShelfDTO> {
@Mappings({
@Mapping(target = "bookId", source = "id"),
@Mapping(target = "readingProgress", constant = "未读"),
@Mapping(target = "channelName", expression = "java(ChannelTypeEnum.getTitleByValue(bookEs.getChannelType()))"),
@Mapping(target = "serialStatusName", expression = "java(BookSerialStatusEnum.getTitleByValue(bookEs.getSerialStatus()))"),
@Mapping(target = "twoCategory.id", source = "bookEs.twoCategoryId"),
@Mapping(target = "twoCategory.categoryName", source = "bookEs.twoCategoryName"),
@Mapping(target = "threeCategory.id", source = "bookEs.threeCategoryId"),
@Mapping(target = "threeCategory.categoryName", source = "bookEs.threeCategoryName")
})
UserBookShelfDTO apply(BookEs bookEs);
- 使用依赖注入
对于 Web 开发,依赖注入应该很熟悉。MapSturct 也支持使用依赖注入,同时也推荐使用依赖注入。componentModel就是依赖注入,类似于在spring的servie层用@servie注入,那么在其他地方可以使用@Autowired取到值。该属性可取的值为
componentModel | 描述 |
default | 这个就是经常使用的 xxxMapper.INSTANCE.xxx |
cdi | 使用该属性,则在其他地方可以使用@Inject取到值 |
spring | 使用该属性,则在其他地方可以使用@Autowired取到值 |
jsr330/Singleton | 使用者两个属性,可以再其他地方使用@Inject取到值 |
Mapstruct实现原理
生成的字节码
查看target下Mapstruct生成的字节码文件BookCategoryDTOAssemblerImpl.class,它实现接口BookCategoryDTOAssembler。BookCategoryDTOAssemblerImpl源代码:
@Component
public class BookCategoryDTOAssemblerImpl implements BookCategoryDTOAssembler {
public BookCategoryDTOAssemblerImpl() {
}
public BookCategoryDTO apply(BookCategoryDO arg0) {
if (arg0 == null) {
return null;
} else {
BookCategoryDTO bookCategoryDTO = new BookCategoryDTO();
bookCategoryDTO.setId(arg0.getId());
bookCategoryDTO.setCategoryName(arg0.getCategoryName());
bookCategoryDTO.setChannelType(arg0.getChannelType());
bookCategoryDTO.setSort(arg0.getSort());
bookCategoryDTO.setProductType(arg0.getProductType());
bookCategoryDTO.setParentId(arg0.getParentId());
return bookCategoryDTO;
}
}
public List<BookCategoryDTO> toList(List<BookCategoryDO> arg0) {
if (arg0 == null) {
return null;
} else {
List<BookCategoryDTO> list = new ArrayList(arg0.size());
Iterator var3 = arg0.iterator();
while(var3.hasNext()) {
BookCategoryDO bookCategoryDO = (BookCategoryDO)var3.next();
list.add(this.apply(bookCategoryDO));
}
return list;
}
}
}
MapStruct优点
- 性能高:这是相对反射来说的,反射需要去读取字节码的内容,花销会比较大。而通过 MapStruct 来生成的代码,其类似于人手写。速度上可以得到保证。
- 使用简单:如果是完全映射的,使用起来肯定没有反射简单。用类似 BeanUtils 这些工具一条语句就搞定了。但是,如果需要进行特殊的匹配(特殊类型转换,多对一转换等),其相对来说也是比较简单的。基本上,使用的时候,我们只需要声明一个接口,接口下写对应的方法,就可以使用了。
Java程序编译流程
Java源码到class文件的过程其实是一个比较复杂的过程。其中的经过可以用下图描述
com.sun.tools.javac.main.JavaCompiler#compile
public void compile(List<JavaFileObject> var1, List<String> var2, Iterable<? extends Processor> var3) {
if (var3 != null && var3.iterator().hasNext()) {
this.explicitAnnotationProcessingRequested = true;
}
if (this.hasBeenUsed) {
throw new AssertionError("attempt to reuse JavaCompiler");
} else {
this.hasBeenUsed = true;
this.options.put(Option.XLINT_CUSTOM.text + "-" + LintCategory.OPTIONS.option, "true");
this.options.remove(Option.XLINT_CUSTOM.text + LintCategory.OPTIONS.option);
this.start_msec = now();
try {
this.initProcessAnnotations(var3);
this.delegateCompiler = this.processAnnotations(this.enterTrees(this.stopIfError(CompileState.PARSE, this.parseFiles(var1))), var2);
this.delegateCompiler.compile2();
this.delegateCompiler.close();
this.elapsed_msec = this.delegateCompiler.elapsed_msec;
} catch (Abort var8) {
if (this.devVerbose) {
var8.printStackTrace(System.err);
}
} finally {
if (this.procEnvImpl != null) {
this.procEnvImpl.close();
}
}
}
}
org.mapstruct.ap.MappingProcessor
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.mapstruct.ap;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementKindVisitor6;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.internal.gem.MapperGem;
import org.mapstruct.ap.internal.gem.ReportingPolicyGem;
import org.mapstruct.ap.internal.option.Options;
import org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext;
import org.mapstruct.ap.internal.processor.ModelElementProcessor;
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
import org.mapstruct.ap.internal.util.AnnotationProcessorContext;
import org.mapstruct.ap.internal.util.RoundContext;
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
@SupportedAnnotationTypes({"org.mapstruct.Mapper"})
@SupportedOptions({"mapstruct.suppressGeneratorTimestamp", "mapstruct.suppressGeneratorVersionInfoComment", "mapstruct.unmappedTargetPolicy", "mapstruct.unmappedSourcePolicy", "mapstruct.defaultComponentModel", "mapstruct.defaultInjectionStrategy", "mapstruct.disableBuilders", "mapstruct.verbose"})
public class MappingProcessor extends AbstractProcessor {
private static final boolean ANNOTATIONS_CLAIMED_EXCLUSIVELY = false;
protected static final String SUPPRESS_GENERATOR_TIMESTAMP = "mapstruct.suppressGeneratorTimestamp";
protected static final String SUPPRESS_GENERATOR_VERSION_INFO_COMMENT = "mapstruct.suppressGeneratorVersionInfoComment";
protected static final String UNMAPPED_TARGET_POLICY = "mapstruct.unmappedTargetPolicy";
protected static final String UNMAPPED_SOURCE_POLICY = "mapstruct.unmappedSourcePolicy";
protected static final String DEFAULT_COMPONENT_MODEL = "mapstruct.defaultComponentModel";
protected static final String DEFAULT_INJECTION_STRATEGY = "mapstruct.defaultInjectionStrategy";
protected static final String ALWAYS_GENERATE_SERVICE_FILE = "mapstruct.alwaysGenerateServicesFile";
protected static final String DISABLE_BUILDERS = "mapstruct.disableBuilders";
protected static final String VERBOSE = "mapstruct.verbose";
private Options options;
private AnnotationProcessorContext annotationProcessorContext;
private Set<DeferredMapper> deferredMappers = new HashSet();
public MappingProcessor() {
}
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.options = this.createOptions();
this.annotationProcessorContext = new AnnotationProcessorContext(processingEnv.getElementUtils(), processingEnv.getTypeUtils(), processingEnv.getMessager(), this.options.isDisableBuilders(), this.options.isVerbose());
}
private Options createOptions() {
String unmappedTargetPolicy = (String)this.processingEnv.getOptions().get("mapstruct.unmappedTargetPolicy");
String unmappedSourcePolicy = (String)this.processingEnv.getOptions().get("mapstruct.unmappedSourcePolicy");
return new Options(Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.suppressGeneratorTimestamp")), Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.suppressGeneratorVersionInfoComment")), unmappedTargetPolicy != null ? ReportingPolicyGem.valueOf(unmappedTargetPolicy.toUpperCase()) : null, unmappedSourcePolicy != null ? ReportingPolicyGem.valueOf(unmappedSourcePolicy.toUpperCase()) : null, (String)this.processingEnv.getOptions().get("mapstruct.defaultComponentModel"), (String)this.processingEnv.getOptions().get("mapstruct.defaultInjectionStrategy"), Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.alwaysGenerateServicesFile")), Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.disableBuilders")), Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.verbose")));
}
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
if (!roundEnvironment.processingOver()) {
RoundContext roundContext = new RoundContext(this.annotationProcessorContext);
Set<TypeElement> deferredMappers = this.getAndResetDeferredMappers();
this.processMapperElements(deferredMappers, roundContext);
Set<TypeElement> mappers = this.getMappers(annotations, roundEnvironment);
this.processMapperElements(mappers, roundContext);
} else if (!this.deferredMappers.isEmpty()) {
Iterator var8 = this.deferredMappers.iterator();
while(var8.hasNext()) {
DeferredMapper deferredMapper = (DeferredMapper)var8.next();
TypeElement deferredMapperElement = deferredMapper.deferredMapperElement;
Element erroneousElement = deferredMapper.erroneousElement;
String erroneousElementName;
if (erroneousElement instanceof QualifiedNameable) {
erroneousElementName = ((QualifiedNameable)erroneousElement).getQualifiedName().toString();
} else {
erroneousElementName = erroneousElement != null ? erroneousElement.getSimpleName().toString() : null;
}
deferredMapperElement = this.annotationProcessorContext.getElementUtils().getTypeElement(deferredMapperElement.getQualifiedName());
this.processingEnv.getMessager().printMessage(Kind.ERROR, "No implementation was created for " + deferredMapperElement.getSimpleName() + " due to having a problem in the erroneous element " + erroneousElementName + ". Hint: this often means that some other annotation processor was supposed to process the erroneous element. You can also enable MapStruct verbose mode by setting -Amapstruct.verbose=true as a compilation argument.", deferredMapperElement);
}
}
return false;
}
private Set<TypeElement> getAndResetDeferredMappers() {
Set<TypeElement> deferred = new HashSet(this.deferredMappers.size());
Iterator var2 = this.deferredMappers.iterator();
while(var2.hasNext()) {
DeferredMapper deferredMapper = (DeferredMapper)var2.next();
TypeElement element = deferredMapper.deferredMapperElement;
deferred.add(this.processingEnv.getElementUtils().getTypeElement(element.getQualifiedName()));
}
this.deferredMappers.clear();
return deferred;
}
private Set<TypeElement> getMappers(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
Set<TypeElement> mapperTypes = new HashSet();
Iterator var4 = annotations.iterator();
while(true) {
TypeElement annotation;
do {
if (!var4.hasNext()) {
return mapperTypes;
}
annotation = (TypeElement)var4.next();
} while(annotation.getKind() != ElementKind.ANNOTATION_TYPE);
try {
Set<? extends Element> annotatedMappers = roundEnvironment.getElementsAnnotatedWith(annotation);
Iterator var7 = annotatedMappers.iterator();
while(var7.hasNext()) {
Element mapperElement = (Element)var7.next();
TypeElement mapperTypeElement = this.asTypeElement(mapperElement);
if (mapperTypeElement != null && MapperGem.instanceOn(mapperTypeElement) != null) {
mapperTypes.add(mapperTypeElement);
}
}
} catch (Throwable var10) {
this.handleUncaughtError(annotation, var10);
}
}
}
private void processMapperElements(Set<TypeElement> mapperElements, RoundContext roundContext) {
Iterator var3 = mapperElements.iterator();
while(var3.hasNext()) {
TypeElement mapperElement = (TypeElement)var3.next();
try {
List<? extends Element> tst = mapperElement.getEnclosedElements();
ModelElementProcessor.ProcessorContext context = new DefaultModelElementProcessorContext(this.processingEnv, this.options, roundContext, this.getDeclaredTypesNotToBeImported(mapperElement), mapperElement);
this.processMapperTypeElement(context, mapperElement);
} catch (TypeHierarchyErroneousException var8) {
TypeMirror erroneousType = var8.getType();
Element erroneousElement = erroneousType != null ? roundContext.getAnnotationProcessorContext().getTypeUtils().asElement(erroneousType) : null;
if (this.options.isVerbose()) {
this.processingEnv.getMessager().printMessage(Kind.NOTE, "MapStruct: referred types not available (yet), deferring mapper: " + mapperElement);
}
this.deferredMappers.add(new DeferredMapper(mapperElement, erroneousElement));
} catch (Throwable var9) {
this.handleUncaughtError(mapperElement, var9);
break;
}
}
}
private Map<String, String> getDeclaredTypesNotToBeImported(TypeElement element) {
return (Map)element.getEnclosedElements().stream().filter((e) -> {
return ElementKind.CLASS.equals(e.getKind());
}).map(Element::getSimpleName).map(Object::toString).collect(Collectors.toMap((k) -> {
return k;
}, (v) -> {
return element.getQualifiedName().toString() + "." + v;
}));
}
private void handleUncaughtError(Element element, Throwable thrown) {
StringWriter sw = new StringWriter();
thrown.printStackTrace(new PrintWriter(sw));
String reportableStacktrace = sw.toString().replace(System.lineSeparator(), " ");
this.processingEnv.getMessager().printMessage(Kind.ERROR, "Internal error in the mapping processor: " + reportableStacktrace, element);
}
private void processMapperTypeElement(ModelElementProcessor.ProcessorContext context, TypeElement mapperTypeElement) {
Object model = null;
Iterator var4 = this.getProcessors().iterator();
while(var4.hasNext()) {
ModelElementProcessor<?, ?> processor = (ModelElementProcessor)var4.next();
try {
model = this.process(context, processor, mapperTypeElement, model);
} catch (AnnotationProcessingException var7) {
this.processingEnv.getMessager().printMessage(Kind.ERROR, var7.getMessage(), var7.getElement(), var7.getAnnotationMirror(), var7.getAnnotationValue());
break;
}
}
}
private <P, R> R process(ModelElementProcessor.ProcessorContext context, ModelElementProcessor<P, R> processor, TypeElement mapperTypeElement, Object modelElement) {
return processor.process(context, mapperTypeElement, modelElement);
}
private Iterable<ModelElementProcessor<?, ?>> getProcessors() {
Iterator<ModelElementProcessor> processorIterator = ServiceLoader.load(ModelElementProcessor.class, MappingProcessor.class.getClassLoader()).iterator();
List<ModelElementProcessor<?, ?>> processors = new ArrayList();
while(processorIterator.hasNext()) {
processors.add((ModelElementProcessor)processorIterator.next());
}
Collections.sort(processors, new ProcessorComparator());
return processors;
}
private TypeElement asTypeElement(Element element) {
return (TypeElement)element.accept(new ElementKindVisitor6<TypeElement, Void>() {
public TypeElement visitTypeAsInterface(TypeElement e, Void p) {
return e;
}
public TypeElement visitTypeAsClass(TypeElement e, Void p) {
return e;
}
}, (Object)null);
}
private static class DeferredMapper {
private final TypeElement deferredMapperElement;
private final Element erroneousElement;
private DeferredMapper(TypeElement deferredMapperElement, Element erroneousElement) {
this.deferredMapperElement = deferredMapperElement;
this.erroneousElement = erroneousElement;
}
}
private static class ProcessorComparator implements Comparator<ModelElementProcessor<?, ?>> {
private ProcessorComparator() {
}
public int compare(ModelElementProcessor<?, ?> o1, ModelElementProcessor<?, ?> o2) {
return Integer.compare(o1.getPriority(), o2.getPriority());
}
}
}
手写注解处理器
- 自定义一个Annotation Processor,需要继承javax.annotation.processing.AbstractProcessor,并覆写process方法。
- 自定义一个注解,注解的元注解需要指定@Retention(RetentionPolicy.SOURCE)。
- 需要在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedAnnotationTypes指定在第2步创建的注解类型的名称(注意需要全类名,"包名.注解类型名称",否则会不生效)。
- 需要在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedSourceVersion指定编译版本。
- 可选操作,可以通在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedOptions指定编译参数。
- 通过服务注册指定,META-INF/services/javax.annotation.processing.Processor文件中添加 前面定义AnnotationProcessor
定义注解Demo
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Demo {
}
POM文件
<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc7</version>
</dependency>
<!-- 提供JCTree等一些功能api -->
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
定义处理器AnnotationProcessor
@AutoService(Processor.class)
@SupportedAnnotationTypes(value = {"com.test.lombok.Demo"})
@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
public class AnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Context context = ((JavacProcessingEnvironment) this.processingEnv).getContext();
JavacElements elementUtils = (JavacElements) this.processingEnv.getElementUtils();
TreeMaker treeMaker = TreeMaker.instance(context);
JCTree.JCMethodDecl jcMethodDecl;
Iterator iterator = roundEnv.getElementsAnnotatedWith(Demo.class).iterator();
while (iterator.hasNext()) {
Element element = (Element) iterator.next();
jcMethodDecl = (JCTree.JCMethodDecl) elementUtils.getTree(element);
treeMaker.pos = jcMethodDecl.pos;
jcMethodDecl.body = treeMaker.Block(0L, List.of(treeMaker.Exec(treeMaker.Apply(List.nil(), treeMaker.Select(treeMaker.Select(treeMaker.Ident(elementUtils.getName("System")), elementUtils.getName("out")), elementUtils.getName("println")), List.of(treeMaker.Literal("这是HelloWorld打印的")))), jcMethodDecl.body));
}
return false;
}
}
测试类定义
public class Student {
private Integer id;
private String name;
@Demo
public static void main(String[] args) {
System.out.println("这是Test自己打印的");
}
}
参考文档
AbstractProcessor (Java Platform SE 8 )