前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一篇文章,全面掌握Java自定义注解(Annontation)

一篇文章,全面掌握Java自定义注解(Annontation)

作者头像
程序新视界
发布2022-05-09 19:51:25
1.8K0
发布2022-05-09 19:51:25
举报
文章被收录于专栏:丑胖侠丑胖侠

如果此刻不方便阅读,可收藏或关注微信公众号《程序新视界》回复“注解”获得PDF版本。

注解简介

注解(Annontation),Java5引入的新特性,位于java.lang.annotation包中。提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。是一种说明、配置、描述性的信息,与具体业务无关,也不会影响正常的业务逻辑。但我们可以用反射机制来进行校验、赋值等操作。

常见的注解:@Override,@author,@param,@Deprecated,@SuppressWarnings。

注解的常见用途:

  • 生成文档的注解,如@author,@param。
  • 跟踪代码依赖性,实现替代配置文件功能,如spring mvc的注解。
  • 编译时进行格式检查,如@override。
  • 编译时进行代码生成补全,如lombok插件的@Data。

注解基础知识

注解的定义

  • 注解的定义通过@interface表示,所有的注解会自动继承java.lang.Annotation接口,且不能再继承别的类或是接口。
  • 注解的成员参数只能用public或默认(default) 访问权修饰来进行修饰。
  • 成员参数只能使用八种基本类型(byte、short、char、int、long、float、double、boolean)和String、Enum、Class、annotations等数据类型,及其数组。
  • 获取类方法和字段的注解信息,只能通过Java的反射技术来获取 Annotation 对象。
  • 注解可以没有定义成员,只做标识。

元注解

元注解是专门用来注解其他注解的注解,听起来有些绕口,实际上就是专门为自定义注解提供的注解。java.lang.annotation提供了四种元注解:

  • @Documented – 注解是否将包含在JavaDoc中
  • @Retention – 什么时候使用该注解
  • @Target – 注解用于什么地方
  • @Inherited – 是否允许子类继承该注解
  • @Repeatable - 是否可重复注解,jdk1.8引入
注解的生命周期

通过@Retention定义注解的生命周期,格式如下:

代码语言:javascript
复制
@Retention(RetentionPolicy.SOURCE)

其中RetentionPolicy的不同策略对应的生命周期如下:

  • RetentionPolicy.SOURCE : 仅存在于源代码中,编译阶段会被丢弃,不会包含于class字节码文件中。@Override, @SuppressWarnings都属于这类注解。
  • RetentionPolicy.CLASS : 默认策略,在class字节码文件中存在,在类加载的时被丢弃,运行时无法获取到。
  • RetentionPolicy.RUNTIME : 始终不会丢弃,可以使用反射获得该注解的信息。自定义的注解最常用的使用方式。
注解的作用目标

通过@Target定义注解作用的目标,比如作用于类、属性、或方法等,默认可用于任何地方。格式如下:

代码语言:javascript
复制
@Target(ElementType.TYPE)

对应ElementType参数值适用范围如下:

  • ElementType.TYPE: 类、接口、注解、enum
  • ElementType.CONSTRUCTOR: 构造函数
  • ElementType.FIELD: 成员变量、对象、属性、枚举的常量
  • ElementType.LOCAL_VARIABLE: 局部变量
  • ElementType.METHOD: 方法
  • ElementType.PACKAGE: 包
  • ElementType.PARAMETER: 参数
  • ElementType.ANNOTATION_TYPE): 注解
  • ElementType.TYPE_PARAMETER:类型参数,表示这个注解可以用在 Type的声明式前,jdk1.8引入。
  • ElementType.TYPE_USE:类型的注解,表示这个注解可以用在所有使用Type的地方(如:泛型,类型转换等),jdk1.8引入。
Documented

@Documented,表示是否将此注解的相关信息添加到javadoc文档中。

Inherited

@Inherited,定义该注解和子类的关系,使用此注解声明出来的自定义注解,在使用在类上面时,子类会自动继承此注解,否则,子类不会继承此注解。注意,使用Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。

实例

下面通过一个实例来演示注解的使用:通过注解进行赋值和通过注解进行校验。

自定义注解

这里定义两个注解,一个用来赋值,一个用来校验。

代码语言:javascript
复制
/**
 * 性别赋值
 * @author zzs
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface InitSex {

	/**
	 * sex enum
	 * @author zzs
	 */
	enum SEX_TYPE {MAN, WOMAN}

	SEX_TYPE sex() default SEX_TYPE.MAN;
}
代码语言:javascript
复制
/**
 * 年龄校验
 * @author zzs
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface ValidateAge {

	/**
	 * 最小值
	 */
	int min() default 18;

	/**
	 * 最大值
	 */
	int max() default 99;

	/**
	 * 默认值
	 */
	int value() default 20;
}
定义数据模型

这里用User类来表示具体待处理的数据对象。

代码语言:javascript
复制
/**
 * user
 *
 * @author zzs
 */
public class User {

	private String username;

	@ValidateAge(min = 20, max = 35, value = 22)
	private int age;

	@InitSex(sex = InitSex.SEX_TYPE.MAN)
	private String sex;

    // 省略getter/setter方法
}
测试调用

具体测试调用的过程,参考代码中的注解,其中initUser方法来演示通过反射给属性赋值,checkUser方法通过反射拿到当前属性的值进行对比校验。

代码语言:javascript
复制
import java.lang.reflect.Field;

/**
 * @author zzs
 */
public class TestInitParam {

	public static void main(String[] args) throws IllegalAccessException {

		User user = new User();
		initUser(user);

		// 年龄为0,校验为通过情况
		boolean checkResult = checkUser(user);
		printResult(checkResult);

		// 重新设置年龄,校验通过情况
		user.setAge(22);
		checkResult = checkUser(user);
		printResult(checkResult);
	}

	static void initUser(User user) throws IllegalAccessException {

		// 获取User类中所有的属性(getFields无法获得private属性)
		Field[] fields = User.class.getDeclaredFields();

		// 遍历所有属性
		for (Field field : fields) {
			// 如果属性上有此注解,则进行赋值操作
			if (field.isAnnotationPresent(InitSex.class)) {
				InitSex init = field.getAnnotation(InitSex.class);
				field.setAccessible(true);
				// 设置属性的性别值
				field.set(user, init.sex().toString());
				System.out.println("完成属性值的修改,修改值为:" + init.sex().toString());
			}
		}
	}

	static boolean checkUser(User user) throws IllegalAccessException {

		// 获取User类中所有的属性(getFields无法获得private属性)
		Field[] fields = User.class.getDeclaredFields();

		boolean result = true;
		// 遍历所有属性
		for (Field field : fields) {
			// 如果属性上有此注解,则进行赋值操作
			if (field.isAnnotationPresent(ValidateAge.class)) {
				ValidateAge validateAge = field.getAnnotation(ValidateAge.class);
				field.setAccessible(true);
				int age = (int) field.get(user);
				if (age < validateAge.min() || age > validateAge.max()) {
					result = false;
					System.out.println("年龄值不符合条件");
				}
			}
		}

		return result;
	}

	static void printResult(boolean checkResult) {
		if (checkResult) {
			System.out.println("校验通过");
		} else {
			System.out.println("校验未通过");
		}
	}
}

打印日志:

代码语言:javascript
复制
完成属性值的修改,修改值为:MAN
年龄值不符合条件
校验未通过
校验通过
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 注解简介
    • 注解的常见用途:
    • 注解基础知识
      • 注解的定义
        • 元注解
          • 注解的生命周期
          • 注解的作用目标
          • Documented
          • Inherited
          • 自定义注解
          • 定义数据模型
          • 测试调用
      • 实例
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档