java retentionpolicy_Java注解之如何利用RetentionPolicy.SOURCE生存周期

上一篇文章简单讲了下Java注解的学习之元注解说明,学习了Java注解是如何定义的,怎么使用的,但是并没有介绍Java的注解是怎么起作用的,像Spring Boot里面的那些注解,到底是怎么让程序这样子运行起来的?特别是讲到RetentionPolicy这一块,到底在SOURCE阶段,在CLASS阶段,在RUNTIME阶段有什么差别,注解是如何在这三个阶段起作用的呢?而且,在SOURCE阶段,在CLASS阶段,程序又不运行,那注解到底会用来做些什么呢?

带着这些疑问,我就去了解下了如何让注解起作用,发现RUNTIME阶段的介绍到处都是,但是SOURCE和CLASS阶段就很少文章讲解到了,真的得搜刮好几十篇文章才能成功的把程序跑起来,几乎每一篇文章都少讲了些东西。

本文优先讲的是SOURCE阶段注解如何发挥作用,发现这一块涉及的知识非常多且难,资料还少,另外还发现,Java服务器端编程的人用这个反而不如Android开发的人用得多。对我学习SOURCE阶段的注解帮助最大的是Pluggable Annotation Processing API,JSR269插件化注解API以及JVM进阶 -- 浅谈注解处理器。搜索这方面的资料用“插件化注解处理API”这个关键词能搜得更全。

这几篇文章基本上把SOURCE阶段的注解实现和使用讲了,但是具体细节,比如用javac直接编译运行代码,javac使用jar包,使用maven等三个方式如何运行注解处理器,倒是基本上蜻蜓点水,摸索了我好久才搞定了。关于注解处理器的知识,可以从如上三篇文章了解,本文主要讲注解的定义和运行。

我用的代码是摘抄至 JVM进阶 -- 浅谈注解处理器 ,它定义了一个CheckGetter的注解,用来检查一个类里面的字段,哪个没有Getter方法,没有的话编译就报错。不过我的和他的稍稍不同,他的代码定义没有放到package里面,我的放到package里面了,这样子的使用和执行又有了点不同。

首先,定义CheckGetter注解:

package com.shahuwang.processor;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.FIELD})

@Retention(RetentionPolicy.SOURCE)

public @interface CheckGetter {

}

注意上面的代码,是放到 package com.shahuwang.processor 里面的,因此,先创建如下结构的文件夹

Processor

—— com

—— shuhuwang

—— processor

—— CheckGetter.java

注解已经定义好了,现在先来用一下这个注解:

package com.shahuwang.processor;

@CheckGetter

public class TestGetter {

String name;

String first;

public String getName(){

return this.name;

}

}

这个类有两个字段,但是有一个字段没有设置getter。

下面才是真正重要的代码,就是让CheckGetter这个注解真正能运行起来,发挥作用:

package com.shahuwang.processor;

import javax.annotation.processing.AbstractProcessor;

import javax.annotation.processing.RoundEnvironment;

import javax.annotation.processing.SupportedAnnotationTypes;

import javax.annotation.processing.SupportedSourceVersion;

import javax.lang.model.SourceVersion;

import javax.lang.model.element.ExecutableElement;

import javax.lang.model.element.Modifier;

import javax.lang.model.element.TypeElement;

import javax.lang.model.element.VariableElement;

import javax.lang.model.util.ElementFilter;

import javax.tools.Diagnostic;

import javax.lang.model.element.Element;

import java.util.Set;

// 这个地方换了包名就需要改过来,否则processor就不会执行了, 这里是最需要注意的地方,千万注意!!!!!

@SupportedAnnotationTypes("com.shahuwang.processor.CheckGetter")

@SupportedSourceVersion(SourceVersion.RELEASE_11)

public class CheckGetterProcessor extends AbstractProcessor {

@Override

public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {

for (TypeElement annotatedClass : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(CheckGetter.class))) {

for (VariableElement field : ElementFilter.fieldsIn(annotatedClass.getEnclosedElements())) {

if(!containsGetter(annotatedClass, field.getSimpleName().toString())){

processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,

String.format("getter not found for '%s.%s'.", annotatedClass.getSimpleName(), field.getSimpleName()));

}

}

}

return false;

}

private static boolean containsGetter(TypeElement typeElement, String name){

String getter = "get" + name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();

for(ExecutableElement executableElement: ElementFilter.methodsIn(typeElement.getEnclosedElements())){

if(!executableElement.getModifiers().contains(Modifier.STATIC) &&

executableElement.getSimpleName().toString().equals(getter) &&

executableElement.getParameters().isEmpty()){

return true;

}

}

return false;

}

}

上面这段代码要理解的概念有点儿多,实际上我现在也不是很懂,但是本文的目标是先让注解处理器跑起来,所以先不管。这里最重要也是折磨我最惨的地方,就是这一句@SupportedAnnotationTypes("com.shahuwang.processor.CheckGetter"),你定义的注解在哪个package下,这里就要写完整了,如果写错了,注解处理器就不起作用了。

现在在Processor目录下,打开命令行,先编译CheckGetterProcessor.java: javac com/shahuwang/processor/CheckGetterProcessor.java,编译好之后就可以使用了,再执行命令:javac -processor com.shahuwang.processor.CheckGetterProcessor com/shahuwang/processor/TestGetter.java,便可以看到这样的提示:

错误: getter not found for 'TestGetter.first'.

上面这种方式,需要每次执行编译的时候,指定一下processor才能做到检查作用,那如果在团队开发中,希望自动执行一些检查,那可以用SPI 服务提供者发现机制或者maven来实现。

SPI 服务提供者发现机制就是配置META-INF文件夹和里面的文件,告诉Java你要执行某个东西,这个东西的路径在哪里。再把编译好的processor和META-INF打包成jar包,就可以很方便使用了。文件夹和文件结构如下:

processor

-com

-shahuwang

- processor

- CheckGetterProcessor.class

-META-INF

-services

- javax.annotation.processing.Processor

编译方式和上述一样。

javax.annotation.processing.Processor是一个普通的文本,就是告知java一些关于注解处理器的配置,里面的内容如下:

com.shahuwang.processor.CheckGetterProcessor

就是告知这个注解处理器要用哪些个,如果有多个的话,可以一行一个。

然后在processor目录下,执行指令:jar -cvf processor.jar com META-INF, 形成了一个 processor.jar 的包,此时可以执行指令:

javac -cp processor.jar com/shahuwang/processor/TestGetter.java

就会自动执行注解处理器检查字段有没有getter方法了。

jar包的这个方法,其实是把注解和注解的实现单独放一块作为一个插件来使用了,maven也是如此的。

现实的开发中,还是用maven最多,先用指令创建一个maven项目:mvn archetype:generate -DgroupId=com.shahuwang -DartifactId=Processor -Dpackage=com.shahuwang.processor

然后在pom.xml的层级下添加如下配置:

UTF-8

UTF-8

1.8

1.8

1.8

org.apache.maven.plugins

maven-compiler-plugin

3.5.1

1.8

1.8

UTF-8

com.shahuwang.processor.CheckGetterProcessor

代码目录结构如下:

Processor

- src

- main

- java

-com

-shahuwang

-processor

-CheckGetter.java

-CheckGetterProcessor.java

- pom.xml

然后Processor目录下执行: mvn clean install, 这个项目就会被安装到本地的maven库里面。

再用maven新建一个TestProcessor项目,项目结构如下:

TestProcessor

- src

- main

- java

-com

-shahuwang

-TestGetter.java

- pom.xml

TestGetter.java 的代码上面有。

修改pom.xml, 先在 下添加:

UTF-8

UTF-8

1.8

1.8

1.8

org.apache.maven.plugins

maven-compiler-plugin

3.5.1

1.8

1.8

UTF-8

com.shahuwang.processor.CheckGetterProcessor

再在dependencies添加:

com.shahuwang

Processor

1.0-SNAPSHOT

也就是引入上面编译好的处理器的包。

现在,在TestProcessor目录下执行:

mvn compile

就会看到熟悉的提示:

错误: getter not found for 'TestGetter.first'.

三种主流的注解处理器使用方式现在都搞懂怎么使用了。下一篇文章将会关注注解处理器的使用方法,各个注解处理器API的使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: The scope of the `RetentionPolicy` annotations in Java determines when the annotations should be discarded. The `RetentionPolicy` enumeration has three values: `SOURCE`, `CLASS`, and `RUNTIME`. - `RetentionPolicy.SOURCE` means that the annotations are only to be retained in the source code and will be discarded after the code is compiled. This is the least restrictive retention policy. - `RetentionPolicy.CLASS` means that the annotations are to be stored in the compiled class files but will not be available during runtime. This is more restrictive than `RetentionPolicy.SOURCE`. - `RetentionPolicy.RUNTIME` means that the annotations are to be stored in the compiled class files and made available during runtime, which is the most restrictive retention policy. So, in terms of scope, `RetentionPolicy.RUNTIME` has the largest scope, followed by `RetentionPolicy.CLASS` and then `RetentionPolicy.SOURCE`. ### 回答2: 在Java中,RetentionPolicy是一个枚举类型,用于定义注解的保留策略。RetentionPolicy.SOURCE表示注解只会保留在源代码中,不会包含在编译后的类文件中。RetentionPolicy.CLASS表示注解会保留在编译后的类文件中,但在运行时不会被加载到JVM中。RetentionPolicy.RUNTIME表示注解会保留在编译后的类文件中,并且可以在运行时被加载到JVM中,可以通过反射机制来获取注解的信息。 从范围的角度来看,RetentionPolicy.SOURCE的范围最小,只在源代码中存在,不会影响编译后的结果和运行时的行为。RetentionPolicy.CLASS的范围稍大,注解会被包含在编译后的类文件中,但在运行时不会被加载到JVM中,只能通过编译时处理器来获取注解的信息。RetentionPolicy.RUNTIME的范围最大,注解会被包含在编译后的类文件中,并且可以在运行时被加载到JVM中,可以通过反射机制来获取注解的信息。 因此,RetentionPolicy.RUNTIME的范围最大,它的作用会涵盖RetentionPolicy.SOURCE和RetentionPolicy.CLASS的功能。选择使用哪个保留策略取决于注解的使用场景和需求。 ### 回答3: 在Java编程中,RetentionPolicy是一个枚举类型,用于指定注解在编译器,类加载器和运行时的保留策略。RetentionPolicy枚举类型定义了三个常量:RetentionPolicy.SOURCE,RetentionPolicy.CLASS和RetentionPolicy.RUNTIME。 RetentionPolicy.SOURCE是最低级别的保留策略。通过使用RetentionPolicy.SOURCE注解只保留在源代码中,并在编译后的类文件中将被完全丢弃。这意味着在运行时无法访问这些注解。 RetentionPolicy.CLASS是中级保留策略。通过使用RetentionPolicy.CLASS,注解在编译后的类文件中保留,并可以由类加载器加载。但是,这些注解在运行时是不可见的,因此无法通过Java反射机制访问。 RetentionPolicy.RUNTIME是最高级别的保留策略。通过使用RetentionPolicy.RUNTIME,注解在编译后的类文件中保留,并可以由类加载器加载。与RetentionPolicy.CLASS不同的是,这些注解可以在运行时通过Java反射机制进行访问,因此可以在运行时使用注解提供的信息。 综上所述,RetentionPolicy.RUNTIME的范围最大。它能够在编译后的类文件中保留注解,并且这些注解可以在运行时通过反射机制来访问。而RetentionPolicy.SOURCE和RetentionPolicy.CLASS的范围较小,无法在运行时访问注解的信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值