如果没有用来读取注解的工具,那注解将基本没有任何作用,它也不会比注释更有用。读取注解的工具叫作注解处理器。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. 父页面(JSP页面)传参数到子页面(JSP页面)

    父页面(JSP页面)传参数到子页面(JSP页面) 1.父页面传参数到子页面 //JavaScript代码 $.ajax({ type:"POST", uri:"../st ...

  2. 使用串口下载vxworks映象的方法

    使用串口下载vxworks映象的方法 由于坛子里这方面的可行性文章比较少,不时有一些网友在询问这方面的问题,再加上通过这种方法可以调试网络驱动,所以我花了一点时间把整个下载过程试了一下. 1.配置co ...

  3. freemarker报错之九

    1.错误描述 五月 30, 2014 11:52:04 下午 freemarker.log.JDK14LoggerFactory$JDK14Logger error 严重: Template proc ...

  4. E: 无法获得锁 /var/lib/apt/lists/lock - open (11: 资源暂时不可用)

    1 错误描述 youhaidong@youhaidong:~$ sudo apt-get update E: 无法获得锁 /var/lib/apt/lists/lock - open (11: 资源暂 ...

  5. nested exception is java.sql.SQLException: IO 错误

    1.错误描述 (mx.messaging.messages::ErrorMessage)#0 body = (null) clientId = "18CE3B03-9709-9DA8-763 ...

  6. Nethogs - 网络流量监控工具

    命令iftop来检查带宽使用情况.netstat用来查看接口统计报告.还有其他的一些工具Bandwidthd.Speedometer.Nethogs.Darkstat.jnettop.ifstat.i ...

  7. CentOS 7.0 启动多个MySQL实例(mysql-5.7.21)

    Linux系统:CentOS-7.0 MySQL版本:5.7.21 Linux系统下启动多个MySQL实例,目前知道有两种方法,一种是通过官方提供的mysqld_multi.server来实现,但是我 ...

  8. 1.1 java语言有什么优点

    1.java是一种纯面向对象的语言.(更易理解和学习) 2.平台无关性.(java是一种解释性的语言,编译器会把java语言变成中间代码,然后在JVM上运行,可移植性) 3.java提供了很多内置的库 ...

  9. 关于 CI框架访问数据库类提示Call to undefined function mysqli_init() 问题解决

    我上次实践发现,安装在Win10 WampServer3.0.4集成环境,不仅打不开phpmyadmin会报错就算了,而且报错后又没提示那么解决,同时你打开php扩展配置发现,WampServer系统 ...

  10. Python 常用命令

    对Python进行软件的安装.卸载和查看,是我们在日常工作中经常要做的事情,有的时候会突然忘记常用的命令,所以在此记录下来: pip 安装软件包 pip install xxx 卸载软件包 pip u ...