简单的注解剖析

Posted qpf1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单的注解剖析相关的知识,希望对你有一定的参考价值。

  周边有许多同事只会使用注解,并不了解注解的原理。于是随手写一个小Demo,普及下注解的使用原理,顺便加深自己的理解。如有错误,欢迎大牛指正。

1 注解类基本样式

/**
 *
 * @author qpf
 * 此注解用于对表名的设置
 *
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}

2 注解类的元素解析

元注解:
 
总共有四个元注解,分别是:@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”。后面例子中会有详细介绍。还需注意一点,注解中返回的类型是有限定的,且方法一定要是无参的,不懂的可自行百度。
 

3 利用注解实现小Demo

3.1 需求内容

1、有一张学生信息表,字段包括姓名,年级,班级,教师名,性别等;

2、利用注解,对表实例的每个字段进行检查,并打印出查询sql   

3.2 代码结构

技术分享图片

annotation:存放创建的注解。
bean:存放数据库表所用到的javaBean
test:main函数,用于测试并打印查询sql;
 

3.3 注解类

/**
 *
 * @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();
}

 

3.4 学生信息表的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;
}
 

  

3.5 AnnotationTest方法

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‘)

  

 

以上是关于简单的注解剖析的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码剖析-基于注解的IOC启动流程

SpringMVC源码剖析5:消息转换器HttpMessageConverter与@ResponseBody注解

spring注解驱动开发原理剖析

Spring2.5学习3.2_编码剖析@Resource注解的实现原理

twemproxy发送流程探索——剖析twemproxy代码正编

《02.Spring Boot连载:Spring Boot实战.Spring Boot核心原理剖析》