如果没有用来读取注解的工具,那注解将基本没有任何作用,它也不会比注释更有用。读取注解的工具叫作注解处理器。Java提供了两种方式来处理注解:第一种是利用运行时反射机制;另一种是使用Java提供的API来处理编译期的注解。

反射机制方式的注解处理器

仅当定义的注解的@Retention为RUNTIME时,才能够通过运行时的反射机制来处理注解。下面结合例子来说明这种方式的处理方法。

Java中的反射API(如java.lang.Class、java.lang.reflect.Field等)都实现了接口java.lang.reflect.AnnotatedElement,来提供获取类、方法和域上的注解的实用方法。

通过JavaBean上定义的注解来生成相应的SQL。

1.1、定义注解

1.1.1、类注解映射表名

package com.zenfery.example.annotation.sql;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target(ElementType.TYPE)//定义注解应用于类
@Retention(RetentionPolicy.RUNTIME)//定义注解在JVM运行时保留
public @interface TableSQL {
    String value() default "";//指定对应的表名
}

定义注解@TableSQL,只定义一个value值来映射表名,默认值为空,如果程序不给此值,将使用类名(小写)来作为表名。

1.1.2、属性与字段对应注解

package com.zenfery.example.annotation.sql;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target(ElementType.FIELD)//定义注解应用于成员变量
@Retention(RetentionPolicy.RUNTIME)//定义注解在JVM运行时保留
public @interface TableColumnSQL {
 String value() default "";
 Constraint constraint() default @Constraint();
}

定义注解@TableColumnSQL的目标为FIELD,仅能在类的属性上使用;value()属性定义对应的字段名;constraint()定义字段的约束,它是由注解@Constraint定义,其定义如下:

package com.zenfery.example.annotation.sql;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target(ElementType.FIELD)//定义注解应用于成员变量
@Retention(RetentionPolicy.RUNTIME)//定义注解在JVM运行时保留
public @interface Constraint {
 boolean allowNull() default true//是否允许为空
 boolean isPrimary() default false//是否为主键
}

@Constraint注解仅定义了两个注解元素,allowNull()指定字段是否允许为空值;isPrimary()指定字段是否是主键。

1.2、定义JavaBean

package com.zenfery.example.annotation.clazz;
 
import com.zenfery.example.annotation.sql.Constraint;
import com.zenfery.example.annotation.sql.TableColumnSQL;
import com.zenfery.example.annotation.sql.TableSQL;
 
/**
 * 定义User类与数据库映射
 * @author zenfery
 */
@TableSQL()
public class User {
  
 //定义id字段,与表user的列id相映射,指定约束为:不为空,为主键。
 @TableColumnSQL(value="id",constraint=@Constraint(allowNull=false,isPrimary=true))
 String id;
  
 //只为注解指定value字段,可省略value。
 @TableColumnSQL("name")
 String name;
}

定义User类。类上使用@TableSQL来注解映射表名;字段id和name使用@TableColumnSQL来注解映射字段名。

1.2、注解处理器

在以上工作做完后,注解没有任何意义(它没有做任何事情),下面来为RUNTIME级别的注解来编写处理器,在此编写的处理器和真实的处理器相差比较多,无通用性,仅为演示理解用。详细代码如下:

package com.zenfery.example.annotation.clazz;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
 
import com.zenfery.example.annotation.sql.Constraint;
import com.zenfery.example.annotation.sql.TableColumnSQL;
import com.zenfery.example.annotation.sql.TableSQL;
 
public class TableSQLHandler {
 
  /**
   * 注解处理器:读取User类中注解,生成对应的SQL并打印出来
   * 在此假设表的所有字段均为varchar(10)
   * @since JDK 1.6
   * @param args
   */
  public static void main(String[] args) {
    /**
     * 注解的@Retention均为JVM保留注解(RetentionPolicy.RUNTIME)
     * ,在此直接使用main方法启动JVM,通过Java提供的反射机制来处
     * 理。
     */
    try {
      //指定在JVM需要处理注解的类
      Class userClass = null;
      //userClass = Class.forName("com.zenfery.example.annotation.clazz.User");
      userClass = User.class;
       
      //打印所有类的注解
      Annotation[] annotations = userClass.getDeclaredAnnotations();
      for(int i=0; i<annotations.length; i++){
        System.out.println("注解["+(i+1)+"] = "+annotations[i].toString());
      }
       
      //检查类是否有@TableSQL注解
      if( userClass.isAnnotationPresent(TableSQL.class) ){
        //sql
        String sql = "\nCREATE TABLE ";
        //注解了TableSQL注解
        TableSQL ts = (TableSQL)userClass.getAnnotation(TableSQL.class);
        String tableName = ts.value();
        if("".equals(tableName)){//如果获取的值为TableSQL的默认值,则使用类名来做为表名
          tableName = userClass.getSimpleName().toLowerCase();
        }
        System.out.println("获取"+userClass.getName()+"对应的表名为:"+tableName);
        sql += tableName + " ( \n";
         
        //从User类的属性中获取需要与数据库映射的字段
        Field[] fields = userClass.getDeclaredFields();
         
        List<String> primaryKeys = new ArrayList<String>();//存储主键
        for(int i=0; i<fields.length; i++){
          Field field = fields[i];
          if( field.isAnnotationPresent(TableColumnSQL.class) ){
            TableColumnSQL tcs = (TableColumnSQL)field.getAnnotation(TableColumnSQL.class);
            String fieldName = tcs.value();//表中的字段名
            Constraint c = tcs.constraint();//字段对应的约束
            boolean allowNull = c.allowNull();//是否可为空
            boolean isPrimary = c.isPrimary();//是否为主键
             
            //拼接SQL
            sql += "\t" + fieldName +" VARCHAR(10)";
            if(!allowNull) sql += " NOT NULL";//不允许为空
            if(i<fields.length-1) sql+= ",\n";
             
            //主键
            if(isPrimary) primaryKeys.add(fieldName);
          }else{
            System.out.println("字段"+field.getName()+"未使用注解@TableColumnSQL!");
          }
        }
        if(primaryKeys.size()>0){
          StringBuilder keys = new StringBuilder();
          for(int k=0; k<primaryKeys.size(); k++){
            keys.append(primaryKeys.get(k));
            if(k<primaryKeys.size()-1)keys.append(",");
          }
           
          sql += ",\n\tPRIMARY KEY "+keys.toString();
        }
        sql += "\n) DEFAULT CHARSET=utf8";
        // ====> 打印SQL
        System.out.println("生成的SQL:"+sql);
         
      }else{
        System.out.println("警告:"+userClass.getName()+"未使用@TableSQL注解!");
      }
    catch (Exception e) {
      e.printStackTrace();
    }
 
  }
 
}
  1. 获取使用了注解的User类。
  2. 根据类上的注解@TableSQL获取表名。
  3. 根据类中所有的字段上的注解@TableColumnSQL来获取字段名,并获取字段的特性。
  4. 根据获取的表名和字段名拼接SQL。
  5. 打印SQL。

运行TableSQLHandler的main()方法,输出:

注解[1] = @com.zenfery.example.annotation.sql.TableSQL(value=)
获取com.zenfery.example.annotation.clazz.User对应的表名为:user
生成的SQL:
CREATE TABLE user (
 id VARCHAR(10) NOT NULL,
 name VARCHAR(10),
 PRIMARY KEY id
) DEFAULT CHARSET=utf8

转载请注明:子暃之路 » Java注解(2)-注解处理器(运行时|RetentionPolicy.RUNTIME)

Java注解(2)-注解处理器(运行时|RetentionPolicy.RUNTIME)的更多相关文章

  1. Android中的自定义注解(反射实现-运行时注解)

    预备知识: Java注解基础 Java反射原理 Java动态代理 一.布局文件的注解 我们在Android开发的时候,总是会写到setContentView方法,为了避免每次都写重复的代码,我们需要使 ...

  2. java 常用类库:操作系统System类,运行时环境Runtime

    System类: System 类代表Java程序的运行平台,程序不能创建System类的对象, System类提供了一些类变量和类方法,允许直接通过 System 类来调用这些类变量和类方法. Sy ...

  3. Java 反射(二)运行时获取类的信息

    目录 一.获得类的运行时结构 1. 获得类的名字 2. 获得类的属性 获取属性列表 获取指定属性 3. 获取类的方法 获得类的方法列表 获得指定方法 4. 获得的构造器 获得构造器列表 获得指定构造器 ...

  4. 通过SOFA看Java服务端如何实现运行时的模块化

    本文阅读时间大约7分钟. 今天我们谈谈SOFA模块化,首先看一段SOFA的介绍: SOFABoot是蚂蚁金服开源的基于Spring Boot的研发框架,它在Spring Boot的基础上,提供了诸如 ...

  5. Java中获取类的运行时结构

    获取运行时类的完整结构 通过反射获取运行时类的完整结构 Field(属性).Method(方法).Constructor(构造器).Superclass(父类).Interface(接口).Annot ...

  6. [二]Java虚拟机 jvm内存结构 运行时数据内存 class文件与jvm内存结构的映射 jvm数据类型 虚拟机栈 方法区 堆 含义

    前言简介 class文件是源代码经过编译后的一种平台中立的格式 里面包含了虚拟机运行所需要的所有信息,相当于 JVM的机器语言 JVM全称是Java Virtual Machine  ,既然是虚拟机, ...

  7. Java虚拟机 - 结构原理与运行时数据区域

    http://liuwangshu.cn/java/jvm/1-runtime-data-area.html 前言 本来计划要写Android内存优化的,觉得有必要在此之前介绍一下Java虚拟机的相关 ...

  8. iOS运行时编程(Runtime Programming)和Java的反射机制对比

    运行时进行编程,类似Java的反射.运行时编程和Java反射的对比如下:   1.相同点   都可以实现的功能:获取类信息.属性设置获取.类的动态加载(NSClassFromString(@“clas ...

  9. Java源文件编译成功但是运行时加载不到文件

    最近系统重装了一些,Java等环境变量都需要重新配置,配置好以后编写了一个Java源文件编译了一下,通过Javac编译源文件,编译成功,但是再通过Java运行时没找到报出找不到加载文件或者加载文件不存 ...

随机推荐

  1. Deadlock found when trying to get lock; try restarting transaction

    1.错误描述 [ERROR:]2015-06-09 16:56:19,481 [抄送失败] org.hibernate.exception.LockAcquisitionException: erro ...

  2. Logger之简单入门

    Java 中自带的日志系统,今天抽空了解了一点,算是入了门,所以将自己的一些心得记录下来,以备日后查看,有兴趣的朋友,看到此文章,觉得有错误或需要添加的地方,请在下方评论留言,大家可以共同进步,谢谢: ...

  3. 漫漫人生路,学点Jakarta基础-重写(覆盖)、重载

    首先我们现在开始进入Jakarta的时代,由原甲骨文易主到 Eclipse基金会下,但是不想舍弃java名字,因此基金会重新投票选出了Jakarta EE(雅加达).但是我们明白换汤汤不换药的道理,基 ...

  4. Spring有什么缺点?

    Spring有什么缺点如下: 1.jsp中要写很多代码: 2.控制器过于灵活,缺少一个公用控制器: 3.不支持分布式部署.

  5. mybatis快速入门(四)

    mybatis动态标签<where><if><foreach>以及sql片段 1.创建一个包装类UserQueryVo.java package cn.my.myb ...

  6. Luogu3092:[USACO13NOV]No Change

    题面 传送门 Sol 状压一下\(k\),\(f[S]\)表示用过的硬币集合为\(S\)能买到的物品个数 # include <bits/stdc++.h> # define RG reg ...

  7. [ZJOI2007]时态同步

    题目描述 小Q在电子工艺实习课上学习焊接电路板.一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字1,2,3….进行标号.电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点 ...

  8. oracle 裸设备划分 --centos6.5

    主题思想:物理卷PV->卷组VG->逻辑卷LV(类型:raw)->添加表空间 操作过程 vg_orcl         8g 一:划分 二:创建裸设备 方法1:目前最常用的方法 #c ...

  9. 详解QT5.10.0搭载OpenCV3.4.0环境配置步骤说明

    一.准备工作: 1下载Qt5.10.0 和 Qt Creator 4.5.0软件(http://download.qt.io/official_releases/qt/) 2.下载opencv3.40 ...

  10. 25.django Model

    django ORM基本配置 django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表 1.修改project数据库配置 (1)settigs.py里面 默认 DAT ...