写点什么

Java Core「5」自定义注解编程

作者:Samson
  • 2022 年 5 月 25 日
  • 本文字数:2580 字

    阅读完需:约 8 分钟

Java Core「5」自定义注解编程

01-元注解

定义:可以加在注解上的注解


@Document用于制作文档;


@Target描述注解的作用范围,即限制注解可以加在什么位置,例如加在属性、方法、包上等;


  • TYPE,只能在类、接口和枚举类型上使用

  • FIELD,只能在属性上使用

  • METHOD,只能在方法上使用


@Retention用于定义注解的保留策略;


  • SOURCE,表示注解只保留在*.java 中,编译成*.class 文件后丢弃;

  • CLASS,表示注解会保留到*.class 文件中,当类加载到内存中时,注解丢失;通过 javap 查看 class 文件的字节码,可以发现,此种类型的注解表示为 RuntimeInvisibleAnnotations :


public void classPolicy();  flags: ACC_PUBLIC  Code:    stack=0, locals=1, args_size=1      0: return    LineNumberTable:    line 11: 0  RuntimeInvisibleAnnotations:    0: #11()
复制代码


RUNTIME,表示注解会保留到运行时,只有这种类型的注解才能够被反射机制读取到;通过 javap 查看 class 文件的字节码,可以发现,此种类型的注解表示为 RuntimeVisibleAnnotations :


public void runtimePolicy();  flags: ACC_PUBLIC  Code:    stack=0, locals=1, args_size=1      0: return    LineNumberTable:      line 15: 0  RuntimeVisibleAnnotations:    0: #14()
复制代码

02-注解中属性的类型

定义注解时,可用的属性类型如下:


  • 8 中基本类型,byte/short/int/long/float/double/boolean/char

  • String

  • Enum

  • Class

  • 注解

  • 上述类型的一维数组


当注解中的属性只有一个,并且名为 value 时,使用注解时,可以不指定属性名,默认为 value 赋值;当注解中的属性有多个,使用注解时,不论是否为 value 赋值,都必须指定属性名,表明一一对应关系;使用注解时,若属性为一维数组,且值只有一个,则可以省略{}

03-注解与反射接口

定义注解之后,如何读取注解中的内容呢?


java.lang.reflect 包中 AnnotatedElement 接口定义了与注解相关的方法:


/** 判断是否具备特定的注解 */boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);/** 返回特定类型的注解(若有),否则 null */<T extends Annotation> T getAnnotation(Class<T> annotationClass);/** 获得所有的注解 */Annotation[] getAnnotations();/** 获得特定类型的注解 */<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass);/** 获得直接出现的注解 */default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass);/** 获得直接出现的、特定类型的注解 */<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass);Annotation[] getDeclaredAnnotations();
复制代码


java.lang.Class、java.lang.reflect.Method、java.lang.reflect.Field 等都实现了 AnnotatedElement 接口。

04-自定义注解编程

通过上面的介绍,可以发现,自定义注解编程,基本上分为三个步骤:1)使用元注解定义自定义注解;2)在编程中使用自定义注解;3)编写读取注解并处理的逻辑。

04.1-注解编程基本原理

  • 注解定义,@interface

  • 注解使用,@MyAnnotation

  • 注解读取

04.2-注解编程示例

根据注解自动生成 SQL 查询语句


定义注解


@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Table {    String value();}
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Column { String value();}
复制代码


使用注解


@Table("student")public class StudentInfo {
@Column("STUDENT_NAME") private String name; @Column("STUDENT_AGE") private int age; @Column("STUDENT_SEX") private int sex;
// ...getter // ...setter}
复制代码


读取注解


@Testpublic void testCase1() {
StringBuilder stringBuilder = new StringBuilder();
StudentInfo studentInfo = new StudentInfo(); studentInfo.setName("张三"); studentInfo.setAge(18); studentInfo.setSex(1);
Class<?> clazz = studentInfo.getClass(); if (!clazz.isAnnotationPresent(Table.class)) { System.err.println("StudentInfo.class上不包含@Table注解"); return ; }
Table table = clazz.getAnnotation(Table.class); String tableName = table.value(); stringBuilder.append(String.format("SELECT * FROM %s WHERE 1=1", tableName));
for (Field field : clazz.getDeclaredFields()) { if (!field.isAnnotationPresent(Column.class)) { System.out.println(String.format("%s is not annotated by @Column", field.getName())); continue; } Column column = field.getAnnotation(Column.class); String columnName = column.value(); try { Method getter = clazz.getMethod(String.format("get%s", TableColumnTest.captureName(field.getName()))); Object value = getter.invoke(studentInfo);
if (value instanceof String) { stringBuilder.append(String.format(" AND %s = '%s'", columnName, value)); } else if (value instanceof Integer) { stringBuilder.append(String.format(" AND %s = %s", columnName, value)); } else { System.err.println("不支持的变量类型"); }
} catch (NoSuchMethodException nse) {
} catch (IllegalAccessException | InvocationTargetException e) {
} }
System.out.println(stringBuilder.toString());}
public static String captureName(String str) { char[] chars = str.toCharArray(); chars[0] -= 32; return String.valueOf(chars);}
复制代码


输出结果为:


SELECT * FROM student WHERE 1=1 AND STUDENT_NAME = '张三' AND STUDENT_AGE = 18 AND STUDENT_SEX = 1


[1] java注解的本质以及注解的底层实现原理

[2] Java注解处理器


历史文章

Java Core「4」java.util.concurrent 包简介

Java Core「3」volatile 关键字

Java Core「2」synchronized 关键字

Java Core「1」JUC- 线程基础

发布于: 刚刚阅读数: 4
用户头像

Samson

关注

还未添加个人签名 2019.07.22 加入

还未添加个人简介

评论

发布
暂无评论
Java Core「5」自定义注解编程_学习笔记_Samson_InfoQ写作社区