java基础-注解
  TEZNKK3IfmPf 2023年11月14日 23 0

Annotation(注解)是 Java 提供的一种对元程序中元素关联信息和元数据(metadata)的途径和方法。Annatation(注解)是一个接口,程序可以通过反射来获取指定程序中元素的 Annotation对象,然后通过该 Annotation 对象来获取注解中的元数据信息。注解发生在编译阶段,它是把parse和enter阶段生成的AST语法树,经过AbstractProcessor类处理生成修改过的语法树,再交给下游进行处理。Lombook就是用这种方式实现的,注解暂时不支持继承

/***定义注解*/

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供应商编号
*/
public int id() default -1;

/*** 供应商名称*/
public String name() default "";

/**
* 供应商地址
*/
public String address() default "";
}

/*注解使用*/
public class Apple {
@FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路")
private String appleProvider;

public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}

public String getAppleProvider() {
return appleProvider;
}
}
/*注解处理器*/
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz) {
String strFruitProvicer = "供应商信息:";
Field[] fields = clazz.getDeclaredFields();//通过反射获取处理注解
for (Field field : fields) {
if (field.isAnnotationPresent(FruitProvider.class)) {
FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class); //注解信息的处理地方
strFruitProvicer = " 供应商编号:" + fruitProvider.id() + " 供应商名称:"
+ fruitProvider.name() + " 供应商地址:" + fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
public static void main(String []args){
FruitInfoUtil.getFruitInfo(Apple.class);
}
}

二、JDK内置的支持

2.1、内置的三种标准注解

  • @SuppressWarnings("unchecked"):unchecked执行了未检查的转换时的警告,例如当使用集合时没有用泛型来指定集合保存的类型;Unused没有被访问过的元素,去除警告;Fallthrough当Switch程序块直接通往下一种情况二没有Break时的警告;Path在类路径,源文件路径等中有不存在的路径时的警告;Serial当在可序列化的类上缺少serialVersionUID定义时的警告;Finally任何finally子句不能正常完成时的警告;All关于上边所有情况的警告:
  • @Override:当前的方法覆盖超类中的方法;
  • @Deprecated:当前的方法已过时,编译器会发出警告;
  • @SafeVarargs:参数安全,阻止编译过程中发现警告

2.2、四种元数据

  • @Targer:表示此注解可以用在什么地方,用ElementType.做为前缀
  • CONSTRUCTOR构造器声明
  • FIELD域声明,
  • LOCAL_VARIABLE局部变量声明
  • METHOD方法声明
  • PACKAGE包声明
  • PARAMETER参数声明
  • TYPE类、接口或enum声明
  • ANNOTATION注解
  • @Retention:表示需要在什么级别保存该注解信息,用RetentionPolicy.做为前缀
  • SOURCE:注解只在源码阶段生效,会在编译成class中不显示
  • CLASS:注解在class文件中可用,但不会加载到VM中
  • RUNTIME:VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息
  • @Documented:将此注解包含在javaDoc中
  • @Inherited:允许子类继承父类中的注解如果一个使用了@Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该class 的子类。
  • @Repeatable:1.8的新特性,同一注意会以数组形式运行,类似过滤器链

2.3、属性

在注解中只可定义属性不可定义方法,且属性为基础值,比如public @interface Trace:

  • int id() default 1:在类上可以用@Trace(1)或@Trace(id=1)
  • 如果无参数:在类上可以用@Trace

三、高级使用

3.1、Spring扫描实现

注解一般都需要一个解析器,上面是一种实现,这也是一种实现,一般需要借助spring等启动类来达到运行的目的;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id() default -1;//由于default不能为null,所以一般用””或负数来表示特殊值
public String description() default "no description";
} ///:~

public class UseCaseTracker {
public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
for(Method m : cl.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);//返回指定类型的注解对象
if(uc != null) {
System.out.println("FoundCase:" + uc.id() + " " + uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for(int i : useCases) {
System.out.println("Warning: Missing use case-" + i);
}
}
public static void main(String[] args) {
List<Integer> useCases = new ArrayList<Integer>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
}

3.2、编译时自动启动

其原理就是JVM在编译过程中会扫描所有.jar包META-INF/services/javax.annotation.processiong.Processor文件,在这个文件中指定注解类就可以了。以lombook为例。其文件结构如下:

gradle:
incremental.annotation.processors
MANIFEST.MF
services:
javax.annotation.processing.Processor
lombok.core.LombokApp
lombok.core.PostCompilerTransformation
lombok.core.runtimeDependencies.RuntimeDependencyInfo
lombok.eclipse.EclipseAnnotationHandler
lombok.eclipse.EclipseASTVisitor
lombok.eclipse.handlers.EclipseSingularsRecipes$EclipseSingularizer
lombok.installer.IdeLocationProvider
lombok.javac.handlers.JavacSingularsRecipes$JavacSingularizer
lombok.javac.JavacAnnotationHandler
lombok.javac.JavacASTVisitor
org.mapstruct.ap.spi.AstModifyingAnnotationProcessor

javax.annotation.processing.Processor 内容如下,指定了注解类,其核心源码如下:lombok.launch.AnnotationProcessorHider$AnnotationProcessor。

//创建一个类加载器,它会加载.SCL.lombok文件(其实就是.class文件),存放在lombok.javac路径下。这个类加载器的作用是为了避免污染项目的命名空间
private static AbstractProcessor createWrappedInstance() {
ClassLoader cl = Main.getShadowClassLoader();

try {
Class<?> mc = cl.loadClass("lombok.core.AnnotationProcessor");
return (AbstractProcessor)mc.getDeclaredConstructor().newInstance();
} catch (Throwable var2) {
if (var2 instanceof Error) {
throw (Error)var2;
} else if (var2 instanceof RuntimeException) {
throw (RuntimeException)var2;
} else {
throw new RuntimeException(var2);
}
}
}

3.3、改写AST语法树

执行avac -processor com.jsr269.DataAnnoProcessor Test.java,注意这需要用-processor指定要使用的的注解,或在maven-compiler-plugin中配置compilerArgument参数指定值为-proc:none。

<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
@Target(ElementType.TYPE)
@Retentioin(RetentionPolicy.SOURCE)
public @interface Data {
}
@SupportedAnnotationTypes("com.Data") //注解类的全限定名
@SupportedSourceVersion(SourceVersion.RELEASE_8)//最高支持1.8的JDK编译出来的类
public class DataAnnoProcessor extends AbstractProcessor {

/*语法树元素的基类*/
private JavacTrees javacTrees;

/*创建方法树的方法*/
private TreeMaker treeMaker;

/*提供了访问标识符的方法,比如this或super这样的引用*/
private Names names;

/*完成初始化工作*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
Context context = (Context) ((JavacProcessingEnvironment)processingEnv).getContext();
javacTrees = JavacTrees.instance(processingEnv);
treeMaker = TreeMaker.instance(context);
names = Names.instance(context);
}

/*做AST语法树的修改*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//取得所有被Data注释的类的集合
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Data.class);

for(Element element : set){
//得到当前类的AST树
JCTree tree = javacTrees.getTree(element);
//这个相当于事件,当遍历AST过程会触发相应的方法
tree.accept(new TreeTranslator(){
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
//
jcClassDecl.defs = jcClassDecl.defs.prepend(genMethod());
super.visitClassDef(jcClassDecl);
}
});
}

return false;
}

//生成新方法
private JCTree.JCMethodDecl genMethod(){
return null;
}
}
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

上一篇: java基础-泛型 下一篇: java基础 - 数据结构
  1. 分享:
最后一次编辑于 2023年11月14日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   19天前   43   0   0 java
  TEZNKK3IfmPf   2024年05月31日   54   0   0 java
TEZNKK3IfmPf