目录
一、注解的概念
二、注解的类型
2.1、内置注解
2.2、元注解
2.2.1、各个元注解的作用
2.3、自定义注解
2.4、自定义注解实现及测试
一、注解的概念
1、注解的作用
 ①:注解一般用于对程序的说明,就像注释一样,但是区别是注释是给人看的,但是注解是给程序看的。
 ②:让编译器进行编译检查的作用,比如下边这个@Override注解是重写的意思,子类重写了父类的方法,但是改动了方法名,所以报错。
2、注解的格式
 注解是以“@注解名”在代码当中存在的,还可以添加一些参数值,例如 @SuppressWarnings(value = “unchecked”)
3、注解在哪里使用
 可以附加在package、class、method、field等上面,相当于给他们添加了额外的辅助信息。我们可以通过反射的方式对这些注解进行访问。
二、注解的类型
一般常用的注解分为三类:
2.1、内置注解
①:@Override:修辞方法的,表示一个方法重写了父类方法
②:@Deprecated:修辞方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。通俗来说就是遗弃。
③:@SuppressWarnings:用来抑制编译时的警告信息,括号里的的值包括
- deprecation:使用了过时的类或方法的警告
- unchecked:执行了未检查时的转换时的警告,集合就是未指定泛型
- fall through:当在switch语句使用时发生case穿透
- path:在类路径、源文件路径等中有不存在路径的警告
- serial:可序列化类上缺少serialVerisonUID定义时的警告
- finally:任何finally橘子不能完成时的警告
- all:以上所有情况的警告。
一个 @SuppressWarnings(“all”),
多个 @SuppressWarnings(value={“all”,“path”})
2.2、元注解
元注解的作用是负责对其他注解进行说明的注解。自定义注解时可以使用元注解。Java 5 定义了 4 个注解,分别是 @Documented、@Target、@Retention 和 @Inherited;Java 8 又增加了 @Repeatable 和 @Native 两个注解。可以在java.lang.annotation包中找到。
2.2.1、各个元注解的作用
1、@Target 指定一个注解的使用范围
即被 @Target 修饰的注解可以用在什么地方
@Target源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
   
    ElementType[] value();
    
}@Target 注解有一个成员变量(value)用来设置适用目标。以下为注解的枚举常量:
public enum ElementType {
    /** 类、接口(包括注解类型),或者枚举声明 */
    TYPE,
    /** 字段声明(包括枚举常量) */
    FIELD,
    /** 方法声明 */
    METHOD,
    /** 形参声明 */
    PARAMETER,
    /** 构造方法声明 */
    CONSTRUCTOR,
    /** 局部变量声明 */
    LOCAL_VARIABLE,
    /** 注解类型声明 */
    ANNOTATION_TYPE,
    /** 包声明 */
    PACKAGE,
    /**  类型参数声明 */
    TYPE_PARAMETER,
    
    /** 类型的使用 */
    TYPE_USE
}2、@Retention 描述注解保留的时间范围
即被描述的注解在它所修饰的类中可以被保留到何时
@Retention注解源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
   
    RetentionPolicy value();
    
}他的value是 java.lang.annotation.RetentionPolicy 枚举类型。
public enum RetentionPolicy {
   
   	//源文件保留
    SOURCE,
  	//编译期保留,默认值
    CLASS,
  	//运行期保留,可通过反射去获取注解信息
    RUNTIME
    
}生命周期大小排序为
SOURCE < CLASS < RUNTIME,前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,如 @Documented 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码,就用 CLASS 注解,如 @NonNull 注解;如果只是做一些检查性的操作,则可选用 SOURCE 注解,如 @Override 和 @SuppressWarnings 注解。
3、@Documented 描述javadoc
描述在使用javadoc工具为类生成帮助文档时是否要保留其注解信息。
@Documented注解源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。
4、@Inherited 阐述了某个被标注的类型是被继承的
@Inherited 是一个标记注解,用来指定该注解可以被继承。如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解。
举个例子:
注解
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInheritedAnnotation {
    public String name() default "liuliu";
}父类
@MyInheritedAnnotation(name = "parent")
public class Parent {
}子类
public class Child extends Parent {
    public static void main(String[] args) {
        Class<Child> child = Child.class;
        MyInheritedAnnotation annotation = child.getAnnotation(MyInheritedAnnotation.class);
        System.out.println(annotation.name());
    }
}输出:
 parent
5、@Repeatable注解
@Repeatable 是Java 8 新增,@Repeatable注解用于声明标记的注解为可重复类型注解,可以在同一个地方多次使用。Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。
@Repeatable 注解源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    
    Class<? extends Annotation> value();
    
}举个例子
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRepeatableAnnotation {
    RepeatableAnnotationTest[] value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MyRepeatableAnnotation.class)
public @interface RepeatableAnnotationTest {
    String key();
    String value();
}
@RepeatableAnnotationTest(key = "aa",value = "1a")
@RepeatableAnnotationTest(key = "bb",value = "2b")
public class RepeatableTest {
    public static void main(String[] args) {
        RepeatableAnnotationTest[] annotation = RepeatableTest.class.getAnnotation(MyRepeatableAnnotation.class).value();
        for (RepeatableAnnotationTest a : annotation) {
            System.out.println(a);
        }
    }
}输出:
 @annotation.RepeatableAnnotationTest(key=aa, value=1a)
 @annotation.RepeatableAnnotationTest(key=bb, value=2b)
6.@Native注解
@Native注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于@Native注解不常使用,了解即可。
2.3、自定义注解
 ①:@interface是用来声明一个注解的,格式public @interface 注解名{定义内容}
 ②:其中的每一方法实际上是声明了一个配置参数
 ③:方法的名称就是参数的名称
 ④:返回值类型就是参数的类型(返回值类型只能是基本数据类型,Class,String,enum)
 ⑤:可以通过default来声明参数的默认值
 ⑥:如果只有一个参数成员,一般参数名称为value
 ⑦:注解参数必须有值,我们自定义注解元素时,经常使用空字符串,0作为默认值

2.4、自定义注解实现及测试
Controller.annotation:
import java.lang.annotation.*;
/**
 * @author yunyan
 * @date 2023/7/22
 */
//该注解可以应用于类、接口(包括注解类型)、枚举
@Target(ElementType.TYPE)//ElementType.TYPE
//该注解标记的元素可以被Javadoc 或类似的工具文档化
@Documented
//该注解的生命周期,由JVM 加载,包含在类文件中,在运行时可以被获取到
@Retention(RetentionPolicy.RUNTIME)//RUNTIME
public @interface Controller {
    String value() default "";
}RequestMapping.annotation:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @author yunyan
 * @date 2023/7/22
 */
//该注解的生命周期,由JVM 加载,包含在类文件中,在运行时可以被获取到
@Retention(RetentionPolicy.RUNTIME)
//该注解可以应用于类、接口(包括注解类型)、枚举 以及方法上
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequestMapping {
    String value() default "";
    RequestMethod[] method() default {};
}
enum RequestMethod {
    GET, POST
}TestController.java
import com.qcby.sourcecode.annotation.Controller;
import com.qcby.sourcecode.annotation.RequestMapping;
@Controller
@RequestMapping("test")
public class TestController {
    @RequestMapping
    public  String index(){
        System.out.println("test->index");
        return "";
    }
    @RequestMapping("index1")
    public  String index1(){
        System.out.println("test->index1");
        return "";
    }
}如果把controller注解在方法上,会报错,原因是我们定义的@controller注解只能写在类、接口、枚举上面。
Main.java 用于将该项目中被标记@Controller注解的类,创建实例并存入一个Map中。
import com.qcby.sourcecode.annotation.Controller;
import com.qcby.sourcecode.annotation.RequestMapping;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 *
 * @author yunyan
 * @date 2023/07/24
 */
public class Main {
    public static List<String>arr= new ArrayList<>();
    public static Map<String,Object>controllerMap = new HashMap<>();
    static {
        String fileName =  "E:\\SpringTest\\src";
        File file = new File(fileName);
        getFilePath(file);
        try {
            chooseController();
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
    private static void getFilePath(File file) {
        File[] fs = file.listFiles();
        for (File f : fs) {
            if (f.isDirectory()){
                getFilePath(f);
            }
            if (f.isFile()) {
                String filepath = f.toString();
                filepath = filepath.split("src")[1];
                filepath = filepath.substring(1,filepath.length());
                if( filepath.endsWith(".java")) {
                    //把是.java文件的全类名放到arr中
                    arr.add(filepath.replace("\\", ".").replace(".java", ""));
                }
            }
        }
    }
    //查找所有controller,并创建对象装入Map里(“url”:Object)
    private static void chooseController() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        for(String file: arr){
            Class<?> aClass = Class.forName(file);
            if(aClass.isAnnotationPresent(Controller.class)){
                Object o = aClass.getDeclaredConstructor().newInstance();
                RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
                if(annotation==null){
                    throw  new RuntimeException("没有标记RequestMapping");
                }
                controllerMap.put(annotation.value(),o);
            }
        }
    }
}结果:



















