开发者必会的技能 -- 自定义注解
注解对于开发者而言是再熟悉不过的. 注解可以大幅度提升开发的效率,减少重复代码.但是在程序员多种多样、纷繁复杂的业务需求的背景下,Spring以及Java提供的注解显得力不从心,所以自己创建自定义注解成为程序员的必备技能!
基本知识
在Java中,注解分为两种,元注解和自定义注解.
一般理解: 元注解理解为描述注解的注解. 在java中仅仅只有极少的元注解(4个).其余皆为自定义注解,包括我们常用的框架提供的注解,其本质仍然是自定义注解.
元注解的相关知识(@Target、@Retention、@Documented、@Inherited)
@Target
作用: 描述被修饰的注解可以使用的地点
取值范围: 定义在ElementType枚举中
public enum ElementType {
TYPE, // 类、接口、枚举类
FIELD, // 成员变量(包括:枚举常量)
METHOD, // 成员方法
PARAMETER, // 方法参数
CONSTRUCTOR, // 构造方法
LOCAL_VARIABLE, // 局部变量
ANNOTATION_TYPE, // 注解类
PACKAGE, // 可用于修饰:包
TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
}
@Retention
作用: 描述被修饰的注解可以保留的时间范围
取值范围: 定义在RetentionPolicy枚举中
public enum RetentionPolicy {
SOURCE, // 仅源文件阶段保留,在编译器进行编译时它将被丢弃忽视
CLASS, // 编译期保留,并不会被加载到 JVM 中.默认值
RUNTIME // 运行期保留,会被加载进入到 JVM.可通过反射去获取注解信息(最常用)
}
@Documented
作用: 描述在使用 javadoc 工具为类生成帮助文档时是否要保留被其修饰的注解的信息。
取值范围: 定义在RetentionPolicy枚举中
@Inherited
作用: 使被其修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)
创建自定义注解的详细步骤
1. 创建注解
以@interface关键字开头,并在内部写的属性.
如下所示:
@Target({ElementType.FIELD})
@Retention({RetentionPolicy.RUNTIME})
@Documented
public @interface MyAnno {
String value() default "";
public int id() default -1;
Sex sex(); // Sex是一个自定义枚举类
}
说明:
- Annotation 型定义为@interface, 所有的Annotation 会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
- 参数成员只能用public 或默认(default) 这两个访问权修饰
- 参数成员只能用基本类型byte、short、char、int、long、float、double、boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
- 为啥明明叫属性,却带有括号?(历史遗留问题,不需要过多关注)
- 注解中的每个属性必须有值.要么有默认值(default值),要么在使用注解时,对该属性进行赋值.不允许出现既没有默认值也没有赋值的情况.
- 注解的使用必须清晰明了.只要清晰明了,顺序和是否有值无所谓.
2. 注解的功能实现
自定义注解的功能实现常常和反射以及AOP绑定在一起出现.
例1: 反射
说明: 与注解功能实现的两个常用的方法
- isAnnotationPresent(注解名.class): 判断当前对象上有无对应的注解
- getAnnotation(注解名.class): 获取该对象上的对应注解信息并封装成对应的注解对象.
FruitName.java
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 水果名称注解
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
FruitColor.java
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 水果颜色注解
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitColor {
/**
* 颜色属性
*/
Color fruitColor() default Color.GREEN;
}
FruitProvider.java
复制代码
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 水果供应者注解
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供应商编号
*/
public int id() default -1;
/**
* 供应商名称
*/
public String name() default "";
/**
* 供应商地址
*/
public String address() default "";
}
FruitInfoUtil.java
import java.lang.reflect.Field;
/**
* 注解处理器
*/
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);
}
}
}
}
Apple.java
import test.FruitColor.Color;
/**
* 注解使用
*/
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
@FruitProvider(id=1,name="红苹果集团",address="华夏路天子脚下1号")
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("水果的名字是:苹果");
}
}
FruitRun.java
/**
* 输出结果
*/
public class FruitRun {
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
运行结果:
水果名称:Apple
水果颜色:RED
供应商编号:1 供应商名称:红苹果集团 供应商地址:华夏路天子脚下一号
例2: AOP