一. 注解基本介绍
1. 什么是注解?
什么是注解?严谨的来说,注解提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。注解包含在 java.lang.annotation 包中。
具体定义如下:
注解 (Annotation),也叫元数据。一种代码级别的说明。它是 JDK1.5 及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
——摘自百度百科
2.注解的作用
- 能够读懂别人写的代码(尤其是框架相关的代码);
- 实现替代配置文件的功能。比如可能原本需要很多配置文件以及很多逻辑才能实现的内容,如果使用合理的注解,就可以使用一个或多个注解来实现相同的功能。这样就使得代码更加清晰和整洁;
- 编译时进行格式检查。
二. 元注解
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。或者可以理解为:元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。
基本的元标签有 @Retention, @Documented, @Target, @Inherited 四种(后来到了 Java 8 又加入了 @Repeatable)。
2.1 @Retention
@Retention 定义了该注解的生命周期。当 @Retention 应用到一个注解上的时候,作用就是说明这个注解的存活时间。
RetentionPolicy.SOURCE: 注解只在源码阶段保留,在编译器完整编译之后,它将被丢弃忽视;
例:@Override, @SuppressWarnings
RetentionPolicy.CLASS: 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中;
RetentionPolicy.RUNTIME: 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们;笔者接触到大部分的注解都是 RUNTIME 的生命周期。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
String value() default "";
}
2.2 @Target
@Target 表示该注解用于什么地方,可以理解为:当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。可以使用的 ElementType 参数:
ElementType.CONSTRUCTOR: 对构造方法进行注解;
ElementType.ANNOTATION_TYPE: 对注解进行注解;
ElementType.FIELD: 对属性、成员变量、成员对象(包括 enum 实例)进行注解;
ElementType.LOCAL_VARIABLE: 对局部变量进行注解;
ElementType.METHOD: 对方法进行注解;
ElementType.PACKAGE: 对包进行注解;
ElementType.PARAMETER: 对描述参数进行注解;
ElementType.TYPE: 对类、接口、枚举进行注解;
2.3 @Documented
@Documented 是一个简单的标记注解,表示是否将注解信息添加在 Java 文档,即 Javadoc 中。
2.4 @Inherited
Inherited 是指继承,@Inherited 定义了一个注释与子类的关系。如果一个超类带有 @Inherited 注解,那么对于该超类,它的子类如果没有被任何注解应用的话,那么这个子类就继承了超类的注解。
三. 自定义注解
自定义注解类编写的规则:
注解类型定义为 @interface,所有的注解会自动继承 java.lang.Annotation 这一接口,而且不能再去继承其他的类或接口;
参数成员只能用 public 或 default 两个关键字修饰;
参数成员只能用基本类型:byte, short, char, int, long, float, double, boolean,以及 String, Enum, Class, Annotations 等数据类型,以及这些类型的数组;
要获取类方法和字段的注解信息,必须通过 Java 的反射技术;
注解也可以不定义成员变量,但这样的注解没有什么卵用;
自定义注解需要使用元注解进行编写;
/**
* 水果名称注解
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
/**
* 水果颜色注解
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitColor {
/**
* 颜色枚举
*/
public enum Color{ BLUE,RED,GREEN};
/**
* 颜色属性
*/
Color fruitColor() default Color.GREEN;
}
/**
* 水果供应者注解
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供应商编号
*/
public int id() default -1;
/**
* 供应商名称
*/
public String name() default "";
/**
* 供应商地址
*/
public String address() default "";
}
/**
* 注解处理器
*/
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz){
String strFruitName=" 水果名称:";
String strFruitColor=" 水果颜色:";
String strFruitProvicer="供应商信息:";
Field[] fields = clazz.getDeclaredFields();
for(Field field :fields){
if(field.isAnnotationPresent(FruitName.class)){
FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
strFruitName=strFruitName+fruitName.value();
System.out.println(strFruitName);
}
else if(field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
}
else if(field.isAnnotationPresent(FruitProvider.class)){
FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
}
/**
* 注解使用
*/
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor = FruitColor.Color.RED)
private String appleColor;
@FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
private String appleProvider;
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
public String getAppleProvider() {
return appleProvider;
}
public void displayName(){
System.out.println("水果的名字是:苹果");
}
}