「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」
前言
这个东西其实和我们的python的装饰器也就是“语法糖豆”不像,只是我习惯了,所以我就叫它小糖豆。那么这个玩意其实更像是一个特殊接口,特殊标记。不过这个东西也贼有用,像里面原来的Spring框架,里面有大量的注解来代替我们配置文件。后面如果要自己写一个小框架这个东西绝对是少不了的。 包括我们常用的 javadoc的文档注释 这里插一嘴,我们在idea里面可以设置一下我们的注释模板 在我们的setting里面
那么闲话就说到这里,那么我们注解的实现的话还是要用到反射的。
内置注解
这个其实就是官方的内置的注解。 这几个也是老朋友了,第一个是 @Override @Deprectated @SuppressWarnings
第一个不用说,第二个标注过时,第三个用来屏蔽警告的。
@SuppressWarnings("all")
哈哈,不讲武德~
定义注解
首先这个注解的格式很简单其实就相当于声明。其实这玩意就是一个特殊的接口。
格式
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnot {
int age();
String name() default "狗蛋";
}
这里分两个部分,第一个部分其实是啥,是那个我们的对我们自己定义的注解的描述。 也叫作原注解,这里简单说明一下。
元注解
首先是第一个,这个是告诉你作用域,你的这个注解是作用在哪里的?
这个是一个枚举类型的,可以看到不少作用域。
这里有几个小细节注意,一个是如果我们想要作用多个作用域
@Target({ElementType.TYPE,其他你想的位置})
直接这样。
还有一个是这个value,这个呢其实是个特殊属性,如果你的接口里面只需要一个值,那么你就可以直接那啥用value,这样使用的时候,我们就不需要制定元素的值了,这个接下来会说。
@Retention(RetentionPolicy.RUNTIME)
这个是运行的一个阶段,也就是在那个java代码的三个阶段嘛,我们一般时在运行时。
之后那两个注解,一个是说,javadoc的时候,要不要显示那个类用了这个注解,还一个是说,如果有子类继承,要不要继承注解,也就是这个注解会不会被继承。这两个看你的需求,常用的,必须的就是我前面说的那两个。
元素
这个元素其实就是,这个玩意
这个咋个说呢?乍一看像是抽象方法,其实也确实像是抽象方法,只是这个比较特殊,是内部自己会去实现的一种特殊接口,这里叫做元素。
先记住这个格式就行了。接下来举个例子。
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;
}
}
可以看到我直接使用了自己注解,然后去拿到值,给我的那个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是很重要的,一方面面试少不了,另一方面里面的思想很值得学习,我们都应该从一个使用者变成一个创造者)