您的位置 首页 > 德语词汇

annotation是什么意思?一文读懂Annotation

大家好,今天来为大家分享annotation是什么意思的一些知识点,和一文读懂Annotation的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!

annotation是什么意思?一文读懂Annotation

欢迎大家关注我的微信公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

IntheJavacomputerprogramminglanguage,anannotationisaformofsyntacticmetadatathatcanbeaddedtoJavasourcecode.Classes,methods,variables,parametersandJavapackagesmaybeannotated.LikeJavadoctags,Javaannotationscanbereadfromsourcefiles.UnlikeJavadoctags,JavaannotationscanalsobeembeddedinandreadfromJavaclassfilesgeneratedbytheJavacompiler.ThisallowsannotationstoberetainedbytheJavavirtualmachineatrun-timeandreadviareflection.Itispossibletocreatemeta-annotationsoutoftheexistingonesinJava.

Java注解又称Java标注,是JDK5.0版本开始支持加入源代码的特殊语法元数据。Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java虚拟机可以保留标注内容,在运行时可以获取到标注内容。当然它也支持自定义Java标注。

这定义已经够清晰了,但你可能有个疑问,老周啊,通过反射获取标注内容,反射在哪体现啊。别问,一问又是要看底层源码了。

既然这样,那我们就来看下JDKjava.lang.annotation包的结构:

看见带@标识的没,一共有6个,所以JDK源码里定义了6个注解。

等一等,元注解又是什么?不急,我们来看下一节。

元注解的作用就是负责注解其它注解,它们被用来提供对其它annotation类型作说明。

@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.ANNOTATION_TYPE)\npublic@interfaceDocumented{\n}\n

不管是这里的Documented还是我们自定义的注解,都需要使用@interface标识,使用了这个标识,会自动继承java.lang.annotation.Annotation接口,由编译程序自动完成其它细节。你又可能会说了,为啥是这样的标识就会自动继承这个接口呀。额,这是人家的事先约定的规范,你如果你是JDK源码的开发人员,也可以自己定义其它标识,亦或是写了一套比注解还好用的规范呢。

在定义注解时,不能继承其它的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

1、Annotation类型里面的参数该如何设定

2、元注解的用途

在详细说这四个元数据的含义之前,先来看一个在工作中会经常使用到的@Autowired注解,此注解中使用到了@Target、@Retention、@Documented这三个元注解。

@Target({ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD,ElementType.ANNOTATION_TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic@interfaceAutowired{\nbooleanrequired()defaulttrue;\n}\n

2.1@Target元注解

@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.ANNOTATION_TYPE)\npublic@interfaceTarget{\nElementType[]value();\n}\n

@Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的,标明作用范围;取值在java.lang.annotation.ElementType进行定义的。

publicenumElementType{\n/**类,接口(包括注解类型)或枚举的声明*/\nTYPE,\n\n/**属性的声明*/\nFIELD,\n\n/**方法的声明*/\nMETHOD,\n\n/**方法形式参数声明*/\nPARAMETER,\n\n/**构造方法的声明*/\nCONSTRUCTOR,\n\n/**局部变量声明*/\nLOCAL_VARIABLE,\n\n/**注解类型声明*/\nANNOTATION_TYPE,\n\n/**包的声明*/\nPACKAGE,\n\n/**作用于类型参数(泛型参数)声明*/\nTYPE_PARAMETER,\n\n/**作用于使用类型的任意语句(不包括class)*/\nTYPE_USE\n}\n

根据此处可以知道@Autowired注解的作用范围:

//可以作用在构造方法、方法、方法形参、属性、注解类型上\n@Target({ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD,ElementType.ANNOTATION_TYPE})\n

2.2@Retention元注解

@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.ANNOTATION_TYPE)\npublic@interfaceRetention{\nRetentionPolicyvalue();\n}\n

@Retention注解,翻译为持久力、保持力。即用来修饰自定义注解的生命周期。

同样使用了RetentionPolicy枚举类型对这三个阶段进行了定义:

publicenumRetentionPolicy{\n/**\n*注解将被编译器忽略掉\n*/\nSOURCE,\n\n/**\n*注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为\n*/\nCLASS,\n\n/**\n*注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到\n*/\nRUNTIME\n}\n

再详细描述下这三个阶段:

注意:实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME。

2.3@Documented元注解

@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.ANNOTATION_TYPE)\npublic@interfaceDocumented{\n}\n

@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。

2.4@Inherited元注解

@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.ANNOTATION_TYPE)\npublic@interfaceInherited{\n}\n

@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。

@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。

上面把注解与元注解说完了,那得实战一下吧。其实很多人在工作中已经用到过了或者自己没用到过但项目中有用到过。但你有没有想过自定义注解是怎么关联到目标方法的,是的没错,就是我们开头讲的定义,通过反射。

这里我就拿一个我们项目中自定义注解的例子来说:

1、标记日志打印的自定义注解

@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic@interfacePrintLog{\n}\n

2、定义一个切面,在切面中对使用了@PrintLog自定义注解的方法进行环绕增强通知

@Component\n@Aspect\n@Slf4j\npublicclassPrintLogAspect{\n@Around(value="@annotation(com.riemann.core.annotation.PrintLog)")\npublicObjecthandlerPrintLog(ProceedingJoinPointjoinPoint)throwsThrowable{\nStringclazzName=joinPoint.getSignature().getDeclaringTypeName();\nStringmethodName=joinPoint.getSignature().getName();\nObject[]args=joinPoint.getArgs();\n\nMap<String,Object>nameAndArgs=getFieldsName(this.getClass(),clazzName,methodName,args);\nlog.info("Enterclass[{}]method[{}]params[{}]",clazzName,methodName,nameAndArgs);\n\nObjectobject=null;\ntry{\nobject=joinPoint.proceed();\n}catch(Throwablethrowable){\nlog.error("Processclass[{}]method[{}]error",clazzName,methodName,throwable);\n}\nlog.info("Endclass[{}]method[{}]",clazzName,methodName);\nreturnobject;\n}\n\nprivateMap<String,Object>getFieldsName(Classclazz,StringclazzName,StringmethodName,Object[]args)throwsNotFoundException{\nMap<String,Object>map=newHashMap<>();\nClassPoolpool=ClassPool.getDefault();\nClassClassPathclassPath=newClassClassPath(clazz);\npool.insertClassPath(classPath);\n\nCtClasscc=pool.get(clazzName);\nCtMethodcm=cc.getDeclaredMethod(methodName);\nMethodInfomethodInfo=cm.getMethodInfo();\nCodeAttributecodeAttribute=methodInfo.getCodeAttribute();\nLocalVariableAttributeattr=(LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);\nif(attr==null){\n//exception\n}\nintpos=Modifier.isStatic(cm.getModifiers())?0:1;\nfor(inti=0;i<cm.getParameterTypes().length;i++){\nmap.put(attr.variableName(i+pos),args[i]);\n}\n\nreturnmap;\n}\n}\n

3、最后,在Controller中的方法上使用@PrintLog自定义注解即可;当某个方法上使用了自定义注解,那么这个方法就相当于一个切点,那么就会对这个方法做环绕(方法执行前和方法执行后)增强处理。

@RestController\npublicclassController{\n@PrintLog\n@GetMapping(value="/user/findUserNameById/{id}",produces="application/json;charset=utf-8")\npublicStringfindUserNameById(@PathVariable("id")intid){\n//模拟根据id查询用户名\nStringuserName="公众号【老周聊架构】";\nreturnuserName;\n}\n}\n

4、在浏览器中输入网址:http://127.0.0.1:8080/api/user/findUserNameById/666回车后触发方法执行,发现控制台打印了日志

Enterclass[Controller]method[findUserNameById]params[{id=666}]\nEndclass[Controller]method[findUserNameById]\n

这样的话,项目中的Controller类的请求日志我们不必每个方法都打印一遍了,而且收集日志到日志中心请求的参数也有具体统一的格式,排查问题也方便了不少。使用自定义注解+AOP实现日志的打印,有木有如丝滑般顺畅的感觉,哈哈,这样代码看着也优雅了不少。

这里要说一下,写这篇文章的初衷是我一个好哥们在群里提了这么个问题,找不到自定义注解和相关方法的关联。我第一时间想到的是反射,然后再想想自己项目中的场景,虽说是AOP实现,但注解是如何通过反射获取值的呢?AOP切面织入底层是如何实现的呢?有了这两点疑问,所以老周才下写下这篇文章,那下来两篇就会对这个两个疑点进行揭秘,敬请期待。

欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

END,本文到此结束,如果可以帮助到大家,还望关注本站哦!

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023