如果没有用来读取注解的工具,那注解将基本没有任何作用,它也不会比注释更有用。读取注解的工具叫作注解处理器。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. Jquery常用操作:checkbox、select取值,radio、checkbox、select选中及其相关

    常用Jquery操作:checkbox取值.select取值.radio选中.checkbox选中.select选中及其相关: 1.影藏页面元素 使用jquery真的很方便,比如要控制div的显示与隐 ...

  2. Git Compare with base,比较大文件时,长时间等待,无法加载

    问题 当使用Git比较一个大文件(几十兆数量级)版本见差异时,会一直等待加载,且内存消耗很大,导致其他进程很难执行.任务管理器中,可以看到此时的TortoiseGitMerge吃掉3G左右的内存. 原 ...

  3. C#图解教程 第十六章 转换

    转换 什么是转换隐式转换显式转换和强制转换 强制转换 转换的类型数字的转换 隐式数字转换溢出检测上下文 1.checked和unchecked运算符2.checked语句和unchecked语句 显式 ...

  4. js+jq实现图片预览,支持到ie9+ff+chrome

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 白话讲述Java中volatile关键字

    一.由一段代码引出的问题 首先我们先来看这样一段代码: public class VolatileThread implements Runnable{ private boolean flag = ...

  6. Xcode 9.0 新增功能大全

    Xcode是用于为Apple TV,Apple Watch,iPad,iPhone和Mac创建应用程序的完整开发人员工具集.Xcode开发环境采用tvOS SDK,watchOS SDK,iOS SD ...

  7. javaweb代码生成器,专注于javaweb项通用目的代码生成器

    该项目为javaWEB项目通用代码生成器,根据数据库表和自定义代码模板生成相应的jsp,js,java文件,生成到指定路径下,javaweb项目开发利器: 项目开源地址:https://gitee.c ...

  8. angularjs 缓存详解

    一.什么是缓存 一个缓存就是一个组件,它可以透明地存储数据,以便未来可以更快地服务于请求. 缓存能够服务的请求越多,整体系统性能就提升得越多. 二.Angular 中的缓存 2.1 $cacheFac ...

  9. 使用IIS Server Farms搭建应用服务负载均衡

    当公司的业务扩大, 伴随着大量的请求,应用服务器的承受能力已经不能满足不断增长的业务需求,使用IIS Server Farms搭建应负载均衡的方式,把请求分发给不同的应用服务器进行处理,这个时候就降低 ...

  10. sys模块和序列化模块

    import sysprint(sys.version) #查看当前pycharm版本print(sys.path )#返回模块的搜索路径print(sys.platform )#返回操作系统的版本p ...