java注解(review(@语法小糖豆))

145 阅读4分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

前言

这个东西其实和我们的python的装饰器也就是“语法糖豆”不像,只是我习惯了,所以我就叫它小糖豆。那么这个玩意其实更像是一个特殊接口,特殊标记。不过这个东西也贼有用,像里面原来的Spring框架,里面有大量的注解来代替我们配置文件。后面如果要自己写一个小框架这个东西绝对是少不了的。 包括我们常用的 javadoc的文档注释 这里插一嘴,我们在idea里面可以设置一下我们的注释模板 在我们的setting里面

image.png

那么闲话就说到这里,那么我们注解的实现的话还是要用到反射的。

内置注解

这个其实就是官方的内置的注解。 这几个也是老朋友了,第一个是 @Override @Deprectated @SuppressWarnings

第一个不用说,第二个标注过时,第三个用来屏蔽警告的。

@SuppressWarnings("all")

哈哈,不讲武德~

定义注解

首先这个注解的格式很简单其实就相当于声明。其实这玩意就是一个特殊的接口。

格式

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnot {
    int age();
    String name() default "狗蛋";

}

这里分两个部分,第一个部分其实是啥,是那个我们的对我们自己定义的注解的描述。 也叫作原注解,这里简单说明一下。

元注解

首先是第一个,这个是告诉你作用域,你的这个注解是作用在哪里的?

image.png

image.png

这个是一个枚举类型的,可以看到不少作用域。

这里有几个小细节注意,一个是如果我们想要作用多个作用域

@Target({ElementType.TYPE,其他你想的位置})

直接这样。

还有一个是这个value,这个呢其实是个特殊属性,如果你的接口里面只需要一个值,那么你就可以直接那啥用value,这样使用的时候,我们就不需要制定元素的值了,这个接下来会说。

@Retention(RetentionPolicy.RUNTIME)

这个是运行的一个阶段,也就是在那个java代码的三个阶段嘛,我们一般时在运行时。

image.png

之后那两个注解,一个是说,javadoc的时候,要不要显示那个类用了这个注解,还一个是说,如果有子类继承,要不要继承注解,也就是这个注解会不会被继承。这两个看你的需求,常用的,必须的就是我前面说的那两个。

元素

这个元素其实就是,这个玩意

image.png

这个咋个说呢?乍一看像是抽象方法,其实也确实像是抽象方法,只是这个比较特殊,是内部自己会去实现的一种特殊接口,这里叫做元素。

先记住这个格式就行了。接下来举个例子。




import org.junit.Test;

@MyAnnot(age = 15)
public class Person {
    private String name;
    private int age;

    @Test
    public void test() {
        Class<Person> personClass = Person.class;
        MyAnnot annotation = personClass.getAnnotation(MyAnnot.class);

        int age = annotation.age();
        String name = annotation.name();

        this.setAge(age);
        this.setName(name);
        System.out.println(this.getName()+":"+this.getAge());

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

image.png

可以看到我直接使用了自己注解,然后去拿到值,给我的那个Person去赋值。 其实在内部当我们这样

@MyAnnot(age = 15) 的时候在java内部是这样处理的,当然这里还是依托一套反射机制实现的。

类似是这样的。
class MyAnno implements MyAnnot{

    @Override
    public int age() {
        return 15;
    }

    @Override
    public String name() {
        return "小明";
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return null;
    }
}

小升级

我们注解的目的是获取那个放在上面的值,或者是相当于一个标记,如果在操作的时候,发现了有这么一个注解,那么就执行相应操作。接下来我们就通过反射去简单地优化一下。

哦,对了别忘了给Person加个toString()方法

public class Main {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("com.huterox.Reflect.annotation.Person");
        Person p = (Person) aClass.newInstance();
        MyAnnot annotation = aClass.getAnnotation(MyAnnot.class);

        Field age = aClass.getDeclaredField("age");
        Field name = aClass.getDeclaredField("name");

        age.setAccessible(true);
        name.setAccessible(true);

        age.set(p,annotation.age());
        name.set(p,annotation.name());

        System.out.println(p);

    }
}

那这个时候,还有个问题,那就是在Spring里面明明打个注解就OK了,为什么这里那么麻烦。这里就不得不说到Spring的机制了,首先Spring没那么聪明,他怎么知道你有没有加注解,所以要判断有木有加注解,那么首先要做的就是扫描全包,这样才能检测出来哪些类做了修改,需要被加载到它自己的对象池里边方便调用,管理。

下面是spring一个生命周期,(spring是很重要的,一方面面试少不了,另一方面里面的思想很值得学习,我们都应该从一个使用者变成一个创造者)

image.png