Java中的注解
注解的基础知识
元注解:@retention @target @document @inherited
annotation型定义为@interface, 所有的annotation会自动继承java.lang.annotation这一接口,并且不能再去继承别的类或是接口。
参数成员只能用public或默认(default)这两个访问权修饰
参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和string、enum、class、annotations等数据类型,以及这一些类型的数组。
要获取类、方法和字段的注解信息,必须通过java的反射技术来获取 annotation对象,除此之外没有别的获取注解对象的方法
注解也可以没有定义成员, 不过这样注解就没啥用了,只起到标识作用
jdk的元注解
jdk提供了4种元注解,分别是@retention
、@target
、@document
和@inherited
四种。这4个注解是用来修饰我们自定义的其他注解的,因此称为元注解。
1. @retention
定义注解的保留策略。首先要明确生命周期长度 source < class < runtime ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,
那只能用 runtime 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 butterknife),就用 class注解;如果只是做一些检查性的操作,比如
@override 和@suppresswarnings,则可选用 source 注解。
@retention(retentionpolicy.source) //注解仅存在于源码中,在class字节码文件中不包含 @retention(retentionpolicy.class) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得, @retention(retentionpolicy.runtime) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
2. @target
定义注解的作用目标。也就是这个注解能加在类的哪些元素上。
@target(elementtype.type) //接口、类、枚举、注解 @target(elementtype.field) //字段、枚举的常量 @target(elementtype.method) //方法 @target(elementtype.parameter) //方法参数 @target(elementtype.constructor) //构造函数 @target(elementtype.local_variable)//局部变量 @target(elementtype.annotation_type)//注解 @target(elementtype.package) ///包
3.@document
说明该注解将被包含在javadoc中
4.@inherited
说明子类可以继承父类中的该注解。如果一个注解@xx被元注解@inherited修饰,然后使用@xx修饰了一个类a,那么类a的子类b也可以继承@xx注解。
自定义注解
@retention(retentionpolicy.runtime) @target(elementtype.type) public @interface description { string[] name(); string desc(); string author() default "jtzeng"; int age() default 21; }
想要处理这些注解信息,我们必须使用反射技术获取这些注解然后再做相应的处理。
下面举个列子:使用反射获取当前包下面所有标注了description
的类信息。
@description(desc = "java lover", author = "csx") public class csx { } @description(desc = "php lover", author = "zr") public class zr { }
上面定义了两个类,分别用description
标注。
下面的代码首先获取了当前类所在的包名,然后将这个包下面的class遍历了一遍。通过反射将标注有description
注解的类信息打印了出来。
public class demo { public static void main(string[] args) { class<demo> democlass = demo.class; string name = democlass.getpackage().getname(); list<class<?>> classes = getclasses(name); for (class<?> aclass : classes) { description annotation = aclass.getannotation(description.class); if(annotation!=null){ system.out.println(annotation.author()+":"+annotation.desc()); } } system.out.println("end..."); } public static list<class<?>> getclasses(string packagename){ //第一个class类的集合 list<class<?>> classes = new arraylist<class<?>>(); //是否循环迭代 boolean recursive = true; //获取包的名字 并进行替换 string packagedirname = packagename.replace('.', '/'); //定义一个枚举的集合 并进行循环来处理这个目录下的things enumeration<url> dirs; try { dirs = thread.currentthread().getcontextclassloader().getresources(packagedirname); //循环迭代下去 while (dirs.hasmoreelements()){ //获取下一个元素 url url = dirs.nextelement(); //得到协议的名称 string protocol = url.getprotocol(); //如果是以文件的形式保存在服务器上 if ("file".equals(protocol)) { //获取包的物理路径 string filepath = urldecoder.decode(url.getfile(), "utf-8"); //以文件的方式扫描整个包下的文件 并添加到集合中 findandaddclassesinpackagebyfile(packagename, filepath, recursive, classes); } else if ("jar".equals(protocol)){ //如果是jar包文件 //定义一个jarfile jarfile jar; try { //获取jar jar = ((jarurlconnection) url.openconnection()).getjarfile(); //从此jar包 得到一个枚举类 enumeration<jarentry> entries = jar.entries(); //同样的进行循环迭代 while (entries.hasmoreelements()) { //获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如meta-inf等文件 jarentry entry = entries.nextelement(); string name = entry.getname(); //如果是以/开头的 if (name.charat(0) == '/') { //获取后面的字符串 name = name.substring(1); } //如果前半部分和定义的包名相同 if (name.startswith(packagedirname)) { int idx = name.lastindexof('/'); //如果以"/"结尾 是一个包 if (idx != -1) { //获取包名 把"/"替换成"." packagename = name.substring(0, idx).replace('/', '.'); } //如果可以迭代下去 并且是一个包 if ((idx != -1) || recursive){ //如果是一个.class文件 而且不是目录 if (name.endswith(".class") && !entry.isdirectory()) { //去掉后面的".class" 获取真正的类名 string classname = name.substring(packagename.length() + 1, name.length() - 6); try { //添加到classes classes.add(class.forname(packagename + '.' + classname)); } catch (classnotfoundexception e) { e.printstacktrace(); } } } } } } catch (ioexception e) { e.printstacktrace(); } } } } catch (ioexception e) { e.printstacktrace(); } return classes; } public static void findandaddclassesinpackagebyfile(string packagename, string packagepath, final boolean recursive, list<class<?>> classes){ //获取此包的目录 建立一个file file dir = new file(packagepath); //如果不存在或者 也不是目录就直接返回 if (!dir.exists() || !dir.isdirectory()) { return; } //如果存在 就获取包下的所有文件 包括目录 file[] dirfiles = dir.listfiles(new filefilter() { //自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件) public boolean accept(file file) { return (recursive && file.isdirectory()) || (file.getname().endswith(".class")); } }); //循环所有文件 for (file file : dirfiles) { //如果是目录 则继续扫描 if (file.isdirectory()) { findandaddclassesinpackagebyfile(packagename + "." + file.getname(), file.getabsolutepath(), recursive, classes); } else { //如果是java类文件 去掉后面的.class 只留下类名 string classname = file.getname().substring(0, file.getname().length() - 6); try { //添加到集合中去 classes.add(class.forname(packagename + '.' + classname)); } catch (classnotfoundexception e) { e.printstacktrace(); } } } } }
输出如下:
csx:java lover zr:php lover end...
jdk8可重复注解
重复注解:即允许在同一申明类型(类,属性,或方法)前多次使用同一个类型注解。
在java8 以前,同一个程序元素前最多只能有一个相同类型的注解;如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。
public @interface authority { string role(); } public @interface authorities { //@authorities注解作为可以存储多个@authority注解的容器 authority[] value(); } public class repeatannotationuseoldversion { @authorities({@authority(role="admin"), @authority(role="manager")}) public void dosomething(){ } }
java8 新增了重复注解,其使用方式为:
//这边还是需要定义注解容器 @repeatable(authorities.class) public @interface authority { string role(); } public @interface authorities { authority[] value(); } public class repeatannotationusenewversion { @authority(role="admin") @authority(role="manager") public void dosomething(){ } }
不同的地方是,创建重复注解 authority 时,加上@repeatable,指向存储注解 authorities,在使用时候,直接可以重复使用 authority 注解。从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点。但是,仍然需要定义容器注解。
两种方法获得的效果相同。重复注解只是一种简化写法,这种简化写法是一种假象:多个重复注解其实会被作为“容器”注解的 value 成员的数组元素处理。(一种语法糖而已,java中类似的语法还有很多。具体内容可以参考博客java中的语法糖)
参考
- https://www.cnblogs.com/a591378955/p/8350499.html
- https://blog.csdn.net/liupeifeng3514/article/details/80722003
上一篇: 神奇的H110主板:复活39年前的ISA、28年前的PCI插槽
下一篇: 牙套勾住了