如果没有用来读取注解的工具,那注解将基本没有任何作用,它也不会比注释更有用。读取注解的工具叫作注解处理器。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) |
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 (<span style= "color: #0000ff;" >ElementType.FIELD</span>) |
@Retention (RetentionPolicy.RUNTIME) |
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 (<span style= "color: #0000ff;" >ElementType.FIELD</span>) |
@Retention (RetentionPolicy.RUNTIME) |
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; |
@TableColumnSQL (value= "id" ,constraint= @Constraint (allowNull= false ,isPrimary= true )) |
定义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 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) |
public static void main(String[] args) { |
* 注解的@Retention均为JVM保留注解(RetentionPolicy.RUNTIME) |
* ,在此直接使用main方法启动JVM,通过Java提供的反射机制来处 |
Annotation[] annotations = userClass.getDeclaredAnnotations(); |
for ( int i= 0 ; i<annotations.length; i++){ |
System.out.println( "注解[" +(i+ 1 )+ "] = " +annotations[i].toString()); |
if ( userClass.isAnnotationPresent(TableSQL. class ) ){ |
String sql = "\nCREATE TABLE " ; |
TableSQL ts = (TableSQL)userClass.getAnnotation(TableSQL. class ); |
String tableName = ts.value(); |
if ( "" .equals(tableName)){ |
tableName = userClass.getSimpleName().toLowerCase(); |
System.out.println( "获取" +userClass.getName()+ "对应的表名为:" +tableName); |
sql += tableName + " ( \n" ; |
Field[] fields = userClass.getDeclaredFields(); |
List<String> primaryKeys = new ArrayList<String>(); |
for ( int i= 0 ; i<fields.length; 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 += "\t" + fieldName + " VARCHAR(10)" ; |
if (!allowNull) sql += " NOT NULL" ; |
if (i<fields.length- 1 ) sql+= ",\n" ; |
if (isPrimary) primaryKeys.add(fieldName); |
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" ; |
System.out.println( "生成的SQL:" +sql); |
System.out.println( "警告:" +userClass.getName()+ "未使用@TableSQL注解!" ); |
- 获取使用了注解的User类。
- 根据类上的注解@TableSQL获取表名。
- 根据类中所有的字段上的注解@TableColumnSQL来获取字段名,并获取字段的特性。
- 根据获取的表名和字段名拼接SQL。
- 打印SQL。
运行TableSQLHandler的main()方法,输出:
注解[1] = @com.zenfery.example.annotation.sql.TableSQL(value=) |
获取com.zenfery.example.annotation.clazz.User对应的表名为:user |
转载请注明:子暃之路 » Java注解(2)-注解处理器(运行时|RetentionPolicy.RUNTIME)