简单的注解剖析
周边有许多同事只会使用注解,并不了解注解的原理。于是随手写一个小Demo,普及下注解的使用原理,顺便加深自己的理解。如有错误,欢迎大牛指正。编写的代码会放到附件里,main函数在AnnotationTest.java文件下。
- 如何创建一个注解类
- 注解类基本样式
/** * * @author qpf * 此注解用于对表名的设置 * */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Table { String value(); }
- 注解类的元素解析
元注解:
总共有四个元注解,分别是:@Target,@Retention,@Documented,@Inherited,属于jdk注解,作用如下:
@Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括:
ElemenetType.CONSTRUCTOR----------------------------构造器声明
ElemenetType.FIELD --------------------------------------域声明(包括 enum 实例)
ElemenetType.LOCAL_VARIABLE------------------------- 局部变量声明
ElemenetType.METHOD ----------------------------------方法声明
ElemenetType.PACKAGE --------------------------------- 包声明
ElemenetType.PARAMETER ------------------------------参数声明
ElemenetType.TYPE--------------------------------------- 类,接口(包括注解类型)或enum声明
@Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:
RetentionPolicy.SOURCE ---------------------------------注解将被编译器丢弃
RetentionPolicy.CLASS -----------------------------------注解在class文件中可用,但会被VM丢弃
RetentionPolicy.RUNTIME -------将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
@Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
@Inherited 允许子类继承父类中的注解。
|
注解使用方法:
@Table("StudentInfo") public class StudentInfo extends BaseTable { //学生姓名 @Column("name") private String name; //性别 @Column("sex") private String sex; }
注意:
@Target
设置了注解放在什么地方,设置为
ElemenetType.FIELD,
则需要将注解放置在属性的上方。设置为
ElemenetType.TYPE,
则需要放置在class上方。放错位置,编译器会报错。
原理:
把
public
@interface
Table
{String value();}
和
@Table
(
"StudentInfo"
)
放在一起看,其实注解就是返回你所设置的指定类型的值。其实就是在调用注解的value()方法,会返回String类型的值,值为注解中设置的
“
StudentInfo
”
。后面例子中会有详细介绍。还需注意一点,注解中返回的类型是有限定的,且方法一定要是无参的,不懂的可自行百度。
- 利用注解实现小Demo
- 需求内容
1、
有一张学生信息表,字段包括姓名,年级,班级,教师名,性别等;
2、利用注解,对表实例的每个字段进行检查,并打印出查询sql
- 代码结构
annotation:存放创建的注解。
bean:存放数据库表所用到的javaBean
test:main函数,用于测试并打印查询sql;
- 注解类
/** * * @author qpf * 此注解用于对表字段的设置 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Column { String value(); }
/** * * @author qpf * 此注解用于对表名的设置 * */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Table { String value(); }
- 学生信息表的javabean
下面的代码删去了get和set方法,附件包里的代码是完整的,可下载后对比一下!可以自己写一写,注意观察注解的放置位置!
/** * * @author qpf * Table标签里为表名 * Column标签里为数据库字段名 */ @Table("StudentInfo") public class StudentInfo extends BaseTable { //学生姓名 @Column("name") private String name; //性别 @Column("sex") private String sex; //年级 @Column("grade") private String grade; //班级 @Column("class_num") private String classNum; //班主任 @Column("teacher") private String teacher; //总分 @Column("total_scores") private String totalScores; //微信号 @Column("weChat_num") private String weChatNum; //手机号 @Column("tel_num") private String telNum; //父亲名 @Column("father_name") private String fatherName; //母亲名 @Column("mother_name") private String motherName; }
- AnnotationTest方法
方法已经写好了注释,方便大家观看。注解是通过反射来获取的,所以不懂反射原理的,可以先去学一下反射。Main函数中已经提供了一个
StudentInfo
对象,以供大家参考。
public class AnnotationTest { /** * 需求设计: * 1、有一张学生信息表,字段包括姓名,年级,班级,教师名,性别等; * 2、利用注解,对每个字段的组合条件进行检查,并打印出sql */ public static void main(String[] args) { // TODO Auto-generated method stub StudentInfo studentInfo=new StudentInfo(); studentInfo.setName("Tom"); studentInfo.setSex("men"); studentInfo.setTelNum("18255005147,18255005148,18255005149"); studentInfo.setWeChatNum("qpf123456"); String sql=querySql(studentInfo); System.out.println((sql==null)?"":sql); } public static String querySql(BaseTable table) { //获取类加载器 Class<?> cTable=table.getClass(); //判断类中是否包含Table的注解 boolean tableIsExists=cTable.isAnnotationPresent(Table.class); //判断是否为表实例 if(!tableIsExists) { return null; } //获取表名 String tableName=cTable.getAnnotation(Table.class).value(); //初始化表查询语句 StringBuilder builder=new StringBuilder("select * from"); builder.append(" "+tableName+" where 1=1"); //获取表实例中的所有字段 Field[] fields=cTable.getDeclaredFields(); try { for (Field field : fields) { //判断是否为表数据字段 boolean columnIsExist=field.isAnnotationPresent(Column.class); if(!columnIsExist) { continue; } //判断表数据字段的值是否为null field.setAccessible(true); String fieldValue=(String)field.get(table); if(null==fieldValue || "".equals(fieldValue)) { continue; } //检测到需要拼接字段时,再获取数据库字段名 String columnName=field.getAnnotation(Column.class).value(); //需要用in的字段处理 if(fieldValue.contains(",")) { builder.append(" and "+columnName+" in("); String[] strs=fieldValue.split(","); for (int i = 0; i < strs.length; i++) { builder.append("'"+strs[i]+"'"); if(i!=strs.length-1) { builder.append(","); } } builder.append(")"); } else { builder.append(" and "+columnName+"="+"'"+fieldValue+"'"); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return builder.toString(); } }
上面方法打印出的查询sql语句为:
select * from StudentInfo where 1=1 and name='Tom' and sex='men' and weChat_num='qpf123456' and tel_num in('18255005147','18255005148','18255005149')