缘起
在前面的文章中,我们使用了注解@ConditionalOnClass,在此注解上面有其它的注解,在自定义注解的时候,如果不明白上面的这些配置,那可能就会搞错这个注解的使用。
一、元注解
1.1 元注解的源码结构
从java.lang.annotation包截图看,一共定义了6个注解:
(1)@Document
(2)@Target
(3)@Retention
(4)@Inherited
(5)@Native
(6)@Repeatable
其中前4个是元注解
1.2 元注解的定义
所谓元注解就是注解的注解,元注解负责注解自定义注解,你可以看到许多自定义的注解上面都有这些元注解:
例如Spring Boot的注解@ConditionalOnClass:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
1.3 元注解的用途
1.3.1 @Target:注解的使用范围
标识注解的使用范围,可以赋值为ElementType类型,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
}
1.3.2 @Documented:是否要被写入javadoc
@Document注解用途主要是标识类型是否要被收入javadoc
1.3.3 @Inherited:是否可以被子类继承
说明子类可以继承父类中的该注解
1.3.4 @Retention:注解保留的位置
注解的保留位置,可以赋值 RetentionPolicy类型,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
}
(1)RetentionPolicy.SOURCE:表明注解会被编译器丢弃,字节码中不会带有注解信息
(2)RetentionPolicy.CLASS:表明注解会被写入字节码文件,且是@Retention的默认值
(3)RetentionPolicy.RUNTIME:表明注解会被写入字节码文件,并且能够被JVM 在运行时获取到,可以通过反射的方式解析到
二、举例说明
2.1 @ConditionalOnClass
(1)@Target:说明该注解可以使用在TYPE(接口、类、枚举、注解)和METHOD(方法)上。
(2)@Retention(RetentionPolicy.RUNTIME):表明注解会被写入字节码文件,并且能够被JVM 在运行时获取到,可以通过反射的方式解析到。
(3)Documented:要写入到javadoc中。
2.2 @RequestParam
(1)@Target(ElementType.PARAMETER):只能在参数上进行使用。
(2)@Retention(RetentionPolicy.RUNTIME):表明注解会被写入字节码文件,并且能够被JVM 在运行时获取到,可以通过反射的方式解析到。
(3)Documented:要写入到javadoc中。
RequestParam可以看下具体的例子:
/**
* 接收普通请求参数
* http://localhost:8080/test16?name=wuqian
* url参数中的name必须要和@RequestParam("name")一致
* @return
*/
@RequestMapping("test16")
public ModelAndView test16(@RequestParam("name")String name){
ModelAndView mv = new ModelAndView();
mv.setViewName("hello2");
mv.addObject("msg", "接收普通的请求参数:" + name);
return mv;
}
如果将此注解参数强制使用在方法上就会编译错误:
错误信息:'@RequestParam' not applicable to method.(“@RequestParam”不适用于方法。)
2.3 @Bean
(1)@Target:说明该注解可以使用在METHOD(方法)和ANNOTATION_TYPE(注解类型)上。
(2)@Retention(RetentionPolicy.RUNTIME):表明注解会被写入字节码文件,并且能够被JVM 在运行时获取到,可以通过反射的方式解析到。
(3)Documented:要写入到javadoc中。
对于@Target可以在方法上使用,想必大家这个没有什么疑惑,对于ANNOTATION_TYPE(注解类型)的话,这个就是注解类型,什么意思呢?
就是这个注解使用被其它注解进行组合注解,也就是说可以将@Bean这个注解用在自定义注解的上面。
这里举个栗子,定义了两个注解MyAnno和MyAnno1:
对于@MyAnno配置了@Target({ElementType.ANNOTATION_TYPE}),所以可以讲此注解定义在MyAnno1上。
当删除@MyAnno 的配置ElementType.ANNOTATION_TYPE的时候,那么就会报错:
我们发现元注解@Document、@Target、@Retention、@Inherited上都配置了@Target(ElementType.ANNOTATION_TYPE)。
所以对于配置了@Target(ElementType.ANNOTATION_TYPE)还有一种解释:
由@Target(ElementType.ANNOTATION_TYPE)注释的每个注释称为Meta-annotation。这意味着,您可以定义自己的自定义注释,这些注释是许多注释的合并,组合成一个注释以创建composed annotations。
特别说明:@Target(ElementType.TYPE)也是可以使用在注解上的。
2.4 @Data
Lombok的注解@Data:
(1)@Target:说明该注解可以使用在TYPE(接口、类、枚举、注解)上。
(2)@Retention(RetentionPolicy.SOURCE):表明注解会被编译器丢弃,字节码中不会带有注解信息。
所以@Data注解的类在被编译之后,此类上面并不会保留@Data的注解。因此通过反射技术无法获取到@Data这个注解。
三、元注解class文件
我们通过反编译可以看出来最终是一个接口:
我们定义注解了:
使用javap反编译一下: