JAVA注解

1 注解

注解提供一种为程序元素设置元数据的方法。

元数据是添加到程序元素如方法、字段、类和包上的额外信息

注解是一种分散式的元数据设置方式,XML是集中式的设置方式

注解不会直接干扰程序代码的运行

所有的注解类都是继承自java.lang.annotation.Annotation接口

注解的功能:

1.作为特定的标记,用于告诉编译器一些信息(@Override表示方法重写了父类的方法,编译器会去检查是否父类有对应的方法)

2.编译时动态处理,如动态生成代码(lombook工具中的@Data方法,在编译时动态生成类的set/get等方法)

3.运行时动态处理,作为额外信息的载体,如获取注解信息(一个注解,本质上来说只是一种特殊的注释而已)

注解的分类

1.标准注解:@Override、@Deprecated、@SuppressWarnings

2.元注解:@Retention、@Target、@Inherited、@Documented

3.自定义注解

2 元注解

用来修饰注解的注解,通常用在注解的定义上

@Target:注解的作用目标

@Retention:注解的生命周期

@Documented:注解是否应当被包含在JavaDoc文档中

@Inherited:是否允许子类继承该注解

@Target

 描述所修饰的注解的使用范围

查看Target(java.lang.annotation.Target)的源码

注解Target需要传入枚举类型ElementType的数组来表示该注解的使用范围

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

ElementType的属性和代表的使用范围

属性

注解使用范围

TYPE

允许所修饰的注解作用在类、接口、枚举上

FIELD

允许所修饰的注解作用在类字段上

METHOD

允许所修饰的注解作用在类方法上

PARAMETER

允许所修饰的注解作用在方法参数上

CONSTRUCTOR

允许所修饰的注解作用在类构造函数上

LOCAL_VARIABLE

允许所修饰的注解作用在本地局部变量上

ANNOTATION_TYPE

允许所修饰的注解作用在注解上

PACKAGE

允许所修饰的注解作用在包上

TYPE_PARAMETER

允许所修饰的注解作用在泛型类的参数上

TYPE_USE

允许所修饰的注解作用在上面所提的所有范围中

举几个我们熟悉的注解的例子:

Spring框架中的@Controller注解

Spring框架中的@Autowired注解

Spring框架中的@RequestParam注解

@Target注解

@Retention

标注注解被保留时间的长短

用于定义注解的生命周期,即被描述的注解在什么范围内有效

查看Retention(java.lang.annotation.Retention)的源码

注解Retention需要传入枚举类型RetentionPolicy来表示该注解的生命周期

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

RetentionPolicy的属性和代表的生命周期

属性

含义

Source

在源文件中保存注解,但是在编译的class文件中就不会存在注解了

Class

注解不仅会出现在源代码中,还会出现在class文件中

RUNTIME

在运行时可以通过反射能够获取该注解的信息

3 自定义注解

属性名其实就是注解的参数,返回值的类型就是参数的类型

1.定义注解类

只能作用在类的成员变量上,生命周期是保留到运行时的,可以通过反射获取

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonInfoAnnotation {
    //名字
    public String name();
    //年龄
    public int age() default 22;
    //擅长的开发语言
    public String[] language();
}

作用在类和方法上,生命周期是保留到运行时的,可以通过反射获取

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CourseInfoAnnotation {
    //课程名
    public String courseName();
    //课程标签
    public String courseTag();
}

2.在类上使用注解

@CourseInfoAnnotation(courseName = "软件工程",courseTag = "1")
public class Course {
    @PersonInfoAnnotation(name="陈老师",language = {"JAVA","C++"})
    private String teacher;
    @CourseInfoAnnotation(courseName = "计算机基础",courseTag = "2")
    private void getInfo()
    {}
}

在程序里不对注解做处理,注解看起来没什么用,恰恰是这样,所以注解不干扰程序的运行

3.在运行时获取注解信息

获取注解信息依赖于AnnotatedElement接口,而反射中的类Method/Field等都实现了这个类

获取注解信息:

Annotation[] getAnnotations()----获取对象上(类)的所有注解,包括对象继承的注解

getAnnotation(Class<T> annotationClass)---获取对象上的单个指定注解

boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)---判断指定类型的注解是否存在于此元素上

解析类的注解:

public class AnnotationParser {
    //解析类注解
    static void parserTypeAnnotation() throws ClassNotFoundException {
        Class clazz = Class.forName("com.base.annotation.Course");
        //这里只获取class对象的注解,不会获取类成员的注解
        Annotation[] annotations=clazz.getAnnotations();
        for(Annotation annotation:annotations){
            CourseInfoAnnotation courseInfoAnnotation=(CourseInfoAnnotation) annotation;
            System.out.println(courseInfoAnnotation.courseName()+" "+courseInfoAnnotation.courseTag());
        }
    }
    public static void main(String[] args) throws ClassNotFoundException {
        AnnotationParser.parserTypeAnnotation();
    }
}

解析成员字段的注解:

public class AnnotationParser {
    //解析成员字段注解
    static  void  parserFieldAnnotation() throws ClassNotFoundException, NoSuchFieldException {
        Class clazz = Class.forName("com.base.annotation.Course");
        Field[] fields = clazz.getDeclaredFields();//获取类的每一个成员字段
        for(Field field:fields)
        {
            //判断每个成员字段是否有被PersonInfoAnnotation注解修饰
            if(field.isAnnotationPresent(PersonInfoAnnotation.class))
            {
                PersonInfoAnnotation personInfoAnnotation=field.getAnnotation(PersonInfoAnnotation.class);
                System.out.println(personInfoAnnotation.name()+ personInfoAnnotation.age());
                for(String l:personInfoAnnotation.language())
                {
                    System.out.println(l);
                }
            }
        }

    }
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { 
        AnnotationParser.parserFieldAnnotation();
    }
}

解析成员函数的注解:

public class AnnotationParser {
   
    //解析成员方法注解
    static  void  parserMethodAnnotation() throws ClassNotFoundException{
        Class clazz = Class.forName("com.base.annotation.Course");
        Method[]  methods= clazz.getDeclaredMethods();//获取类的每一个成员方法
        for(Method method:methods)
        {
            //判断每个成员方法是否有被CourseInfoAnnotation注解修饰
            if(method.isAnnotationPresent(CourseInfoAnnotation.class))
            {
                CourseInfoAnnotation courseInfoAnnotation=method.getAnnotation(CourseInfoAnnotation.class);
                System.out.println(courseInfoAnnotation.courseName()+" "+courseInfoAnnotation.courseTag());
            }
        }

    }
    public static void main(String[] args) throws ClassNotFoundException{
        AnnotationParser.parserMethodAnnotation();
    }
}

如果修改注解的Retention属性不为RUNTIME,执行后无法解析注解。可以发现一旦修改Retention不为RUNTIME,注解就无法通过反射来获取

4 注解的工作原理

1.通过键值对的形式为注解属性赋值

2.编译器检查注解的使用范围,将注解信息写入元素属性表

3.运行时JVM将RUNTIME的所有注解属性取出并最终存入map中

4.创建AnnotationInvocationHandler实例并传入前面的map

5.JVM使用JDK动态代理为注解生成代理类,并初始化处理器

6.调用invoke方法,通过传入方法名返回注解对应的属性值

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值