注解(注解的概念、注解的分类、自定义注解、注解的原理)

一、注解的概念

注释: 给人看的,便于阅读代码, 对代码的描述

注解(Annotation): 对代码的描述, 作为代码形式保留下来,

Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

注释: 类似超市商品下面的标签, 描述商品 方便给顾客查看的

注解: 类似商品的条形码, 描述商品, 方便后期商品结算

注解的本质: 特殊的接口

声明注解: 创建了一个特殊接口

使用注解: @注解名(创建注解的一个对象)

1.注解的作用

1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等 2、跟踪代码依赖性,实现替代配置文件功能。比如Spring的注入,未来java开发,将大量注解配置,具有很大用处; 后期学习框架大量使用, 基于注解的开发 3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出

二、注解的分类:

1.内置注解: jdk定义好这个注解的声明, 开发者直接使用, 语法检查

  • @Override 检测重写

  • @Deprecated 已过时, 只是一个标志, 还是能够使用

  • @SuppressWarnings("all") 抑制编译器生成警告信息

    @SuppressWarnings("all")
    public class Demo1 {
        @SuppressWarnings("all")
        public void fun1(){
            System.out.println("dddd");
        }
    ​
        public static void main(String[] args) {
            Demo2 demo2 = new Demo2();
            demo2.fun1(10);
            Date d = new Date();
            //2022 - 1970 = 52  1900 ~ 1999
            System.out.println(d.getYear()); //2022  122
        }
    }
    @SuppressWarnings("all")
    class Demo2 extends Demo1{
        //重写Demo1的
       //@Override
    ​
        /**
         *
         * @param a  声明了方法参数
         * @return  声明方法返回值
         */
        @Deprecated
        public int fun1(int a){
            return 1;
        }
    ​
    }

    2.元注解: jdk定义好这个注解的声明, 在注解上使用,

1.@Documented-注解是否将包含在JavaDoc中

一个简单的Annotations标记注解,表示是否将注解信息添加在javadoc文档中

2.@Retention –什么时候使用该注解

Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间

它的取值如下:

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。

  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。

  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

    自定义注解: 保留期一定设置为runtime

    3.@Target–注解用于什么地方

默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括 ● ElementType.CONSTRUCTOR:用于描述构造器 ● ElementType.FIELD:成员变量、对象、属性(包括enum实例) ● ElementType.LOCAL_VARIABLE:用于描述局部变量 ● ElementType.METHOD:用于描述方法 ● ElementType.PACKAGE:用于描述包 ● ElementType.PARAMETER:用于描述参数 ● ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明

4.@Inherited – 定义该注释和子类的关系

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解

/**
 * 定义一个注解接口
 *  使用元注解对自定义的注解进行一些声明,说明
 */
@Documented  //表示该MyAnnotation1注解将会在生成doc文档上出现
//跟注解的参数赋值: 如果数组类型
@Target(value={ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})    //目标: 表示这个MyAnnotation1注解在那些地方使用
            // 如果没有写, 表示这个注解在任意的地方使用
@Retention(value= RetentionPolicy.RUNTIME) //自定义注解,保留期一定是Runtime
@Inherited // 表示这个MyAnnotation1这个注解标记的类, 这个类的子类是否继承父类该注解
public @interface MyAnnotation1 {
​
}
@MyAnnotation1  //创建了MyAnnotation1的一个对象
​
public class Student {
    @MyAnnotation1
    private int stuno;
    private String name;
​
    //@MyAnnotation1
    public Student( int stuno, String name) {
        this.stuno = stuno;
        this.name = name;
    }
​
    public Student() {
    }
​
    @MyAnnotation1
    public int getStuno() {
        return stuno;
    }
​
    public void setStuno(int stuno) {
        this.stuno = stuno;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
}
​
class  SubStudent extends  Student{
​
}

三、自定义注解

  1. 自定义注解: 关键字@interface , 默认继承Annotation接口, 本质就是一个接口

  2. 参数成员访问修饰符: public 或者是缺省的(还是public), 参数名后面必须是()

参数本质就是一个抽象方法

但是我们可以使用注解的时候,给参数赋值, 在声明的时候, 设置默认值

参数名() default 值;

如果一个参数没有设置default默认值, 使用这个注解的时候, 一定要给参数赋值,

如果使用default, 使用注解的时候, 可以给参数赋值,也可以不赋值(使用默认值)

  1. 参数的数据类型: 八大基本数据类型,String, 枚举,Class,注解类型,或者12种的数组类型

  2. 自定义的注解,可以有参数,也可以没有参数, 如果没有参数,这个注解没有意义

  3. 自定义注解,它的功能, 必须写代码解析注解,并给它赋予功能,自定义注解保留期: 一定为RUNTIME, 获取该注解的对象, 只能使用反射来获取

  • 自定义注解:

    声明注解的语法: @interface

    public @interface 注解名{
    //成员
    }

    使用注解:

    在方法,类型,包,构造方法,属性,参数... 使用注解

    @注解名

使用注解注意事项:

  1. 如果注解定义的参数是数组类型, 给数组类型赋值

a. 赋一个值: 参数名=值 或者: 参数名={值}

b. 赋多个值: 参数名={值1,值2,值3....}

/**
 * 自定义注解
*/
@Documented
@Target(value={ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Annotation1 {
 //成员参数
 // value参数有一个默认值, 这个方法默认返回值
 //给这个参数赋值, 就是设置这个方法的返回值
 String value();
 SexEnum sex() default SexEnum.MAN;
}
​
​
public enum SexEnum {
 MAN,WOMAN
}
@Annotation1(value="lisi",sex=SexEnum.WOMAN)
public class Demo3 {
}

商品类: 上架方法, 下架方法, 购买方法, 浏览方法,

/**
 * 商品类
 */
public class Product {
​
    @Role("管理员")
    public void shangJia(){
        System.out.println("商品正在上架...");
    }
​
    @Role("管理员")
    public void xiaJia(){
        System.out.println("商品正在下架...");
    }
​
    @Role("顾客")
    public void buy(){
        System.out.println("您正在购买商品...");
    }
​
    public void look(){
        System.out.println("您正在查看商品...");
    }
}

/**
 * 角色的注解
 */
//如果这个参数名为value, 单独给这个value参数赋值, 省略 value=
    // 如果给多个参数赋值, value= 一定不能省略
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {
    String value();
}

要求: 上架方法, 下架方法,必须要求用户的角色是"管理员"

购买方法: 必须要求用户的角色是"顾客"

浏览方法: 不需要角色

        
Scanner input = new Scanner(System.in);
        System.out.println("请输入您的角色:");
        String role = input.next();
        System.out.println("请输入您的调用的方法:");
        String methodName = input.next();
​
        //调用Product的方法
        Product product = new Product();
        //调用方法之前,判断你的角色与方法上定义的角色是否匹配
        //如果不匹配, 禁止访问, 如果匹配,允许访问
        //通过反射
        //1.获取Product类的Class对象
        Class clazz = Product.class;
        //2.获取指定方法名的Method对象
        Method method = clazz.getDeclaredMethod(methodName);
        //判断您的角色是否满足
        //3.获取这个方法上的需要角色: 使用注解
        //判断是否有@Role注解  isAnnotationPresent(注解的Class类型) true表示有, false:没有
        if(method.isAnnotationPresent(Role.class)){ //true
            //判断角色是否匹配
            //获取这个方法上@Role的value参数
            //获取方法上的注解对象  getAnnotation(类 annotationClass) 获取指定类型的注解对象
            //Annotation[] getAnnotations() 获取所有的注解对象
            Role obj = method.getAnnotation(Role.class);
            //获取它的参数值
            String value = obj.value();
            if(role.equals(value)){ //角色是否匹配
                System.out.println("您有权限访问...");
                method.invoke(product);
            }else{
                System.out.println("您没有权限访问...");
            }
​
        }else{//没有,直接执行
            method.invoke(product);
        }
        //3.调用方法
        //method.invoke(product);

四、注解的原理:

注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池

 这个运行时生成的动态代理对象是可以导出到文件的,方法有两种

  • 在代码中加入System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

  • 在运行时加入jvm 参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

 public static void main(String[] args) throws NoSuchMethodException {
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Class clazz = Test.class;
        Method method = clazz.getMethod("fun");
        Role annotation = method.getAnnotation(Role.class);
        System.out.println(annotation);
    }
​
    @Role(value="xxx")
    public static void fun(){
        System.out.println("....");
    }

你可能感兴趣的:(java基础1,java,spring,spring,boot)