自定义annotation-----转载
Java从JDK5.0开始便提供了四个meta-annotation用于自定义注解的时候使用,这四个注解为:@Target,@Retention,@Documented 和@Inherited。
@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方),其源码如下:
|
1
2
3
4
5
6
7
|
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
|
可见@Target 注解只有唯一成员value,类型为ElementType数组。查看ElementType的源码可以发现,ElementType可取的值有:
CONSTRUCTOR:用于描述构造器;
FIELD:用于描述成员变量;
LOCAL_VARIABLE:用于描述局部变量;
METHOD:用于描述方法;
PACKAGE:用于描述包;
PARAMETER:用于描述参数;
TYPE:用于描述类、接口(包括注解类型) 或enum声明。
@Retention:指定被描述的注解在什么范围内有效。源码如下:
|
1
2
3
4
5
6
7
|
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
|
其中RetentionPolicy可取的值有:
SOURCE:在源文件中有效(即源文件保留);
CLASS:在class文件中有效(即class保留);
RUNTIME:在运行时有效(即运行时保留)。
@Documented:是一个标记注解,木有成员,用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。
@Inherited:元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
自定义annotation
用@interface 自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface 用来声明一个注解,其中的每一个方法实际上是声明了一个成员。方法的名称就是成员的名称,返回值类型就是成员的类型。可以通过default来声明参数的默认值。
自定义注解的基本格式为:
|
1
2
3
|
public @interface AnnotationName {
// ...
}
|
Annotation的成员定义必须满足以下三点:
成员只能用public或默认(default)这两个访问权修饰;
成员的类型只能是基本类型,String,Enum,Class,Annotation以及它们的数组类型;
如果只有一个成员,最好将其名称设为value。
AnnotatedElement代表被注解的元素,其包含许多方法,如下图所示:

…
其中主要的几个方法有:
|
1
2
3
4
|
<T extends Annotation> T getAnnotation(Class<T> annotationType) // 根据annotationType获取注解对象
Annotation[] getAnnotations() // 获取所有注解
boolean isAnnotationPresent(Class<T> annotationType) // 判断当前元素是否被annotationType注解
Annotation[] getDeclareAnnotations() // 与getAnnotations() 类似,但是不包括父类中被Inherited修饰的注解
|
实战
假如现在有一个数据库表对应的POJO被一些自定义注解所标记,现在要根据这个POJO自动生成创建库表的SQL语句。其中POJO代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
|
@Table(name = "Student")
class Bean {
@Column(name = "age", length = 3)
int age;
@Column(name = "userName", length = 10)
String name;
@Column(name = "birthday", defaultValue = "sysdate")
Date birthday;
}
|
需要生成类似如下的SQL语句:
|
1
|
create table Student(age NUMBER(3),userName VARCHAR2(10),birthday DATE default sysdate)
|
Bean类被@Table 注解所标记,所以需要定义一个ElementType.TYPE级别的注解:
|
1
2
3
4
5
|
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Table {
String name(); // name用来设置表名
}
|
而@Column 注解标注于Bean的成员变量,并且包含三个成员:
|
1
2
3
4
5
6
7
|
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
String name() default ""; // name用来设置字段名
int length() default 0; // length用来设置字段长度
String defaultValue() default ""; // defaultValue用来设置默认值
}
|
接下来定义两个方法getTableName(Class<?> bean)和getColumns(Class<?> bean),分别用于获取@Table 注解中的表名和被@Column 注解标记的成员变量信息:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
private static String getTableName(Class<?> bean) {
String name = null;
// 判断bean是否被@Table注解
if (bean.isAnnotationPresent(Table.class)) {
// 获取注解对象
Annotation tableAnnotation = bean.getAnnotation(Table.class);
try {
// 获取@Table注解所对应的name
Method method = Table.class.getMethod("name");
name = (String) method.invoke(tableAnnotation);
} catch (Exception e) {
e.printStackTrace();
}
}
return name;
}
private static List<ColumnBean> getColumns(Class<?> bean) {
List<ColumnBean> columns = new ArrayList<ColumnBean>();
Field[] fields = bean.getDeclaredFields();
if (fields != null) {
// 表里所有成员变量(fields),获取其注解信息
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
// 判断是否被@Column注解标记
if (field.isAnnotationPresent(Column.class)) {
String name = null;
int length = 0;
String defaultValue = null;
String type = null;
// 分别获取@Column注解里成员的值
Annotation columnAnnotation = field.getAnnotation(Column.class);
try {
Method nameMethod = Column.class.getMethod("name");
name = (String) nameMethod.invoke(columnAnnotation);
Method rangeMethod = Column.class.getMethod("length");
length = (Integer) rangeMethod.invoke(columnAnnotation);
Method defaultValueMethod = Column.class.getMethod("defaultValue");
defaultValue = (String) defaultValueMethod.invoke(columnAnnotation);
} catch (Exception e) {
e.printStackTrace();
}
// 判断类型,Java类型转换为数据库类型
if (int.class.isAssignableFrom(field.getType())
|| Integer.class.isAssignableFrom(field.getType())) {
type = "NUMBER";
} else if (String.class.isAssignableFrom(field.getType())) {
type = "VARCHAR2";
} else if (Date.class.isAssignableFrom(field.getType())){
type = "DATE";
}else {
throw new RuntimeException("unspported type=" + field.getType().getSimpleName());
}
columns.add(new ColumnBean(type, name, length, defaultValue));
}
}
}
return columns;
}
// 用于描述Column
private static class ColumnBean {
final String type;
final String name;
final int length;
final String defaultValue;
public ColumnBean(String type, String name, int length, String defaultValue) {
this.type = type;
this.name = name;
this.length = length;
this.defaultValue = defaultValue;
}
}
|
最后编写生成SQL的方法:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public static String createTable(Class<?> bean) {
String tableName = getTableName(bean);
List<ColumnBean> columns = getColumns(bean);
if (tableName != null && !tableName.equals("") && !columns.isEmpty()) {
StringBuilder createTableSql = new StringBuilder("create table ");
// 拼接表名
createTableSql.append(tableName);
createTableSql.append("(");
//拼接字段信息
for (int i = 0; i < columns.size(); i++) {
ColumnBean column = columns.get(i);
createTableSql.append(column.name);
createTableSql.append(" ");
createTableSql.append(column.type);
int length = column.length;
if(length != 0){
createTableSql.append("(");
createTableSql.append(column.length);
createTableSql.append(")");
}
String defaultValue = column.defaultValue;
if(defaultValue != null && defaultValue.length() != 0){
createTableSql.append(" default ");
createTableSql.append(defaultValue);
}
if (i != columns.size() - 1) {
createTableSql.append(",");
}
}
createTableSql.append(")");
return createTableSql.toString();
}else {
throw new RuntimeException("table's name is null");
}
}
|
测试
|
1
2
3
4
5
|
public class Test {
public static void main(String[] args) {
System.out.println(TestUtils.createTable(Bean.class));
}
}
|
生成的结果和预期一致
总结
上述的过程可归纳为以下几个步骤:
判断AnnotatedElement是否被某注解所标记:
AnnotatedElement.isAnnotationPresent(SomeAnnotation.class);是的话,获取该注解对象:
Annotation annotation = bean.getAnnotation(SomeAnnotation.class);;根据该注解对象获取某个成员参数(比如name):
Method method = SomeAnnotation.class.getMethod("name");;利用反射机制,获取该注解中的某成员的值:
String name = (String) method.invoke(annotation);。
自定义annotation-----转载的更多相关文章
- java自定义Annotation(载自百度文库)
java中自定义annotation需要@interface关键字和用到几个内置annotation. 用到的注解有@Target,@Retention,@Documented,@Inherited ...
- 自定义Annotation
来源:http://blog.csdn.net/lifetragedy/article/details/7394910 概念篇 来看一个最简单的annotation示例 @Target(Element ...
- Java注解Annotation的用法 - 自定义Annotation实现
Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据. Java语言中的类.方法.变量.参数和包等都可以被标注.和Javadoc不同,Java标注可以通过反射获取标 ...
- puppet之自定义fact(转载)
1.使用环境变量'FACTERLIB'创建fact 1.1.在自定义目录里面定义一个fact,列出当前系统登录的用户数 [root@agent1 ~]# vim /var/lib/puppet/kis ...
- Java自定义Annotation,通过反射解析Annotation
创建一个自定义的Annotation import java.lang.annotation.*; import java.lang.reflect.Method; @Documented @Targ ...
- Lombok自定义annotation扩展含Intellij插件
Lombok简介 Lombok(https://projectlombok.org/) 提供了以注解的形式为java对象增加属性和方法,这使得原来冗长的java源文件变的简洁(不需要再使用ide去生 ...
- 注解2 --- 自定义 Annotation --- 技术搬运工(尚硅谷)
定义新的 Annotation 类型使用 @interface 关键字 自定义注解自动继承了java.lang.annotation.Annotation接口 Annotation 的成员变量在 An ...
- springMVC的自定义annotation(@Retention@Target)详解
自定义注解: 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节.在定义注解时,不能继承其他的注解或接口.@ ...
- SQL Server自定义函数( 转载于51CTO )
用户自定义函数自定义函数不能执行一系列改变数据库状态的操作,可以像系统函数在查询或存储过程等的程序中使用,也可以像相信过程一样能过 execute 命令来执行.自定义函数中存储了一个 Transact ...
- iOS高德地图自定义annotation添加不同图片
1.model类里面添加index #import <MAMapKit/MAMapKit.h> #import <AMapSearchKit/AMapCommonObj.h> ...
随机推荐
- 042——VUE中组件之子组件使用$on与$emit事件触发父组件实现购物车功能
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- HTML5里的placeholder属性
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- 9.2 Zynq嵌入式系统调试方法
陆佳华书<嵌入式系统软硬件协同设计实战指南 第2版>这本书中的实例着实浪费了我不少时间.从本书第一个实例我就碰了一鼻子灰.当然显然是自己时新手的原因.首先第一个实验其实真的特别简单,为什么 ...
- iis6手工创建网站后无法运行php脚本
给人搬了十几个网站,老站用西部数码建站助手创建的,现在过期了无法继续创建,只能在Internet 信息服务(IIS)管理器创建网站,创建下来都没问题,但是就是无法打开网站. 测试打开txt文档.静态页 ...
- 利用Sonar定制自定义JS扫描规则(一)——sonar环境搭建
接触sonar已经有段时间了,最早是在一个项目组里面听到这个框架,后来在公司持续集成活动的推动下,也逐渐学习了sonar的具体功能.sonar集成了findbugs,pmd,checkstyle各种静 ...
- Java中常用的日期操作方法
日常项目当中经常会用到操作日期,抽出时间总结了一下,欢迎和大家多多沟通学习,如有任何改进意见和建议,我的QQ是1243672,欢迎联系哦. /** * 日期操作类 * 开发公司:北京东达悦科技有限公司 ...
- 此文记录了我从研二下学期到研三上学期的找工历程,包括百度、腾讯、网易、移动、电信、华为、中兴、IBM八家企业的面试总结和心得--转
感谢电子通讯工程的研究生学长为大家整理了这么全面的求职总结,希望进入通信公司和互联网公司做非技术类岗位的学弟学妹们千万不要错过哦~ ---------------------------原文分割线-- ...
- 命令行视频(ts/m3u8)下载工具 —— youtube-dl(ffmpeg 解码)
youtube-dl 支持的站点:youtube-dl Supported sites youtube-dl 命令行参数: –version:查看版本: 1. 命令行工具安装 安装视频编解码工具 ff ...
- tensorflow中协调器 tf.train.Coordinator 和入队线程启动器 tf.train.start_queue_runners
TensorFlow的Session对象是支持多线程的,可以在同一个会话(Session)中创建多个线程,并行执行.在Session中的所有线程都必须能被同步终止,异常必须能被正确捕获并报告,会话终止 ...
- Python 高性能并行计算之 mpi4py
MPI 和 MPI4PY 的搭建上一篇文章已经介绍,这里面介绍一些基本用法. mpi4py 的 helloworld from mpi4py import MPI print(&quo ...