文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java怎么自定义注解

2023-07-05 14:44

关注

这篇文章主要介绍“Java怎么自定义注解”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java怎么自定义注解”文章能帮助大家解决问题。

注解

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

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

定义注解

先来看内置注解@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作用范围如下图所示:

Java怎么自定义注解

注解处理器

首先来自定义一个注解:

@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();    }}

下面来看一个具体的例子,我们在新建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

写好之后的目录如下:

Java怎么自定义注解

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

注: ----------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库来帮我们减少工作量。

来看简单的一个栗子:

                //创建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 generatedpackage org.ninetripods.mq.javastudy;public final class autoGenerate {  public static int getValue() {    return 100;  }}

关于“Java怎么自定义注解”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯