注解概念
一、 什么是注解
简单说,注解是说明程序的,给计算机看的,就像我们写代码时候,注释是描述程序的,给程序员看的。
二、 作用分类
- 编写代码:通过代码里的标识的注解生成文档【生成doc文档】
- 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
- 编译检查:通过代码里的标识让编译器能够实现基本的编译检查
生成doc文档演示案例
package com.company;
/**
* 注解javadoc演示
* @Author xhd
* @Date 2020/3/13 14:46
* @Version 1.0
*/
public class AnnoDemo01 {
/**
* 计算两数的和
* @param a
* @param b
* @return
*/
public int add(int a, int b){
return a+b;
}
}
在index.html就能看到类方法文档
自定义注解
格式:public @interface 注解名称{}
本质:注解的本质,编译后是一个借口,改接口默认继承Annotation接口
属性:接口中的抽象方法
- 属性的返回值类型有下列取值
基本数据类型:
String
枚举
注解
以上类型的数组
package com.company;
/**
* @Author xhd
* @Date 2020/3/13 14:19
* @Version 1.0
*/
public @interface MyAnnotation {
String name();
int num();
// 数组
String[] strs();
//枚举
Person per();
}
- 定义了属性,使用时候需要给属性赋值
(1)如果定义了属性是,使用default关键字给属性默认初始化值,则使用注解时候,可以不进行属性的赋值
(2)如果只有一个属性需要赋值,并且属性的名称叫value,则value可以省略,直接定义值。例如 @GettMapping("/hello")。
(3)数组赋值时,值使用{}包裹,如果数组中值只有一个,{}可以省略
元注解
概念:用于描述注解的注解
@Retention
- Retention英文意思有保留、保持的意思,它表示注解存在阶段是保留在源码(编译期),字节码(类加载)或者运行期(JVM中运行)。在@Retention注解中使用枚举RetentionPolicy来表示注解保留时期
- @Retention(RetentionPolicy.SOURCE),注解仅存在于源码中,在class字节码文件中不包含
- @Retention(RetentionPolicy.CLASS), 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
- @Retention(RetentionPolicy.RUNTIME), 注解会在class字节码文件中存在,在运行时可以通过反射获取到
- 如果我们是自定义注解,则通过前面分析,我们自定义注解如果只存着源码中或者字节码文件中就无法发挥作用,而在运行期间能获取到注解才能实现我们目的,所以自定义注解中肯定是使用 @Retention(RetentionPolicy.RUNTIME)
@Documented
- Document的英文意思是文档。它的作用是能够将注解中的元素包含到 Javadoc 中去
@Target
- Target的英文意思是目标,这也很容易理解,使用@Target元注解表示我们的注解作用的范围就比较具体了,可以是类,方法,方法参数变量等,同样也是通过枚举类ElementType表达作用类型
- @Target(ElementType.TYPE) 作用接口、类、枚举、注解
- @Target(ElementType.FIELD) 作用属性字段、枚举的常量
- @Target(ElementType.METHOD) 作用方法
- @Target(ElementType.PARAMETER) 作用方法参数
- @Target(ElementType.CONSTRUCTOR) 作用构造函数
- @Target(ElementType.LOCAL_VARIABLE)作用局部变量
- @Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用该属性)
- @Target(ElementType.PACKAGE) 作用于包
- @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
- @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class (jdk1.8加入)
@Inherited
- Inherited的英文意思是继承,但是这个继承和我们平时理解的继承大同小异,一个被@Inherited注解了的注解修饰了一个父类,如果他的子类没有被其他注解修饰,则它的子类也继承了父类的注解。
/**自定义注解*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
}
/**父类标注自定义注解*/
@MyTestAnnotation
public class Father {
}
/**子类*/
public class Son extends Father {
}
/**测试子类获取父类自定义注解*/
public class test {
public static void main(String[] args){
//获取Son的class对象
Class<Son> sonClass = Son.class;
// 获取Son类上的注解MyTestAnnotation可以执行成功
MyTestAnnotation annotation = sonClass.getAnnotation(MyTestAnnotation.class);
}
}
@Repeatable
- Repeatable的英文意思是可重复的。顾名思义说明被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义
- @Repeatable 注解是用于声明其它类型注解的元注解,来表示这个声明的注解是可重复的。@Repeatable的值是另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解。
/**一个人喜欢玩游戏,他喜欢玩英雄联盟,绝地求生,极品飞车,尘埃4等,则我们需要定义一个人的注解,他属性代表喜欢玩游戏集合,一个游戏注解,游戏属性代表游戏名称*/
/**玩家注解*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface People {
Game[] value() ;
}
/**游戏注解*/
@Repeatable(People.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Game {
String value() default "";
}
/**玩游戏类*/
@Game(value = "LOL")
@Game(value = "PUBG")
@Game(value = "NFS")
@Game(value = "Dirt4")
public class PlayGame {
}
注解的解析
注解的解析就是为了获取注解里面的属性值
例如:现在要求自定义一个注解,要求根据传值获取类名,方法名
执行该类方法
- 创建一个类:Dog
package com.annotation;
/**
* @Author xhd
* @Date 2020/3/14 23:53
* @Version 1.0
*/
public class Dog {
public void run(){
System.out.println("dog run with four legs");
}
}
- 自定义一个注解
package com.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author xhd
* @Date 2020/3/14 23:47
* @Version 1.0
* 描述需要执行的类名,方法名
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
String className();
String methodName();
}
-执行框架
package com.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* @Author xhd
* @Date 2020/3/14 23:55
* @Version 1.0
*/
@Pro(className = "com.annotation.Dog",methodName ="run")
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.解析注解
//1.1获取该类字节码文件对象
Class<ReflectTest> reflectTestClass = ReflectTest.class;
//2.获取注解对象
Pro pro = reflectTestClass.getAnnotation(Pro.class);
//3.调取注解对象中定义的抽象方法,获取返回值
//3.1实际上就是在内存中生成一个接口实现类的对象
/**
* public class ProImpl implements Pro{
* public String className(){
* return "com.annotation.Dog";
* }
* public String methodName(){
* return "run";
* }
*}
*/
String className = pro.className();
String methodName = pro.methodName();
//4.利用反射实现
//4.1 加载该类进内存
Class<?> aClass = Class.forName(className);
//4.2 创建对象
Object o = aClass.newInstance();
//4.2获取方法对象
Method method = aClass.getMethod(methodName);
//4.3执行方法
method.invoke(o);
}
}
执行结果: