-
Java注解
Java注解,顾名思义,就是用于标注和解释Java代码的。
在各种Java框架中,我们只需要使用少量的注解即可实现一些强大的功能。在这些看似强大的功能背后,其实现原理却并不复杂。
1. 什么是注解
让我们看看最常见的注解@Override。在我们使用IDE复写接口代码时,这个注解会自动生成出来,被标注在被复写的方法上,用于代表这个方法被重写了。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
可见注解定义的关键字为:@interface
在注解@Override上,还有两个注解:@Target与@Retention。
这里可以看到注解后面有值的存在,让我们再看看@Target的定义:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value(); }
注解内的定义上,与@Override不同的是,内部较之多了一个方法:ElementType[] value();从返回值上可以看到使用了ElementType类型,而注解使用时也使用了该类型。
该定义的实际描述为:value可以接收一个ElementType[]作为描述。对于@Target(ElementType.METHOD),其完整写法应该为:
@Target(value = {ElementType.METHOD})
value即定义的描述(方法)名称,其值为ElementType组成的数组。
但是注解的特性中,接收的数组中只含有一个元素时,只需要直接使用该值。即
@Target(value = ElementType.METHOD)
另外,当只使用了一个描述,且该描述名为value时,可以省略描述名。最终简化为了:
@Target(ElementType.METHOD)
说完注解的用法后,再来看看这两个注解的用途,在定义一个注解时,这两个注解经常会使用到。
@Target:
用于描述注解可标记的位置,描述值来自ElementType内的枚举值。
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 */ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE }
且看最常用的几种:
枚举值 | 实际意义 |
TYPE |
可以标注到类上,即定义为class、interface、enum的类上。 例如@Target、Spring中的@Component |
FIELD |
可以标注到属性声明上。 例如Spring中的@Autowired |
METHOD |
可以标注到方法(函数)声明上。 例如@Override、Spring中的@Autowired |
PARAMETER |
可以标注到方法的参数上。 例如mybatis的@Param,Spring的@RequestBody |
因此@Target用于描述标注在什么位置。
@Retention
用于描述何时有效。描述值来自RetentionPolicy 枚举值。
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
三个范围分别为:SOURCE:源代码中有效、CLASS:编译后class文件内有效,RUNTIME:以及运行时VM有效。
这个值也与通过注解实现自动装配等功能提供了基础。
通常来说,仅作为描述使用的注解,例如@Override,仅在源代码有效即可,因为仅用于编码人员观看,在运行期不需要用到。
对于Spring中品种繁多的各类注解,其标注的使用范围均为:RetentionPolicy.RUNTIME。
2. 如何获取注解中描述的值?
获取描述的值非常简单,我们先自己定义一个注解@ForClass,并为他定义两个描述值:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ForClass { String value() default ""; String[] packages() default {}; }
default代表使用默认值,这样描述值就是非必填项,获取时也会取到默认值,default的存在令许多Spring中的描述值不必填写。
我们再定义一个类,在类上使用该注解描述这个类:
@ForClass("UseAnnotationClass") public class UseAnnotationClass { }
最后,在main函数中开始获取UserAnnotationClass的描述值:
public static void main(String[] args) { // 获取标注在UseAnnotationClass类上的注解 ForClass forClass = UseAnnotationClass.class.getAnnotation(ForClass.class); // 获取其中value的值 String value = forClass.value(); }
如此只需做简单的几步操作便可获取到value的描述值。packages亦是同理。
如今再来谈谈Spring中的注解,以@Component为例。
在Spring中,我们通常要给Spring提供项目路径,并将Spring组件置于子包中。
在main类上使用@SpringBootApplication其实就能获取到项目的父路径。因此SpringBoot中通常不需要告诉Spring。
有了这个路径后,递归扫描子包,查看哪些类上包含@Component注解,由此获取到所有的使用到@Component注解的类。
获取到这些类后,Spring便知道我们想把这个类放入IOC容器中,于是将其放入IOC容器中。
对注解的使用不过是Spring的冰山一角,以后的文章中会从jdk本身的功能上,逐渐向大家解开Spring的各色看似神奇的操作。
原文:https://www.cnblogs.com/mrjanon/p/java-annotation.html