如果没有用来读取注解的工具,那注解将基本没有任何作用,它也不会比注释更有用。读取注解的工具叫作注解处理器。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. JavaScript实现弹窗报错

    JavaScript实现弹窗报错 1.具体错误如下 SCRIPT 5022:cannot call methods on dialog prior to initialization; attempt ...

  2. Struts+Spring+Hibernate、MVC、HTML、JSP

    javaWeb应用 JavaWeb使用的技术,比如SSH(Struts.Spring.Hibernate).MVC.HTML.JSP等等技术,利用这些技术开发的Web应用在政府项目中非常受欢迎. 先说 ...

  3. 权限的分类(shiro项目中来的五)

    第一种权限:菜单栏展示还是不展示的权限(粗颗粒) 实现方法,在SYS_ROLE表中添加一个字段rights,通过 public static BigInteger sumRights(String[] ...

  4. iOS 双击tabbar刷新页面

    /*在继承UITabBarController控制器中*/ #pragma mark <UITabBarControllerDelegate> -(void)tabBarControlle ...

  5. ORA-01940: cannot drop a user that is currently connected解决方法

    我们在删除数据库用户时候会碰到如下错误 SQL> DROP USER sys_xj cascade; DROP USER sys_xj cascade*ERROR at line 1:ORA-0 ...

  6. TensorLayer官方中文文档1.7.4:API – 强化学习

    API - 强化学习¶ 强化学习(增强学习)相关函数. discount_episode_rewards([rewards, gamma, mode]) Take 1D float array of ...

  7. jquery的各种插件调用(有些已经过时,以备注,其他的一些可以闲的时候用作拆解)

    jquery的valiteDate验证插件应用 <!DOCTYPE html> <html>        < head>            < meta ...

  8. 【CJOJ P1333】【HNOI2012】矿场搭建

    [HNOI2012]矿场搭建 Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖 ...

  9. Vue-路由配置和使用步骤整理

    介绍 路由:控制组件之间的跳转,不会实现请求.不用页面刷新,直接跳转-切换组件>>> 安装 本地环境安装路由插件vue-router:    cnpm install vue-rou ...

  10. js制作列表滚动(有滚动条)

    function mouseWheel(obj, fn){ var ff = navigator.userAgent.indexOf("Firefox"); if (ff != - ...