当前位置: 首页 > 后端技术 > Node.js

Springboot以方便着称,那么你知道它简洁的核心——java注解的原理吗?

时间:2023-04-04 00:07:42 Node.js

java注解用法一个简单的注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceTest1{Stringname()default"123";String[]name2()default{"123","342432","321321321"};}//公共类Test2{@Test1(name="2313122313",name2={"312231321","4342","65456543"})publicstaticvoidsayHello(){System.out.println("123");}publicstaticvoidmain(String[]args){sayHello();}}有几个地方需要注意。首先,@interface被编译器识别,说明该类型是一个继承java.lang.annotation.Annotation接口的子接口,称为注解。@Target和@Retention都是jdk中定义的注解。前者主要表示注解可以用来修饰什么,后者主要决定了注解保留在什么环境下,也就是可以在哪些环境下运行,是编译时、运行时还是写代码的时候。publicenumElementType{TYPE,//类、接口(包括注解类型)或枚举声明FIELD,//字段声明(包括枚举常量)METHOD,//方法声明PARAMETER,//形参声明CONSTRUCTOR,//构造函数声明LOCAL_VARIABLE,//局部变量声明ANNOTATION_TYPE,//注解类型声明PACKAGE,//封装声明TYPE_PARAMETER,//类型参数声明TYPE_USE//类型的使用}publicenumRetentionPolicy{SOURCE,CLASS,RUNTIME}以上两个枚举类型是注解的保留时间(编码期、编译期、运行时)和作用范围,这也是注解的核心属性。但是这样的接口是没有意义的。让注解有意义注意,刚才我们提到@interface表示对象是继承自Annotation的子接口,所以我们首先要看接口的源码publicinterfaceAnnotation{booleanequals(Objectobj);inthashCode();字符串toString();类annotationType();}前三个方法比较基础,但是第四个函数到底表达了什么?刚才说@interface表示对象继承了Annotation的子接口,并没有说是实现了Annotation接口的类。从这里可以看出,如果实现类,@interface不能自动识别如何实现第四个A方法,所以只能是一个接口。从extends关键字也可以看出,@interface修饰的对象是Annotation的子接口。只是和一般接口不同的是,对于注解类型,我们可以在定义函数的时候设置默认值,通过default关键字实现。而且,这样的接口没有实现类。@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceTest1{字符串名称()默认“123”;String[]name2()default{"123","342432","321321321"};}看了几篇博客,都说注解是元数据,可以理解为正常运行的配置文件程序,并可以定义程序运行的顺序、配置等。然后我们看看剩下的元注解的实现@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public@interfaceDocumented{}@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public@interfaceInherited{}@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public@interfaceRetention{RetentionPolicyvalue();}可以看到@Document注解的源码是完全一样的@Inherited注解的源码,但是我们知道这两个注解的语义是不一样的。编译器怎么知道两个不同注解的定义呢?这是因为对于jdk的内置注解,编译器有一套对应的方法,即编译器自己在扫描注解时,会根据名字对内置注解进行识别和行为判断。所以编译器无法识别自定义注解,所以自定义注解一般应用在运行时,即RetentionPolicy.RUNTIME,在编译时编译成字节码。在运行时,我们需要使用反射技术来识别注解及其携带的信息,然后进行相应的处理。这就带来了一个问题。由于使用了反射技术,注解带来的问题只能在运行时发现。同时,这也给调试带来困难。如何在运行时找到并分析它?我们来看下面这个简单的例子//Test1.java定义注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceTest1{Stringname()default"123";String[]name2()default{"123","342432","321321321"};}//Test2.java定义注解修饰的方法,一种修改注解的默认值,一种不修改默认值注释publicclassTest2{@Test1(name="2313122313",name2={"312231321","4342","65456543"})publicstaticvoidchange(){}@Test1publicstaticvoidnotChange(){}}//Test3.java使用反射查找注解修饰的方法publicclassTest3{publicstaticvoidparsing(Objectobj){if(Objects.isNull(obj))return;方法[]方法=obj.getClass().getDeclaredMethods();对于(方法方法:方法){如果(method.isAnnotationPresent(Test1.class)){Test1test1=method.getAnnotation(Test1.class);System.out.println("methodName="+method.getName());System.out.println("name="+test1.name());对于(斯特里ngs:test1.name2()){System.out.println("name2="+s);}}}}}//Test4.java调用publicclassTest4{publicstaticvoidmain(String[]args){Test2test2=newTest2();测试3.解析(测试2);}}运行结果如下methodName=notChangename=123name2=123name2=342432name2=321321321methodName=changename=2313122313name2=312231321name2=435245jd=5实现的反射方法提供了注解相关的方法,方便开发者查找修改的对象通过注解,利用当前注解的值做一些处理,让代码自动运行(即通过简单的配置,代码可以正常运行)本质上,我个人认为注解是可以替代的,但是注释是设计思维的改进,使实现更容易。注解带来的好处不同于注解。注解的内容只存在于编码阶段,编译器会忽略所有的注解。相反,编译器根据注解的保留标志决定是否将注解编译成字节码。注解给程序的灵活性带来了巨大的改变,这也是为什么springboot的出现打破了日常java程序员被xml支配的恐惧,甚至个人认为这间接导致了java程序员的内卷(springboot直接原因)。更复杂的是,知道为什么的程序员越来越少。希望自己能坚持下去,把jdk底层搞懂。炸鸡辣子鸡原创文章,转载请注明出处