java中注解的使用及解析

介绍

java中有四大类型,分别是:类、接口、枚举、注解。注解是jdk1.5加入到语言规范的,注解主要有两个作用,一,给编译器或者虚拟机传递信息,例如@Override方法重载,@SuppressWarnings抑制编译警告等等;二、开发人员可以携带一些信息到运行时期而不用改变原有的代码逻辑。所有注解都是Annotation接口的子接口,Annotation中的方法:

public interface Annotation {
 	//判断两个注解是否相等
    boolean equals(Object obj);

   //返回hashCode
    int hashCode();

   	//返回注解的描述信息
    String toString();

    //返回注解的类型
    Class<? extends Annotation> annotationType();
}

定义注解

import java.lang.annotation.*;

@Inherited
@Documented
@Repeatable(Another.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE,ElementType.METHOD})
public @interface MyAnnotation {
    String name() default "zs";
}

Another中的定义

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface Another {
    MyAnnotation[] value();
}

在MyAnnotation注解中用到了基本常用的元注解类型,下面一一解释

1、 @Inherited注解

Inherited意思是可继承的,意思是标注此注解的类,在被继承时,子类在查找注解信息是可以直接找到,而不用通过继承结构依次向上查找。java虚拟机自动帮我们实现类结构的查找。

2、@Documented注解

此注解的意思是被标注的注解会生成java的doc文档

3、@Repeatable注解

Repeatable可重复的,被标注的注解可以多次声明在一个类上`

@MyAnnotation
@MyAnnotation
@MyAnnotation
public class Order {}

但是我们要指定容器注解,容器注解有几个要求:

  1. 必须有一个value属性,并且返回类型必须为可重复注解的数组类型,参见Another注解
  2. Retention保留策略,生名周期必须大于等于可重复注解的生存周期,参见Another注解
  3. Target标注的目标对象,是可重复注解可标注的目标对象的子集,参见Another注解
  4. 可重复注解上出现的元注解,容器注解上也得出现,例如:@Inherited,@Documented,参见Another注解

4、@Retention注解

Retention保留,只有一个方法: RetentionPolicy value() 保留策略,也就是生命周期,有三种生命周期

public enum RetentionPolicy {
   	//源码上,也就是 .java文件
    SOURCE,

    //字节码文件上, .class文件
    CLASS,

    //运行时期,虚拟机内
    RUNTIME
}

5、@Target目标

@Target中有一个方法,ElementType[] value(),可标注的元素类型,总共有十个:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    //类、接口(包括注解)、枚举
    TYPE,

    /** Field declaration (includes enum constants) */
    //字段(包括枚举常量字段)
    FIELD,

    /** Method declaration */
    //方法上
    METHOD,

    /** Formal parameter declaration */
    //参数上
    PARAMETER,

    /** Constructor declaration */
    //构造器上
    CONSTRUCTOR,

    /** Local variable declaration */
    //局部变量
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    //注解类型
    ANNOTATION_TYPE,

    /** Package declaration */
    //包上,基本不使用
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
     //类型参数,例如: List<@MyAnnotation T>
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
     //个人理解的不是很全面,可以用在泛型以及泛型边界上
     //List<@MyAnnotation T extends @MyAnnotation Number>
    TYPE_USE
}

注解中方法可以返回的类型

1、基本类型及其数组类型

基本类型包括四类八种,整型:byte,short,int ,long;浮点型:float,double;字符型:char;布尔型:boolean

2、引用类型及其数组类型

引用类型有限制,只能使用:String,Class,Enum,Annotation(字符串,Class对象,枚举,注解)

注意
  1. 注解的方法不能有参数
  2. 可以使用default指定默认值, String name() default “zs”;
  3. 注解中可以声明常量以及内部类或接口,和普通的接口一样
import java.lang.annotation.*;

@Inherited
@Documented
@Repeatable(Another.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD})
public @interface MyAnnotation {
    String name() default "zs";

	//声明一个常量,默认为public static final
    String name = "zs";
    //声明一个类,默认为public static
    class Cat{
        
    }
}

注解原理

首先把注解标注在UserMapper上,当然这个注解的保留策略需要是运行时(RUNTIME)

@MyAnnotation
public interface UserMapper {
}

进行测试

 @Test
    public void testAnnotationClass(){
        //把注解标注在UserMapper上
        MyAnnotation annotation = UserMapper.class.getAnnotation(MyAnnotation.class);
        if (annotation != null) {
            System.out.println(annotation.getClass());
        }
    }

输出的结果是:class com.sun.proxy.$Proxy5,所以注解的原理就是JDK的动态代理

解析注解

首先我们要对解析的注解做一些约定,表明我们解析注解的用途。
例如:解析方法上的注解,用于解析方法参数中的占位符,当然可以根据实际情况进行其他的约定

  1. 注解可以声明在方法上
  2. 生命周期是运行时
  3. 有一个name方法,返回一个字符串
//name是一个占位符#{name}
@MyAnnotation(name = "ls")
public void process(String name) {
    //..............
}
//处理方法上的注解,假设这一步已经拿到process方法
public String parse(Method method) {
    //获取注解上的全部方法
    Annotation[] annotations = method.getAnnotations();
    for (Annotation annotation : annotations) {
        String parse = parse(annotation);
        if (parse != null) {
            return parse;
        }
    }
    return null;
}
//解析注解
public String parse(Annotation annotation) {
    try {
        //1、拿到name方法,注解中的方法是不允许有参数的
        Method method = annotation.getClass().getDeclaredMethod("name");
        //2、执行value方法
        Object invoke = method.invoke(annotation);
        //3、如果返回值是String类型,则返回,否则返回null
        if (invoke instanceof String) {
            return (String) invoke;
        } else {
            return null;
        }
    } catch (Exception e) {
        //没有name方法返回null
        return null;
    }
}

我们不需要知道注解的类型,或者注解中有哪些方法,只要这些注解按照我们的约定就行。
可以利用jdk的动态代理或者cglib代理,生成代理类对方法参数进行拦截,设置参数为我们解析的类型,如果没有解析到,则使用原来的参数即可

总结

随着时间段推移,注解使用的也越来越广泛,所以知道注解用法及如何解析对我们开发者而言是很有帮助的,我只是对注解做了简单的介绍,考虑到实际的业务场景,解析注解还是非常繁琐的,但是万变不离其宗,注解就是jdk动态代理生成的普通的java类

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值