jinhy的技术分享

Java注解小总结

2018-05-09  本文已影响69人  _Away_y

背景

最近想做Spring Boot的总结,众所周知,Spring有不少自己的注解,Spring Boot更甚,它的口号是做到零配置,目的也是为了避免配置零散到多个地方的场景。那么如果想要深入了解Spring Boot的话势必要知道它读取到注解后做哪些处理,这些后面会总结。这篇文章算是前提吧。顺势而为。

什么是注解

官方定义如下:

注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响

注解有许多用处,主要如下:
- 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
- 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
- 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取


有些小伙伴可能一看到元数据就已经有些懵了,这里先简单介绍一下元数据:元数据即描述数据的数据。比如说我们描述windows系统里面的一个文件,它是由很多属性定义的,像大小、编码、权限等等,这些就叫做它的元数据。如果想更深入理解可以看下wiki,说的实在是太太太详细了。

回到主题,对于官方定义我觉得可以一句话一句话的理解,参照我定义中标出的关键语句。

最后三句话是简单说了一下注解可应用的主要场景,分别对应了不同的三个周期,周期的问题下面篇幅说明。

注解的定义

定义注解其实很简单,就跟定义一个类或者接口区别不是很大,只是把class | interface换为@interface

同时定义注解我们还需要引入另一个概念,就是元注解

元注解

大家估计现在看名字就能猜出元注解的作用了,就是修饰描述注解的注解。

元注解有以下五种。

@Retention

Retention翻译过来是保留的意思。它描述的就是被修饰注解的生命周期。

它的取值如下,这里我们可以跟上一节里面它的主要用途来对应着看:

@Documented

其作用是能够将注解中的元素包含到 Javadoc 中去。

@Target

其作用是指定被修饰注解可用到哪些地方。

取值如下:

@Inherited

继承的意思。如果超类被某个注解修饰,而这个注解被@Inherited修饰,那么继承超类的子类默认也被这个注解修饰。

@Repeatable

其为Java 1.8版本新引入元注解。场景可以举例说明,比如各个公司都有职位设置,而且也有兼职的这种制度,如果我们定义一个注解为@Role,属性是职位那么可能就有@Role("manager")或者@Role("developer"),而我们修饰的那个人可能就是一个做开发的经理。

代码举例如下:

@interface Roles {
    Role[]  value();
}


@Repeatable(Roles.class)
@interface Role{
    String value() default "";
}


@Role("manager")
@Role("developer")
public class SuperMan{

}

注解的属性

在注解的定义中我们知道注解是可以提供数据的,而这个方式就是定义属性。

这个属性的定义跟我们正常的认知不太一样,它采用的是定义无参无方法体类似定义方法的方式来定义的。其中方法名就是属性的名字,而返回值就是属性的类型。

而且可以通过default关键字来定义默认值。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    public int id() default -1;

    public String msg() default "hello world";

}

如上所示,我们定义了一个名为MyAnnotation的注解,它包含两个属性且都有默认值。

可以这样使用:

@MyAnnotation(id=1, msg="msg")
public class MyClass() {}

//因为注解中有默认值,所以我们也可以不自定义属性值
@MyAnnotation()
public class MyClass() {}

//因为有些注解可能只有一个value属性,那直接引号就可以
@OnlyValue("bye")
public class MyClass() {}

//有些注解一个属性都没有,那就完全可以不带括号
@NoAttribute
public class MyClass() {}

如何获取注解

我们知道注解的生命周期主要有三种,而我们大部分用的都是Runtime。不同周期获取注解的方式是不一样的,我们这版先主要讨论Runtime期间的获取,其他的以后再扩充。

而运行期间进行获取的方式主要是反射(有没有其他方式没有研究过,如果知道其他的方式大家可以留言补充)。作为开发人员的明显看代码更形象。如下所示:

@MyAnnotation(msg="hello")
public class Test {
    
    @Check(value="hi")
    int a;

    @Perform
    public void testMethod(){}

    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }

    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(MyAnnotation.class);
        if ( hasAnnotation ) {
            MyAnnotation testAnnotation = Test.class.getAnnotation(MyAnnotation.class);
            //获取类的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //获取一个成员变量上的注解
            Check check = a.getAnnotation(Check.class);

            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
            
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            if ( testMethod != null ) {
                // 获取方法中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } 
    }
}

参考资料

上一篇下一篇

猜你喜欢

热点阅读