广告
返回顶部
首页 > 资讯 > 后端开发 > Python >浅析Java自定义注解的用法
  • 572
分享到

浅析Java自定义注解的用法

Java自定义注解使用Java自定义注解Java 注解 2023-03-21 11:03:34 572人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录注解定义注解注解处理器运行时解析注解编译时解析注解总结注解 注解为我们在代码中添加信息提供一种形式化的方法,使我们可以在源码、编译时、运行时非常方便的使用这些数据。 注解是在JA

注解

注解为我们在代码中添加信息提供一种形式化的方法,使我们可以在源码、编译时、运行时非常方便的使用这些数据。

注解是在JAVA SE5中引入的,注解让代码更干净易读并且可以实现编译期类型检查等。当创建描述性质的类或接口时,如果有重复性的工作,就可以考虑使用注解来简化或自动化该过程。我们可以让注解保存在源代码中,并且利用Annotation api处理注解,得到我们想要的数据并加以处理,注解的使用比较简单,JAVA SE5内置了3种:

  • @Override 表示当前类中的方法将覆盖父类中的方法,如果不写也不会有错,但是@Override可以起到检查作用,如方法名拼写错误,编译器就会报警告信息。
  • @Deprecated 表示被标注的方法已经被废弃了,如果使用编译器会发出警告信息。
  • @SuppressWarnings 关闭不当的编译器警告信息。除非你确定编译器的警告信息是错误的,否则最好不要使用这个注解。

定义注解

先来看内置注解@Override是怎么被定义的,它位于package java.lang之下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Target、@Retention称为元注解,元注解负责注解其他的注释,如:@Target定义声明的注解的作用域(作用在类上还是方法上),@Retention定义注解在哪个级别可用,在源代码中(SOURCE)、类文件中(CLASS)、还是运行时(RUNTIME)。除了@Target、@Retention还有@Documented及@Inherited,下面用一个表格来分别列出他们各自的作用:

元注解作用
@Target表示注解作用在什么地方,CONSTRUCTOR 声明在构造器、FIELD 域声明、METHOD 方法声明、PACKAGE 包声明、TYPE 类、接口或者enum声明、PARAMETER参数声明、LOCAL_VALABLE局部变量声明
@Retention表示在什么级别保存注解信息,SOURCE注解在编译器编译时丢弃、CLASS注解在编译之后的class文件中存在,但会被VM丢弃、RUNTIME VM将在运行期也保留注解,因此可以用反射读取注解的信息
@Documented将此注解包含在JavaDoc中
@Inherited允许子类继承父类中的注解

@Retention作用范围如下图所示:

注解处理器

首先来自定义一个注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationInfo {

    String[] value();

    int requestCode() default 0;
}
  • 注解中定义的方法没有参数,且返回类型仅限于原始类型,字符串,枚举,注解或以上类型的集合
  • 注解中定义的方法可以有默认值

运行时解析注解

@Target(ElementType.METHOD)指明了我们的注解是作用在方法上的

@Retention(RetentionPolicy.RUNTIME)表示注解在程序运行时期也会存在,即注解信息也会加载到虚拟机VM中,所以可以通过反射来获取注解的相关信息:

编写一个类,声明方法,并在方法上声明我们的自定义注解,如下:

public class AnnotationExample {

    
    @AnnotationInfo(value = {"Android.permission.CALL_PHONE", "android.permission.CAMERA"}, requestCode = 10)
    public void requestPermission() {
        //其他逻辑
    }
}

接着来编写一个运行时解析注解的Java类:AnnotationRuntimeProcessor.java

public class AnnotationRuntimeProcessor {

    public static void main(String[] args) {
        try {
            //获取AnnotationExample的Class对象
            Class<?> cls = Class.forName("com.javastudy.Annotation.AnnotationExample");
            //获取AnnotationExample类中的方法
            Method[] methods = cls.getDeclaredMethods();
            for (Method method : methods) {
                //过滤不含自定义注解AnnotationInfo的方法
                boolean isHasAnnotation = method.isAnnotationPresent(AnnotationInfo.class);
                if (isHasAnnotation) {
                    method.setAccessible(true);
                    //获取方法上的注解
                    AnnotationInfo aInfo = method.getAnnotation(AnnotationInfo.class);
                    if (aInfo == null) return;
                    //解析注解上对应的信息
                    String[] permissions = aInfo.value();
                    System.out.println("value: " + Arrays.toString(permissions));

                    int requestCode = aInfo.requestCode();
                    System.out.println("requestCode: " + requestCode);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上面的逻辑很简单,反射拿到有注解对应类的Class对象,筛选含有注解的方法,最后获取方法上的注解并解析,运行结果如下:

value: [android.permission.CALL_PHONE, android.permission.CAMERA]
requestCode: 10

编译时解析注解

AbstractProcessor是javax下的API,java和javax都是Java的API(Application Programming Interface)包,java是核心包,javax的x是extension的意思,也就是扩展包。一般继承AbstractProcessor需要实现下面的几个方法:

public class ProcessorExample extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        //processingEnvironment提供各种工具类  如Elements Filer Types SourceVersion等
        super.init(processingEnvironment);
    }

    
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return super.getSupportedSourceVersion();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }
}
  • init(ProcessingEnvironment env): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements, Types和Filer。后面我们将看到详细的内容。
  • process(Set (? extends TypeElement) annotations, RoundEnvironment env): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。后面我们将看到详细的内容。
  • getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。
  • getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果你有足够的理由只支持Java 6的话,你也可以返回SourceVersion.RELEASE_6。推荐使用前者。

下面来看一个具体的例子,我们在新建android的普通model和library工程是没有javax的,所以我们需要新建一个java工程,先来看下整个包结构:

首先先定义了注解:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface CompileAnnotation {

    int value() default 0;
}

可见我们的注解是定义在变量FIELD上的,接着来编写我们的解析器:

@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.suyun.aopermission.annotation.CompileAnnotation")
public class AnnotationCompileProcessor extends AbstractProcessor {

    private Messager messager;
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        //processingEnvironment提供各种工具类  如Elements Filer Types SourceVersion等
        super.init(processingEnvironment);
        messager = processingEnv.getMessager();
        filer = processingEnv.getFiler();
    }

    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {

        messager.printMessage(Diagnostic.Kind.NOTE, "----------start----------");

        for (TypeElement annotation : annotations) {
            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(annotation);
            for (Element element : elements) {
                if (element.getKind() != ElementKind.FIELD) {
                    messager.printMessage(Diagnostic.Kind.ERROR, "Only FIELD can be annotated with AnnotationInfo");
                    return true;
                }
                //获取注解
                CompileAnnotation annotationInfo = element.getAnnotation(CompileAnnotation.class);
                //获取注解中的值
                int value = annotationInfo.value();
                messager.printMessage(Diagnostic.Kind.NOTE, "value: " + value);
            }
        }
        return true;
    }

}

一个简单的注解解析器就写完了,看代码知道我们只是简单的把注解里的值打印出来,接下来要做的就是把我们的@CompileAnnotation注解定义到我们类中的变量FILED上了,直接上代码:

public class MainActivity extends AppCompatActivity {

    @CompileAnnotation(value = 200)
    private TextView tv_text;
    
    //....省略其他代码....
}

OK,接下来就是见证奇迹的时刻了,当我们迫不及待的按下编译按钮后,发现什么都没有发生,注解里的信息并没有打印出来,what fu**!!!不要方,其实还少一步操作,我们只是定义了注解解析器,但是并没有把解析器注册到javac中,怎么注册呢,在main目录下新建resources->META-INF->services->javax.annotation.processing.Processor文件,文件里指定解析器的全路径,我的全路径是:

com.suyun.aopermission.processor.AnnotationCompileProcessor

写好之后的目录如下:

接着再来编译一下,这次有了结果:

注: ----------start----------
注: value: 200
注: ----------start----------

成功了,如果觉得上述的配置比较繁琐的话,可以选择使用Google开发的service库来代替上述配置,在build.gradle中配置:

compile 'com.google.auto.service:auto-service:1.0-rc2'

然后我们的解析器中这样写:

@AutoService(Processor.class)
public class AnnotationCompileProcessor extends AbstractProcessor {
//....其他逻辑....
}

没错,我们在注解解析器里又定义了@AutoService(Processor.class)注解,这样和上述配置是一样的效果

自动生成.class代码

编译时期我们可以根据需要自动生成.class代码,跟我们手动写.java代码编译生成的.class代码是一样的,自动生成有一样好处就是一些公共的或者重复的逻辑我们可以通过自动生成来减轻我们的工作了,通常自动生成.class代码需要用到JavaFileObject类,如下:

try {
    //packageName是包名
    JavaFileObject source = mFiler.createSourceFile(packageName);
    Writer writer = source.openWriter();
    //classStr代表的类里所有的字符
    writer.write(classStr);
    writer.flush();
    writer.close();

} catch (Exception e) {
    e.printStackTrace();
}

具体JavaFileObject的用法大家可以去搜下,因为也不复杂,这里就不多说了,因为整个类都需要我们手动写,一是比较麻烦,二是容易出错,square做了一个开源的javapoet库来帮我们减少工作量,javapoet地址:https://GitHub.com/square/javapoet

来看简单的一个栗子:

                //创建method方法类
                MethodSpec methodSpec = MethodSpec.methodBuilder("getValue")
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                        .returns(int.class)
                        .addStatement("return " + proAnnotation.value())
                        .build();
                //创建.class类
                TypeSpec typeSpec = TypeSpec.classBuilder("autoGenerate")
                        .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                        .addMethod(methodSpec)
                        .build();

                String packageName = processingEnv.getElementUtils().
                        getPackageOf(element).getQualifiedName().toString();
                try {
                    JavaFile javaFile = JavaFile.builder(packageName, typeSpec)
                            .addFileComment("this is auto generated")
                            .build();
                    javaFile.writeTo(filer);
                } catch (Exception e) {
                    e.printStackTrace();
                }

编译我们的代码,然后再build->generated->source->apt->debug目录下就可以看到自动生成的.class类了:

// this is auto generated
package org.ninetripods.MQ.javastudy;

public final class autoGenerate {
  public static int getValue() {
    return 100;
  }
}

更多用法请去javapoet的github上查看。

总结

自定义注解在一些优秀的三方库(如:EventBus ButterKnife等)中很常见,用于简化我们的代码,可以通过编译时解析注解生成.class类,统一去处理,所以学习自定义注解还是很有必要的。

到此这篇关于浅析Java自定义注解的用法的文章就介绍到这了,更多相关Java自定义注解内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 浅析Java自定义注解的用法

本文链接: https://www.lsjlt.com/news/200681.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 浅析Java自定义注解的用法
    目录注解定义注解注解处理器运行时解析注解编译时解析注解总结注解 注解为我们在代码中添加信息提供一种形式化的方法,使我们可以在源码、编译时、运行时非常方便的使用这些数据。 注解是在JA...
    99+
    2023-03-21
    Java自定义注解使用 Java自定义注解 Java 注解
  • 浅谈Java中注解Annotation的定义、使用、解析
    此例子,用于说明如何在Java中对“注解 Annotation”的定义、使用和解析的操作。注解一般用于自定义开发框架中,至于为什么使用,此处不作过多说明,这里只说明如何使用,以作备记。下面例子已测试,可以正常运行通过。1、注解自定义。这里定...
    99+
    2023-05-31
    java 自定义注解 解析
  • 浅谈Java自定义注解相关知识
    目录一、自定义注解格式二、注解本质分析三、注解属性及类型四、注解属性类型五、注解属性赋值简化操作一、自定义注解格式 分析 Java 中自带的 @Override 注解 , 源码如下 ...
    99+
    2022-11-12
  • Java自定义注解
    目录 一、什么是自定义注解 1)Java注解简介 2)Java注解分类 JDK基本注解 JDK元注解 自定义注解 如何自定义注解? 二、自定义注解 1)获取类上注解值 2)获取类属性上的注解属性值 3)获取方法上的注解值  4)获取参数修饰...
    99+
    2023-09-06
    java 开发语言
  • 浅谈自定义校验注解ConstraintValidator
    目录一、前言二、自定义参数校验器三、使用自定义注解一、前言 系统执行业务逻辑之前,会对输入数据进行校验,检测数据是否有效合法的。所以我们可能会写大量的if else等判断逻辑,特别是...
    99+
    2022-11-12
  • Java自定义注解的详解
    Java自定义注解Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容。在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解。Java1.5引入了注解,当前许多java框架中大量使用...
    99+
    2023-05-31
    java 自定义 注解
  • Java中自定义注解
    当使用Java编写应用程序时,我们常常使用注解来为程序添加附加信息,并且可以在运行时读取这些注解。除了Java提供的预定义注解外,我们还可以自定义注解来满足自己的需求。在本文中,我们将介绍Java中自定义注解的基础知识。 一、什么是注解? ...
    99+
    2023-09-24
    java 开发语言
  • Java注解详解及实现自定义注解的方法
    目录概念‍♀️作用⛹JDK中预定义的一些注解注解生成文档案例自定义注解格式本质属性:接口中的抽象方法元注解:用于描述注解的注解‍♂️在程序使用(解析)注解:获取注解中...
    99+
    2022-11-13
  • 浅谈java反射和自定义注解的综合应用实例
    前言前几天学习了反射和自定义注解,刚好工作中遇到一个小问题:前台传递到后台的必填字段为空,导致不能插入数据库。就是这样一个小问题,让我考虑到是否可以做一个通用的方法,让前台传递过来的必填字段在后台也校验一遍,如果传递为空,则把响应字段返回提...
    99+
    2023-05-31
    java 自定义注解 反射
  • Java 自定义注解的魅力
    目录注解是什么?元注解是什么?标准的元注解:@Target元注解:@Retention元注解:@Documented元注解:@Inherited元注解:自定义注解实现:自定义注解的简...
    99+
    2022-11-12
  • Java教程:JAVA自定义注解
    注解概念注解是Java SE 5.0版本开始引入的概念,它是对java源代码的说明,是一种元数据(描述数据的数据)。注解和注释的不同注释注释是对代码的说明,给代码的读者看,便于帮读者梳理业务逻辑;在程序代码中经常看到的以@ 开头的大部分是注...
    99+
    2023-06-02
  • Java怎么自定义注解
    这篇文章主要介绍“Java怎么自定义注解”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java怎么自定义注解”文章能帮助大家解决问题。注解注解为我们在代码中添加信息提供一种形式化的方法,使我们可以在...
    99+
    2023-07-05
  • JAVA自定义注解详情
    目录原理:元注解:@Retention参数讲解:案例:给一个类的String属性设置默认值总结原理: 注解的本质是继承Annotation的特殊接口,其具体实现类是Java运行时生成...
    99+
    2022-11-12
  • 详解Java中自定义注解的使用
    目录什么是注解注解的注意事项注解的本质自定义注解使用使用方式 1使用方式 2什么是注解 在早期的工作的时候 ,自定义注解写的比较多,可大多都只是因为 这样看起来 不会存在一堆代码耦合...
    99+
    2023-03-20
    Java自定义注解使用 Java自定义注解 Java 注解
  • javaSE基础java自定义注解原理分析
    目录1. 从注释的角度来理解注解2.提出问题3.编写注解4.通过Java反射获取方法的注解信息结束注解在JavaSE中算是比较高级的一种用法了,为什么要学习注解,我想大概有以下几个原...
    99+
    2022-11-12
  • 【Java 注解】自定义注解(注解属性与使用)
    文章目录 前言一、自定义注解与元注解1.注解属性类型 二、注解的生命周期以及作用目标1.生命周期2.作用目标 三,简单使用四,注解属性赋值简化 前言 Java注解是一种元数据(m...
    99+
    2023-10-21
    java spring spring boot log4j 经验分享 笔记 后端
  • Java注解怎么自定义使用
    这篇文章主要介绍了Java注解怎么自定义使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java注解怎么自定义使用文章都会有所收获,下面我们一起来看看吧。注解注解基本介绍注解概述:Java 注解(Annota...
    99+
    2023-07-05
  • Java自定义注解实现Redis自动缓存的方法
    在实际开发中,可能经常会有这样的需要:从MySQL中查询一条数据(比如用户信息),此时需要将用户信息保存至Redis。刚开始我们可能会在查询的业务逻辑之后再写一段Redis相关操作的代码,时间长了后发现这部分代码实际上仅仅做了Redis的写...
    99+
    2023-05-31
    java redis ava
  • 浅析vue中的自定义指令使用
    Vue3版本:有稍微变化,由于本文主要说的都是 Vue2 版本的(也是手上很多 vue2 版本的老项目在维护),Vue3的就不做详细介绍了,感兴趣可查看: cn.vuejs.org/guide/reusa…实战以下都是我在项目中用到的自定义...
    99+
    2023-05-14
    Vue 指令 自定义指令
  • Java中自定义注解如何使用
    本文小编为大家详细介绍“Java中自定义注解如何使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java中自定义注解如何使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是注解在早期的工作的时候 ,自定义...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作