我们很多人都写过代码自动生成的工具,比如用python结合moko模板引擎,或者java 结合freemarker模板引擎,protoc 等,实现解析策划数据类,proto协议类,或者数据库层的实体类。大大节省了我们开发的时间,让我们可以懒得光明正大。
那么,你有没有办法当你写好几个协议后,只要一保存编译,相关的协议接收和发送类的接口就自动实现了呢?答案是有的。

组件化开发
注解处理器是(Annotation Processor)是javac的一个工具,用来在编译时扫描和编译和处理注解(Annotation)。你可以自己定义注解和注解处理器去搞一些事情。一个注解处理器它以Java代码或者(编译过的字节码)作为输入,生成文件(通常是java文件)。这些生成的java文件不能修改,并且会同其手动编写的java代码一样会被javac编译。看到这里加上之前理解,应该明白大概的过程了,就是把标记了注解的类,变量等作为输入内容,经过注解处理器处理,生成想要生成的java代码。Annotation Process的实质用处就是在编译时通过注解获取相关数据

什么是APT?
APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件,将它们一起生成class文件。

理解了以上的理论,我们来实践一下:

需求:
通过实现协议类,让客户端请求工具和服务端接收处Controller自动添加代码。
1.首先添加依赖包

    // annotationimplementation "com.google.auto.service:auto-service:1.0.1"

2.添加annotation类:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** http proto注解,会被{@link ProtoServiceProcessor}处理** @author Allen Jiang*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)public @interface HttpProto {}

添加Processor处理类:


/*** 用于自动生成admin和业务进程通讯的底层代码** @author Allen Jiang*/
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"com.gamioo.http.annotation.HttpProto"})
public class ProtoServiceProcessor extends AbstractProcessor {public static final String C_2_S_MSG = "_C2S_Msg";public static final String S_2_C_MSG = "_S2C_Msg";public static final String HTTP_PROCESSOR = "IHttpProcessor";public static final String GAME_CLIENT = "GameClient";private Messager messager;@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);messager = processingEnvironment.getMessager();}@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {if (roundEnv.processingOver()) {return false;}for (TypeElement annotation : annotations) {messager.printMessage(Diagnostic.Kind.NOTE, String.format("process annotation start: %s", annotation));try {List<HttpProtoDTO> list = this.getMethodList(annotation, roundEnv);this.buildClass(list, HTTP_PROCESSOR);this.buildClass(list, GAME_CLIENT);} catch (Exception e) {messager.printMessage(Diagnostic.Kind.ERROR, ExceptionUtils.getStackTrace(e));}messager.printMessage(Diagnostic.Kind.NOTE, String.format("process annotation end: %s", annotation));return false;}return false;}/*** 构建模板*/private void buildClass(List<HttpProtoDTO> list, String className) throws IOException, TemplateException {messager.printMessage(Diagnostic.Kind.NOTE, String.format("build class start: %s", className));StatDTO<HttpProtoDTO> dto = new StatDTO<>();dto.setArray(list);String content = ViewTemplateUtils.getContentFromJar(className + ".ftl", dto);//  messager.printMessage(Diagnostic.Kind.NOTE, String.format("content: %s", content));FileObject fileObject = processingEnv.getFiler().createSourceFile(className);try (PrintWriter writer = new PrintWriter(fileObject.openWriter())) {writer.write(content);writer.flush();messager.printMessage(Diagnostic.Kind.NOTE, String.format("build class end: %s", className));} catch (IOException e) {throw e;}}/*** 获取协议列表*/private List<HttpProtoDTO> getMethodList(TypeElement annotation, RoundEnvironment roundEnv) throws IOException {List<HttpProtoDTO> ret = new ArrayList<>();FileObject fileObject = processingEnv.getFiler().getResource(StandardLocation.SOURCE_OUTPUT, "", HTTP_PROCESSOR + ".java");Map<String, Integer> store = new HashMap<>();try {CharSequence content = fileObject.getCharContent(false);String[] list = StringUtils.substringsBetween(content.toString(), "(", C_2_S_MSG);for (String e : list) {store.put(StringUtils.uncapitalize(e), 2);}} catch (Exception e) {messager.printMessage(Diagnostic.Kind.NOTE, e.getMessage() + ",ignore it");}Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);for (Element element : elements) {//   messager.printMessage(Diagnostic.Kind.NOTE, String.format("element: %s", element.getSimpleName().toString()));String method = StringUtils.uncapitalize(StringUtils.substringBefore(element.getSimpleName().toString(), "_"));Integer num = store.get(method);if (num == null) {num = 1;} else {num += 1;}store.put(method, num);}messager.printMessage(Diagnostic.Kind.NOTE, String.format("element: %s", JSONUtils.toJSONString(store)));store.forEach((method, value) -> {String prefix = StringUtils.capitalize(method);String c2s = prefix + C_2_S_MSG;String s2c = prefix + S_2_C_MSG;HttpProtoDTO protoDTO = new HttpProtoDTO(c2s, s2c, method);ret.add(protoDTO);});messager.printMessage(Diagnostic.Kind.NOTE, String.format("message number: %s", ret.size()));// fileObject.delete();return ret;}
}

辅助类:

public class HttpProtoDTO {private String c2s;private String s2c;private String method;public HttpProtoDTO() {}public HttpProtoDTO(String c2s, String s2c, String method) {this.c2s = c2s;this.s2c = s2c;this.method = method;}public String getC2s() {return c2s;}public void setC2s(String c2s) {this.c2s = c2s;}public String getS2c() {return s2c;}public void setS2c(String s2c) {this.s2c = s2c;}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}@Overridepublic String toString() {return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);}

以上类所在的模块我们要编译出一个jar.

依赖的项目dependencies中添加

  annotationProcessor project(':gamioo-core')

接下去我们说如何使用。
首先,定义协议:

import com.gamioo.http.annotation.HttpProto;/*** 执行脚本命令.*/
@HttpProto
public class ExecScript_C2S_Msg {private String script;public String getScript() {return script;}public void setScript(String script) {this.script = script;}
}
/*** @author Allen Jiang*/
@HttpProto
public class ExecScript_S2C_Msg {private String result;private long interval;public long getInterval() {return interval;}public void setInterval(long interval) {this.interval = interval;}public String getResult() {return result;}public void setResult(String result) {this.result = result;}
}

然后在com.gamioo.common.http.proto包执行一下编译,就自动生成了com.gamioo.admin.network.GameClient和com.gamioo.common.http.IHttpProcessor的代码
client 端:
com.gamioo.admin.network.GameClient
admin:

public ExecScript_S2C_Msg execScript(ExecScript_C2S_Msg msg) {ExecScript_S2C_Msg ret = null;String content = this.sendWithReturn(msg);if (content != null) {ret = JSONUtils.parseObject(content, ExecScript_S2C_Msg.class);}return ret;
}

server端:

com.yorha.common.http.IHttpProcessor
ExecScript_S2C_Msg execScript(ExecScript_C2S_Msg msg);

以上两个文件都会生成在generated目录下,并且不支持文件修改,这样就完成的代码自动生成,后续就可以进行协议的具体逻辑实现和调用测试。

Q1: 自定义jsr-269注解处理器 Error:服务配置文件不正确,或构造处理程序对象javax.annotation.processing.Processor: Provider not found
出现的原因
自定义处理器还没有被编译就被调用,所以报 not found
在根据配置寻找自定义的注解处理器时,自定义处理器还未被编译
解决方式
maven项目可以配置编译插件,在编译项目之前先编译处理器,或者编译项目时跳过执行处理器
参考:https://stackoverflow.com/questions/38926255/maven-annotation-processing-processor-not-found

Q2: gradle项目可以将自定义处理器分离出去,单独作为一个项目,并且打包成jar包,将这个项目build后作为依赖使用
例如:

dependencies {compile project(':anno-project-core')annotationProcessor project(':anno-project-core')
}

Q3: 如何进到Processor调试?
需要用gradle的assemble功能,然后选择调试。

参考资料如下:
注解入坑笔记:关于注解使用必须了解的——Annotation、AbstraceProcessor、APT
IDEA+Gradle使用Annotation Processor
Annotation Tool(注解工具-IntelliJ IDEA 插件)

如何用 APT(Annotation Processing Tool)自动生成代码相关推荐

  1. apt编译java_自动生成代码工具--APT

    APT(Annotation Processing Tool)注解处理器,是javac的一个工具,它用来在编译时扫描和处理注解(Annotation).它可以生成Java代码,同时这些代码会跟手工白那 ...

  2. 使用Signature Tool自动生成P/Invoke调用Windows API的C#函数声明

    使用Signature Tool自动生成P/Invoke调用Windows API的C#函数声明 分类: 小技巧 Win32编程 C++ 解决难题 .NET2009-02-05 20:53 5186人 ...

  3. 简单的利用IDEA搭建SpringBoot+Maven+Mybatis+自动生成代码

    最近在系统的学习SpringBoot框架,并且要用该框架做个项目--网上也大大小小看了很多教程,感觉很多写文章的人都不太负责任,只知道搬运,大概都没有实际操作过,问题也是有很多,所以自己写一篇文章记录 ...

  4. 【MyBatis】MyBatis自动生成代码之查询爬坑记

    前言 项目使用SSM框架搭建Web后台服务,前台后使用restful api,后台使用MyBatisGenerator自动生成代码,在前台使用关键字进行查询时,遇到了一些很宝贵的坑,现记录如下.为展示 ...

  5. SpringBoot入门篇--整合mybatis+generator自动生成代码+druid连接池+PageHelper分页插件

    我们这一一篇博客讲的是如何整合Springboot和Mybatis框架,然后使用generator自动生成mapper,pojo等文件.然后再使用阿里巴巴提供的开源连接池druid,这个连接池的好处我 ...

  6. mybatis-Plus自动生成代码

    1.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...

  7. Mybatis Plus自动生成代码

    mybatis-plus自动生成代码 一.简易生成代码 二.指定生成的样式,并且不在一个模块 1.父pom文件配置 2.子模块pom文件配置 3.准备vm文件 4.设置MyBatisPlusGener ...

  8. C#分析数据库结构,使用XSL模板自动生成代码

    <html> <head> <TITLE>分析数据库结构,自动生成代码</TITLE> <meta http-equiv="Conten ...

  9. SpringBoot如何自动生成实体类和Dao层以及映射文件(mybatis generator 自动生成代码)

    一.首先添加自动生成代码插件 <!-- mybatis generator 自动生成代码插件 生成时解除注释 --><plugin><groupId>org.myb ...

最新文章

  1. php v9 ajax 翻页,php ajax 无刷新翻页实现代码
  2. 利用T-SQL语句快速清理ZBLOG程序的SQL SERVER2012数据库内容
  3. android 圆环温度控件,android 圆环倒计时控件
  4. php 启动手机浏览器,php实现读取手机客户端浏览器的类
  5. cad中线段求和lisp_cad中连续线段变更圆滑弧形
  6. 如何将数字转换成口语中的文本串
  7. java定义数组_java中数组的三种定义方式_java中数组的定义及使用方法(推荐)...
  8. 有关JAVA中不同方法对String连接性能的总结
  9. mysql--实战1--查询数据1--scan方法
  10. json-lib解析json之二维JSONArray
  11. 直播视频网站源码,列表的展开更多和点击收起功能
  12. 免费直播编码软件应用技巧
  13. 主编编辑器怎么把文章转成长图?
  14. Hibernate入门第一讲——Hibernate框架的快速入门
  15. 计算机PS怎么做地图怎么改色,ps颜色快捷键,ps调整颜色模式和替换改色
  16. php报错_STORAGE_WRITE_ERROR_:./Application/Runtime/Cache/Admin/df12aa1edf6tt330187a6514aae4fda4.php
  17. Go sync.Pool 浅析
  18. 软件测试显卡最高清晰度,硬件碾压机再临? GTA5显卡性能全测试
  19. Yuga Labs收购之后,CryptoPunks拥有了「商业许可」
  20. 【Python爬虫】| XPANX获取抖音APP小视屏

热门文章

  1. mpeg2是信源还是信道编码_解析信源编码与信道编码之间的区别
  2. html——网页上添加表格
  3. 地铁供电系统原理图_地铁供电系统智能化发展原稿(图文高清版)
  4. GD32F103配置SPI+DMA收发数据
  5. Windows API在线参考手册
  6. MT7601WIFI-Linux驱动总结
  7. 大连四六级百家外语考试中心大学英语四级成绩的作用有哪些?
  8. C语言之循环while,do-while,for的简单使用方法及区别
  9. 【线上峰会】互联网应用架构解析与实战峰会嘉宾、议题和日程大公开
  10. 电商客服议价技巧分享