java的maven框架编译_从maven的debug compile到java的编译时注解(与springboot项目整合)...

本文介绍了如何利用Java的maven框架和编译时注解(@ApiFunction)来自动化生成API配置文件。通过继承AbstractProcessor并结合SPI(Service Provider Interface)实现编译时逻辑。在项目整合过程中,需要配置maven的打包插件以执行注解处理器,并处理与Lombok的兼容问题。最终,通过注解处理器实现了在编译阶段自动生成api-define.json文件,简化了手动配置的繁琐工作。
摘要由CSDN通过智能技术生成

事情的开始要从周一说起,那天晚上我正常编译打包准备更换部件,这时突然发现maven有个选项是debug maven compile,遂感到奇怪,这玩意有啥用??,唯一能想到的是编译时进行debug,但具体的应用场景不清楚,自从架构升级到中台之后,我们负责的模块再也没有控制器了,统一放到了网关部件,网关只依赖各个部件的"能力层",即只依赖接口,需要执行业务逻辑时需要解析各个部件的实现层提供的api文件,这个文件就是一个json数组,但是这玩意很烦,需要手动去配置,我通常是拷贝一个再去修改,但经常翻车,忘记把service或者method改掉,这时我突然想到编译时生成配置文件然后打包不就完事了吗?

1 {2 "uri": "/api/test/ok",3 "operation": "hello",4 "description": "hello",5 "service": "xxxx.service",6 "method": "sayHello"

7 }

第一个想到的实现方式就是注解,但之前用的最多的注解类型是RUNTIME类型的,可以结合spring,反射+point实现注解逻辑,但编译时注解怎么操作?原理是javac编译的过程是一个独立的虚拟机进程,jdk提供了一个AbstractProcessor留给我们去继承重写相关方法(熟悉设计模式的估计已经想到模板模式了),达到编译时执行我们的注解处理器逻辑的目的.怎么做的问题解决了,可如何让虚拟机去加载我们的注解处理器呢?答案是SPI(service provider interfacse),如果你熟悉dubbo,或者spring,你会经常发现他们的jar包中的META-INF下面总是别有洞天,来两张图感受下

9f9bfa5c621f7cb8f5877070763dd7f8.png

2b3ddcce91f00da990c27ed030ea43b8.png

那到底啥是SPI,大白话就是第三方框架或者jsr相关规范定义好了接口,你直接来实现,并且告诉别人这个接口的实现类必须是我写的实现类,怎么操作呢?原始的java SPI网上一搜一堆,META-INF下新建services文件夹,新建文件名称为接口的全限定名,内容为实现类的全限定名,之后使用ServiceLoader进行加载,不过这玩意很烦,只要你写到这个文件类的实现都会被加载,所以dubbo做了优化,改成了键值对的方式,具体的可以看dubbo.internal下的实现

cd3235ab5837bacc7d39f6c075ac46f6.png

ok,把话说回来,SPI的作用就是XXInterface service = your impl,当你作为一个第三方框架的服务提供者,你就感受到这样做的好处了,后面会再写一篇文章详细介绍,等不及的可以先去看effective java,静态工厂方法那一块也有相关SPI讲解

照猫画虎,只要我们的注解处理器也这样操作就可以了,刚刚说到我们继承了AbstractProcessor, 那么我们的spi文件名就是他实现的接口全限定名了,内容是我们的注解处理器的全限定名了,so

34827acfd54eec3f32ad91e004067a5e.png

8a8fad9d6b82a624b680c4bfd474e7ad.png

如果你感觉这样麻烦采用guava提供的注解@AutoService即可,这个注解帮我们生成services下的文件,编码会贴在下面,不过在此之前要先说说怎么整合到我们的项目中?

首先给待整合的项目加入我们注解处理器项目的依赖,然后我们需要配置下maven的打包插件在编译时执行我们的注解处理器,在整合的过程中发现,由于使用了lombok,其自身也是借助编译处理器生成getter,setter代码所以我们也要让maven执行lombok的注解处理器,否则编译时各种找不到符号,可是lombok的注解处理器叫啥名?别着急,找到lombok的jar包然后去找META-INF下的services

e8083c5b9561d203794a684df6c344d2.png

看到了熟悉的文件名,查看内容就有了下面的注解处理器配置,问题解决,最后贴下配置和代码

1

2 xxx

3 api-build

4 1.0-SNAPSHOT

5 provided

6

7

8

9

10 maven-compiler-plugin

11 3.3

12

13 1.8

14 1.8

15 UTF-8

16

17 lombok.launch.AnnotationProcessorHider$AnnotationProcessor

18 lombok.launch.AnnotationProcessorHider$ClaimingProcessor

19 你的注解处理器限定名称

20

21

22

注解定义,注意是编译时注解RetenionPolicy.CLASS

1 @Documented2 @Target(ElementType.METHOD)3 @Retention(RetentionPolicy.CLASS)4 public @interfaceApiFunction5 {6

7 String uri() default "";8

9 String operation() default "";10

11 String description() default "";12

13 /*String service() default "";14

15 String method() default "";*/

16

17 String group() default "";18

19 String version() default "";20

21 String[] authorities() default{};22

23 boolean needAuth() default true;24

25 int priority() default 0;26

27 }

注解处理器相关代码与依赖

1

2

3 com.google.auto.service

4 auto-service

5 1.0-rc6

6

7

8

9

10 maven-compiler-plugin

11 3.8.0

12

13 UTF-8

14 1.8

15 1.8

16 none

17

18

1 /**

2 *@authortele3 * @Description4 * @create 2020-09-075 */

6 @AutoService(Processor.class)7 public class ApiInfoGenerateProcessor extendsAbstractProcessor8 {9 /**

10 * application.properties中的key,是否允许使用注解生成api-define.json11 */

12 private static final String SWITCH = "api.auto-generate.enable";13

14 /**

15 * 文件路径16 */

17 private static final String API_PATH = "config/api-define.json";18

19 /**

20 * 配置项路径21 */

22 private static final String APPLICATION_PATH = "application.properties";23

24 private static booleanenable;25

26 privateFileObject apiInfoFile;27

28 @Override29 public synchronized voidinit(ProcessingEnvironment processingEnv)30 {31 try

32 {33 //文件写入到编译后路径

34 apiInfoFile = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT,"",API_PATH);35 //从源码位置读取配置开关

36 InputStream inputStream = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH, "", APPLICATION_PATH).openInputStream();37 Properties properties = newProperties();38 properties.load(inputStream);39 enable =Boolean.valueOf(properties.getProperty(SWITCH));

// TODO close and check40 }41 catch(IOException e)42 {43 e.printStackTrace();44 }45 }46

47 /**

48 * 指定支持的jdk版本49 *@return

50 */

51 @Override52 publicSourceVersion getSupportedSourceVersion()53 {54 returnSourceVersion.latestSupported();55 }56

57 /**

58 * 处理哪种类型的注解, * 表示处理所有类型的注解59 *@return

60 */

61 @Override62 public SetgetSupportedAnnotationTypes()63 {64 return Sets.newHashSet(ApiFunction.class.getCanonicalName());65 }66

67 /**

68 *69 *@paramannotations getSupportedAnnotationTypes返回的注解处理器集合70 *@paramroundEnv 获取扫描到的注解节点71 *@return当你有多个注解处理器时,返回true表示其他注解处理器不再对该类型的注解进行处理72 */

73 @Override74 public boolean process(Set extends TypeElement>annotations, RoundEnvironment roundEnv)75 {76 if(!annotations.isEmpty() &&enable) {77 Set extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(ApiFunction.class);78 List apiInfoList = elementsAnnotatedWith.stream().filter(e -> e.getEnclosingElement().getKind().equals(ElementKind.CLASS)).map(e ->{79 //service 获得父元素,注解加在方法上,父元素就是类或者接口了

80 TypeElement element =(TypeElement)e.getEnclosingElement();81 //method

82 String methodName =String.valueOf(e.getSimpleName());83 ApiFunction annotation = e.getAnnotation(ApiFunction.class);84 Map map = new LinkedHashMap<>(8);85 map.put("uri", annotation.uri());86 map.put("operation", annotation.operation());87 map.put("description", annotation.description());88 map.put("service", String.valueOf(element.getQualifiedName()));89 map.put("method", methodName);90 if(annotation.group() != null && !"".equals(annotation.group())) {91 map.put("group", annotation.group());92 }93 if(annotation.version() != null && !"".equals(annotation.version())) {94 map.put("version", annotation.version());95 }96 if(annotation.authorities() != null && annotation.authorities().length != 0) {97 map.put("authorities",Arrays.asList(annotation.authorities()));98 }99 //access 默认解析为true

100 if(!annotation.needAuth()) {101 map.put("needAuth", annotation.needAuth());102 }103 if(annotation.priority() != 0) {104 map.put("priority", annotation.priority());105 }106 return JSONUtil.toJson(map,true);107 }).collect(Collectors.toList());108 OutputStream outputStream = null;109 try

110 {111 File file = newFile(apiInfoFile.toUri());112 if(!file.exists()) {113 file.getParentFile().mkdirs();114 file.createNewFile();115 }116 System.out.println(String.format("find api:%d", apiInfoList.size()));117 outputStream = newFileOutputStream(file);118 IOUtils.write(String.valueOf(apiInfoList), outputStream, StandardCharsets.UTF_8);119 outputStream.flush();120 }121 catch(IOException e)122 {123 e.printStackTrace();124 }finally

125 {126 try

127 {128 IOUtils.close(outputStream);129 }130 catch(IOException e)131 {132 e.printStackTrace();133 }134 }135 }136 return true;137 }138 }

说回开头,maven的debug问题,当你在依赖注解处理器的项目上执行debug maven compile时,只要给注解处理器代码打断点就ok了更复杂的注解处理器应用可以参考https://juejin.im/post/6844903879524483086,也可以参考<>第十章关的插入式注解处理器相关内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值