啊啊,终于搞明白了,原来注解是这么一回事。6000+字理解注解【一】
文章目录
- 前言
- 复习注解
- 元注解
- @Retention 存活时间
- @Documented 文档
- @Target 目标
- @Inherited 继承
- @Repeatable 可重复
- 注解属性
- 注解实现-反射
- 反射获取注解方法
- 注解的使用场景
- 后续
- 导航
前言
这两天在gitHub上看一个开源项目,发现项目中有许多自定义注解。虽然在学JavaSE的时候学个注解,但是主要讲的是如何自定义注解,和一些元注解的知识点。并没有涉及到注解如何实现具体的功能。直到看到这个项目,突然醍醐灌顶。
由于篇幅过长,分成两篇来写。
复习注解
注解是在Java 1.5 的时候被引入的,注解的创建与接口十分相似。就是在interface
关键字前面加一个@
符号。
public @interface MyAnnotation{}
创建玩注解之后就可以在想要添加主机的地方使用了,但是想要让注解能够正常工作,还需要给它化化妆。什么是化妆那,这里就要引入元注解的一个概念了,元注解他也是一个注解,只不过它是用来修饰注解的注解。有点迷?不慌咱慢慢看。
元注解
元注解一共有五种分别为:
- @Retention
- @Documented
- @Target
- @Inherited
- @Repeatable
他们的使用方法就是在创建注解的时候在,所要创建的注解上使用他们:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
... ...
public @interface MyAnnotation{}
@Retention 存活时间
Retention中文意思是保留的意思。当@Retention
应用到一个注解上的时候 ,通过参数可以设置这个注解的存活时间。什么是存活时间,就是说通过不同参数可以确定@Retention
修饰的注解在那种情况下会消失,是保留到源码阶段、还是编译阶段、还是加载到JVM那。
下面看一下它的参数:
- RetentionPolicy.SOURCE
注解只被保留到编译阶段,当编译器编译到它的时候看到参数为
RetentionPolicy.SOURCE
会直接将其从源码中剔除。
- RetentionPolicy.SOURCE
注解制备保留到编译进行的时候,也就是能保证在编译期间注解也存在,但是不能进入JVM中。
- RetentionPolicy.SOURCE
注解可以保留到程序中,它会被加载到JVM中从而在服务中起作用。
@Documented 文档
这个注解,翻译过来就是文档。那他并没有其他功能上的作用,主要作用就是能讲注解中的元素包含到Javadoc中去。
@Target 目标
Target 有目标的意思,他的意思就是说,这个注解能够使用的地方是哪里,是方法上、类上还是参数上。我们通过它的参数就可以进行设置。
参数 | 作用 |
---|---|
ElementType.ANNOTATION_TYPE | 可以使用在注解上 |
ElementType.CONSTRUCTOR | 可以在构造方法上使用 |
ElementType.FIELD | 可以在属性上使用注解 |
ElementType.LOCAL_VARIABLE | 作用在局部变量上 |
ElementType.METHOD | 作用在方法上 |
ElementType.PACKAGE | 作用在包上 |
ElementType.PARAMETER | 作用在方法内的参数上 |
ElementType.TYPE | 可以给类型进行注解,比如类、接口、枚举 |
@Inherited 继承
Inherited 意思为继承,这个元注解的作用有点绕。就是说如果@Inherited
注解作用与自定义注解@MyAnnotation
上,然后在A类上使用了自定义注解,然后A类的子类,就相当于也拥有@MyAnnotation
注解。
/*自定义注解*/
@Inherited
public @interface MyAnnotation{}
---------------------------
/*A类*/
@MyAnnotation
public class A{}/*B类继承A类*/
public class B extent A{}/*
//此时,B类也继承了A类的注解。相当于
@MyAnnotation
public class B extent A{
}
*/
@Repeatable 可重复
这个注解是在Java 1.8的时候加进来的。那可重复是什么意思那,就是说使用注解的时候可以同时使用多次注解。
public class A{@MyAnnotation("教师")
@MyAnnotation("公务员")public void test(){}}
如果想实现这样的效果,那么就需要使用@Repeatable
注解修饰@MyAnnotation
注解。@MyAnnotation
才可以在一个方法或其它地方使用多次。
注解属性
注解的属性其实就好像实体类里面的属性,只是写法上有稍微的不同。但是注解是没有方法的,
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
... ...
public @interface MyAnnotation{int id();
String value();
}
注意这里id()
和value()
表示的不是方法而是属性也可叫做注解的变量。
注解的赋值方法就是在使用注解的时候,通过注解中的对应属性="xxx"
的形式
@MyAnnotation(id=101,value="Hello Annotation")
public void test(){}
注解属性可以设置默认值,也就是如果使用注解时没有赋予特定的值,就使用默认值。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
... ...
public @interface MyAnnotation{int id() default 101;
String value();
}
这里还有一个注意点,如果注解中只有一个名字为value的属性的时候,应用这个注解的时候可以直接填写属性值。
pulic @interface MyAnnotation{String value;
}
@MyAnnotation("Hi")
public void test(){}
如果定义的这个注解中没有属性值,我们在使用注解的时候就不用在写括号了
@MyAnnotation
public void test(){
}
注解实现-反射
注解的知识经过上面的内容,应该能够有个了解。那么我们定义好后注解,也是用了,但是并没有什么实质的效果。哪有人该说了那定义有什么用?其实不然,如果真的没有用那么Java官方就不会定义注解了。
好下面我们就给自定义的注解注入灵魂,实现当使用注解后完成相应的功能。那该如何实现那,注解中又不能写方法,之有属性值, 我们也不能通过new获取注解。这里就要提到一个比较重要的知识点就是反射。
我们可以通过反射获取作用在类或者方法上的注解让,后进行对应的处理。
反射获取注解方法
注解通过反射获取。首先通过class对象的isAnnotationPresent()
方法判断他是否应用了某注解。
public Boolean isAnnotationPresent(Class<? extent Annotation> annotationClass) {}
然后通过getAnnotation()方法来获取Annotation对象。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
或者是getAnnotations()方法
public Annotation[] getAnnotations(){}
前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。
如果获取到的Annotation如果不为null,则就可以调用他们的属性方法了。比如
@MyAnnotation()
public class Test{public static void main(String[] agrs){boolean hasAnnotation = Test.class.isAnnotationPressent(TestAnnotation.class);if(hasAnnotation){TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);System.out.println('id:'+testAnnotation.id());}}
}
运行结果:
id:101
上面是获取类上的注解,其实方法、属性的注解都是可以通过反射进行获取的。
自定注解:
package com.zhao.annotationaop.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {String value() default "Hello Annotation";
}
编写测试类
public class TestAnnotation {@MyAnnotation("Hi")public void testMethod(){System.out.println("fun");}public static void main(String[] args) throws NoSuchMethodException {Class<TestAnnotation> aClass = TestAnnotation.class;Method msg = aClass.getDeclaredMethod("testMethod");msg.setAccessible(true);MyAnnotation annotation = msg.getAnnotation(MyAnnotation.class);System.out.println(annotation.value());}
}
运行结果
Hi
这里需要注意一下,如果想要注解在运行时能够获取到,那么必须加上@Retention(RetentionPolicy.RUNTIME)
这个注解参数,因为这个参数是指,注解能在jvm中被执行。
注解的使用场景
到这里应该大家对注解都有一定的了解了,但是还是可能会有疑惑注解到底有什么用呐。
我们不妨先看看官方的回答:
注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。
注解有许多用处,主要如下:
- 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
- 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
- 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
值得注意的是,注解不是代码本身的一部分。
从官方的话我们可以看出注解的作用并不是来写主要业务的,而是通过注解实现一些对代码的处理。
如果大家用过LomBok在实体类上面加一个@Data
就可以帮助我们生成实体类的get和set的方法,在或者说Swagger生成接口文档,在对应的接口上加上对应的注解就可以实现生成对应的接口文档。注解只是起到到了标签的作用,具体的实现方式是有对应的程序获取到注解这个标识,然后去处理产生的。
那么我们可以用注解做什么呢?
现在我们就动手写一个自己的注解,通过这个注解来检测程序是否报错。这是一个简单的小案例。
首先写一个注解@Jiance
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Jiance {}
要测试的程序Cheshi
并在方法上加上注解
public class Cheshi {@Jiancepublic void suanShu(){System.out.println("1234567890");}@Jiancepublic void jiafa(){System.out.println("1+1="+1+1);}@Jiancepublic void jiefa(){System.out.println("1-1="+(1-1));}@Jiancepublic void chengfa(){System.out.println("3 x 5="+ 3*5);}@Jiancepublic void chufa(){System.out.println("6 / 0="+ 6 / 0);}public void ziwojieshao(){System.out.println("我写的程序没有 bug!");}
}
运行检测程序TestJiance
public class TestJiance {public static void main(String[] args) {Cheshi cheshi = new Cheshi();Class<Cheshi> cheshiClass = Cheshi.class;Method[] declaredMethods = cheshiClass.getDeclaredMethods();StringBuilder log = new StringBuilder();log.append("**************日志****************\n");int num = 0;for (Method declaredMethod : declaredMethods) {Jiance annotation = declaredMethod.getAnnotation(Jiance.class);if (annotation!=null){try {declaredMethod.setAccessible(true);declaredMethod.invoke(cheshi,null);} catch (Exception e) {num++;log.append(declaredMethod.getName()+":error:"+e.getCause().getMessage()+"\n");}}}log.append("cheshi has "+num+ " error");System.out.println(log);}
}
执行结果:
1+1=11
1234567890
1-1=0
3 x 5=15
**************日志****************
chufa:error:/ by zero
cheshi has 1 error
这样我们就成了这小的案例。我们通过我们自定义的注解,来检测所有cheshi
类中有错的方法。
注解的作用主要取决于你想用它做什么。
后续
利用反射获取注解并实现功能通过上面已经实现了,但是还有一种方式也可以获取注解,并实现对应的功能逻辑。
那就是利用Spring框架的Aop来实现,AOP面向切面编程,我们可以利用AOP做很多事情。那如果AOP遇到注解会发生什么那。
下一篇我们利用自定义注解和Aop实现接口防刷的功能。也就是在一段时间内如果大量访问接口,就会触发保护的一个案例。
End!!! 如有疑问请留言评论!
导航
接口防刷案例【传送门】
啊啊,终于搞明白了,原来注解是这么一回事。6000+字理解注解【一】相关推荐
- php image gallery in metro ui,终于搞明白糟糕的METRO UI是怎么回事了
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 METRO的意思是地铁,微软决策层犯SB请了一些更SB的设计师以美国地铁标识牌的设计为"灵感"而创造的糟糕界面. 界面尽量以文字为主 ...
- 终于搞明白gluPerspective和gluLookAt的关系了
2019独角兽企业重金招聘Python工程师标准>>> 终于搞明白gluPerspective和gluLookAt的关系了 函数原型 gluLookAt(GLdoble eyex,G ...
- matlab中异步电机y接法,电机接法Y和三角形什么区别,今天终于搞明白了!
原标题:电机接法Y和三角形什么区别,今天终于搞明白了! 电动机三角形接法和星形接法区别: 1.异步电动机因其结构简单.价格便宜.可靠性高等优点被广泛应用.但在起动过程中起动电流较大,所以容量大的电动机 ...
- python和c混合编程 gil_终于搞明白python与gil
感想:东看一篇文章西看一篇文章,终于把gil的概念理顺了 我们都知道,比方我有一个4核的CPU,那么这样一来,在单位时间内每个核只能跑一个线程,然后时间片轮转切换.但是Python不一样,它不管你有几 ...
- java的向下转型_终于搞明白向下转型的作用了,还不懂的进来看下.
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 之前一直搞不明白,向下转型的实际意义,虽然知道向下转型怎么写, 现在我来讲解下 向下转型 的实际作用,如果有错的话,大家可以指出, 如果认为我说的对的话可 ...
- 感染新冠后为啥会丧失嗅觉?最新《细胞》论文终于搞明白了!
感染新冠后,患者的一大症状就是会丧失嗅觉,然而这一症状背后的机理却始终没有得到阐明.日前,顶尖学术期刊<细胞>上的一篇论文,终于揭开了背后的谜底--原来新冠病毒会让嗅觉受体出现下调,使得人 ...
- 最详细的解释小白也能听懂,终于搞明白了
前言 2021春季社招正在火热招聘当中,很多人都挂在了技术知识掌握还是不够广,不够深,甚至连一些基础的问题都只能模模糊糊的回答出来.你说,这跳槽不是跳了个寂寞吗? 其实,根本原因还是对于自己的技术栈掌 ...
- 真香定律!阿里、字节跳动、京东、腾讯、小米等名企高频面试,终于搞明白了
前言 说真的,在 Java 使用最多的集合类中,List 绝对占有一席之地的,它和 Map 一样适用于很多场景,非常方便我们的日常开发,毕竟存储一个列表的需求随处可见.尽管如此,还是有很多同学没有弄明 ...
- html调用eps,EPS/改性EPS/石墨EPS:终于搞明白了
在现代外墙保温装饰工程上经常会爆出EPS,XPS,改性EPS啊-- 这些高科技范儿的词语,对于很多用户或者外行经常搞的头晕脑胀,今天DPX君就为大家讲解一下.一 什么是聚苯板(EPS) 聚苯板全称聚苯 ...
最新文章
- 数据结构与算法分析c++第四版_研分享 | 人工智能学院数据结构与算法分析考研备考整理...
- 零基础python必背代码-30个Python常用极简代码,拿走就用
- 用仿ActionScript的语法来编写html5——终篇,LegendForHtml5Programming1.0开源库件
- 18135usm_佳能PZ-E1+EF-S 18-135mm f/3.5-5.6 IS USM镜头 小型工作室的利器
- ecshop 手机端没做和电脑一样显示
- Android Camera (13)---MTK平台相机插值修改
- centos5.2 lamp安装指南
- 在网页中嵌入百度地图的步骤
- 7.docker pull
- PHP正则获取HTML里需要的数据
- mongodb详细优化策略方案
- MOS管自举电路工作原理以及升压自举电路结构图分析
- phpStudy点击phpadmin出404窗口的解决办法
- linux下Ftp客户端程序与Makefile
- (转)eclipse 打开pom.xml文件很慢 设置pom.xml打开方式
- Android 自定义注解处理器
- 叶新伟 php,基于php+mysql技术bbs论坛设计的开发与实现最终版(样例3)
- 永中软件自己越描越黑
- 用户存续期价值评估CLV(三) Gamma-Gamma模型 Python模拟
- 微信小程序-从注册到上架