【修真院Java小课堂】Annotation注解
标题:
【修真院Java小课堂】Annotation注解
开场语:
大家好,我是IT修真院西安分院第三期学员,一枚正直纯洁善良的JAVA程序员,今天给大家分享一下,修真院官网JAVA任务七,深度思考中的知识点——Annotation注解
(1)背景介绍:
1.1 注解概念
Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
PS:Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。
1.2 注解的用处
1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param 等
2、跟踪代码依赖性,实现替代配置文件功能。
3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。
(2)知识剖析:
2.1 常见标准的Annotation:
1.)Override
java.lang.Override是一个标记类型注解,它被用作标注方法。它说明了被标注的方法重写了父类的方法,起到了断言的作用。如果我们使用了这种注解在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。
2.)Deprecated
Deprecated也是一种标记类型注解。当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的“延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated,但编译器仍然要报警。
3.)SuppressWarnings
SuppressWarning不是一个标记类型注解。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。
2.2 元注解
java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):
@Documented –注解是否将包含在JavaDoc中
@Retention –什么时候使用该注解
@Target –注解用于什么地方
@Inherited – 是否允许子类继承该注解
1.)@Retention– 定义该注解的生命周期
● RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
● RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
● RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
2.)Target – 表示该注解用于什么地方。默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括
● ElementType.CONSTRUCTOR:用于描述构造器
● ElementType.FIELD:成员变量、对象、属性(包括enum实例)
● ElementType.LOCAL_VARIABLE:用于描述局部变量
● ElementType.METHOD:用于描述方法
● ElementType.PACKAGE:用于描述包
● ElementType.PARAMETER:用于描述参数
● ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明
3.)@Documented–一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。
4.)@Inherited – 定义该注释和子类的关系
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
(3)常见问题:
自定义注解
(4)解决方案:
自定义注解:
自定义注解类编写的一些规则:
1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
2. 参数成员只能用public或默认(default)这两个访问权修饰
3. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法
(5)编码实战:
自定义注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Qiao {
String value() default "";
}
简单使用
@Qiao("qiao")
public class People {
@Qiao("乔")
public void test1(){}
public void test2(){};
}
主要内容是通过反射去获取注解所携带的信息,进行逻辑判断
Class clazz = Class.forName("annotation.People");
//获取类上的所有注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation a : annotations) {
System.out.println("annotations========" + a);
}
//获取Qiao注解
Qiao annotation = (Qiao) clazz.getAnnotation(Qiao.class);
System.out.println("annotation======" + annotation);
//输出value值
System.out.println("value========" + annotation.value());
//判断Qiao.class是否在该类上
System.out.println(clazz.isAnnotationPresent(Qiao.class));
在上面的基础上,做一个简单的处理
public static void process(String clazz) throws ClassNotFoundException {
int passed = 0;
int failed = 0;
//获取类实例
Class c = Class.forName(clazz);
//获取方法
Method[] method = c.getMethods();
for (Method m : method) {
System.out.println(m);
//System.out.println(m.isAnnotationPresent(Testable.class));
//判断方法上有没有Testable注解
if (m.isAnnotationPresent(Qiao.class)) {
System.out.println(m);
//如果存在passed+1
passed++;
} else {
//如果不存在failed+1
failed++;
}
}
System.out.println("passed========" + passed);
System.out.println("failed========" + failed);
}
public static void main(String[] args) throws ClassNotFoundException {
Test2.process("annotation.People");
}
(6)拓展思考:
6.1 @RequestMapping
//不用管,url的别名
String name() default "";
//下面的path和value两个代表的含义相同;@AliasFor注解使不同属性期相同的作用
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
//请求方法
RequestMethod[] method() default {};
//指定参数的类型
String[] params() default {};
//header传参
String[] headers() default {};
//指定数据传参的格式
String[] consumes() default {};
//指定返回的内容类型
String[] produces() default {};
DispatcherServlet是如何通过url快速找到相对应的接口方法?
简化理解, (1)Controller的方法上加了@RequestMapping (2)通过上面简单的Demo演示,同理; 先通过反射获取到所有的方法,通过方法去判断每个方法上面是否有@RequestMapping注解; 如果存在,获取value=“/u/People”中的数据,以这个路径(或拼接为完整路径)为Key值;以方法名为value,存储在一个Map中; (3)以上过程都在初始加载的时候完成,当有请求访问时,通过拼接或截取额路径为Key去Map中查询相对应的方法,直接运行。(7)参考文献:
https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html
百度百科
(8)更多讨论:
Q1:注解起到的作用
A1:注解只起到标志(如Serializable)和携带数据的作用,本身与代码逻辑无关,具体的逻辑处理需要程序员编写。
Q2:java.lang包 应该有5个基本注解
A2:除了上面3个,还有:@SafeVarargs堆污染警告、@FunctionalInterface指定该接口是函数式接口;
Q3:元注解@Inherited
A3:是否允许子类继承该注解;
当一个类A上的注解@Z(包含元注解@Inherited)时,验证A类的子类上是否也有@Z注解时,会返回true;
(9)鸣谢:
(10)结束语:
今天的分享就到这里啦,欢迎大家点赞、转发、留言、拍砖~
上一篇: spring3 mvc 注解
下一篇: mvc