Java注解简介

本文首先简单介绍Java的基本注解,然后借助自定义注解的方式,帮助大家理解 HibernateSpring, Struts等等第三方注解是如何工作的。

基本内置注解

@Override

@Override 用在方法上,表示这个方法重写了父类的方法,如toString()

如果父类没有这个方法,那么就无法编译通过,如例所示,在fromString()方法上加上@Override 注解,就会失败,因为Hero类的父类Object,并没有fromString方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package annotation;

public class Hero {

String name;
@Override
public String toString(){
return name;
}
@Override
public String fromString(){
return name;
}
}

@Deprecated

@Deprecated 表示这个方法已经过期,不建议开发者使用。(暗示在将来某个不确定的版本,就有可能会取消掉)

如例所示,开地图这个方法hackMap,被注解为过期,在调用的时候,就会受到提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package annotation;

public class Hero {

String name;

@Deprecated
public void hackMap(){

}
public static void main(String[] args) {
new Hero().hackMap();
}

}

@SuppressWarnings

@SuppressWarnings Suppress英文的意思是抑制的意思,这个注解的用处是忽略警告信息。

比如大家使用集合的时候,有时候为了偷懒,会不写泛型,像这样:

1
List heros = new ArrayList();

那么就会导致编译器出现警告,而加上

1
@SuppressWarnings({ "rawtypes", "unused" })

就对这些警告进行了抑制,即忽略掉这些警告信息。
@SuppressWarnings 有常见的值,分别对应如下意思:

含义
deprecation 使用了不赞成使用的类或方法时的警告(使用@Deprecated使得编译器产生的警告);
unchecked 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告
fallthrough 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
path 在类路径、源文件路径等中有不存在的路径时的警告;
serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告;
finally 任何 finally 子句不能正常完成时的警告;
rawtypes 泛型类型未指明
unused 引用定义了,但是没有被使用
all 关于以上所有情况的警告。
1
2
3
4
5
6
7
8
9
10
11
12
13
package annotation;

import java.util.ArrayList;
import java.util.List;

public class Hero {
String name;
@SuppressWarnings({ "rawtypes", "unused" })
public static void main(String[] args) {
List heros = new ArrayList();
}

}

@SafeVarargs

@SafeVarargs 这是1.7 之后新加入的基本注解. 如例所示,当使用可变数量的参数的时候,而参数的类型又是泛型T的话,就会出现警告。 这个时候,就使用@SafeVarargs来去掉这个警告

@SafeVarargs注解只能用在参数长度可变的方法或构造方法上,且方法必须声明为static或final,否则会出现编译错误。一个方法使用@SafeVarargs注解的前提是,开发人员必须确保这个方法的实现中对泛型类型参数的处理不会引发类型安全问题。

1
2
3
4
5
6
7
8
9
10
package annotation;

public class Hero {
String name;

@SafeVarargs
public static <T> T getFirstOne(T... elements) {
return elements.length > 0 ? elements[0] : null;
}
}

@FunctionalInterface

@FunctionalInterface这是Java1.8 新增的注解,用于约定函数式接口。

函数式接口概念: 如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口称为函数式接口。函数式接口其存在的意义,主要是配合Lambda 表达式 来使用。

如例所示,AD接口只有一个adAttack方法,那么就可以被注解为@FunctionalInterface,而AP接口有两个方法apAttack()和apAttack2(),那么就不能被注解为函数式接口:

  • AD.java
1
2
3
4
5
6
package annotation;

@FunctionalInterface
public interface AD {
public void adAttack();
}
  • AP.java
1
2
3
4
5
6
7
package annotation;

@FunctionalInterface
public interface AP {
public void apAttack();
public void apAttack2();
}

自定义注解

非注解方式DBUtil

通常来讲,在一个基于JDBC开发的项目里,都会有一个DBUtil这么一个类,在这个类里统一提供连接数据库的IP地址,端口,数据库名称, 账号,密码,编码方式等信息。如例所示,在这个DBUtil类里,这些信息,就是以属性的方式定义在类里的。

下例运行结果是获取一个连接数据库test的连接Connection实例。

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
package util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBUtil {
static String ip = "127.0.0.1";
static int port = 3306;
static String database = "test";
static String encoding = "UTF-8";
static String loginName = "root";
static String password = "admin";
static{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException {
String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s", ip, port, database, encoding);
return DriverManager.getConnection(url, loginName, password);
}
public static void main(String[] args) throws SQLException {
System.out.println(getConnection());
}
}

: 运行需要用到连接mysql的jar包。

自定义注解@JDBCConfig

接下来,就要把DBUtil这个类改造成为支持自定义注解的方式。 首先创建一个注解JDBCConfig

  1. 创建注解类型的时候即不使用class也不使用interface,而是使用@interface
1
public @interface JDBCConfig
  1. 元注解
  • @Target({METHOD,TYPE}) 表示这个注解可以用用在类/接口上,还可以用在方法上
  • @Retention(RetentionPolicy.RUNTIME) 表示这是一个运行时注解,即运行起来之后,才获取注解中的相关信息,而不像基本注解如@Override 那种不用运行,在编译时eclipse就可以进行相关工作的编译时注解。
  • @Inherited 表示这个注解可以被子类继承
  • @Documented 表示当执行javadoc的时候,本注解会生成相关文档
    请在学习完本知识点最后一个步骤解析注解之后,再查看 元注解,做更详尽的学习。
  1. 注解元素,这些注解元素就用于存放注解信息,在解析的时候获取出来
1
2
3
4
5
6
String ip(); 
int port() default 3306;
String database();
String encoding();
String loginName();
String password();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package anno;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface JDBCConfig {
String ip();
int port() default 3306;
String database();
String encoding();
String loginName();
String password();
}

注解方式DBUtil

有了自定义注解@JDBCConfig之后,我们就把非注解方式DBUtil改造成为注解方式DBUtil。

如例所示,数据库相关配置信息本来是以属性的方式存放的,现在改为了以注解的方式,提供这些信息了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package util;

import anno.JDBCConfig;

@JDBCConfig(ip = "127.0.0.1", database = "test", encoding = "UTF-8", loginName = "root", password = "admin")
public class DBUtil {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

}

: 目前只是以注解的方式提供这些信息,但是还没有解析,接下来进行解析

解析注解

接下来就通过反射,获取这个DBUtil这个类上的注解对象

1
JDBCConfig config = DBUtil.class.getAnnotation(JDBCConfig.class);

拿到注解对象之后,通过其方法,获取各个注解元素的值:

1
2
3
4
5
6
String ip = config.ip();
int port = config.port();
String database = config.database();
String encoding = config.encoding();
String loginName = config.loginName();
String password = config.password();

后续就一样了,根据这些配置信息得到一个数据库连接Connection实例。

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
package util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import anno.JDBCConfig;

@JDBCConfig(ip = "127.0.0.1", database = "test", encoding = "UTF-8", loginName = "root", password = "admin")
public class DBUtil {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException, NoSuchMethodException, SecurityException {
JDBCConfig config = DBUtil.class.getAnnotation(JDBCConfig.class);

String ip = config.ip();
int port = config.port();
String database = config.database();
String encoding = config.encoding();
String loginName = config.loginName();
String password = config.password();

String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s", ip, port, database, encoding);
return DriverManager.getConnection(url, loginName, password);
}

public static void main(String[] args) throws NoSuchMethodException, SecurityException, SQLException {
Connection c = getConnection();
System.out.println(c);
}
}

: 运行需要用到连接mysql的jar包。

元注解

元注解概念

在讲解元注解概念之前,我们先建立元数据的概念。 元数据在英语中对应单词 metadata, metadata在wiki中的解释是:

Metadata is data [information] that provides information about other data

为其他数据提供信息的数据

这样元注解就好理解了,元注解 meta annotation 用于注解 自定义注解 的注解。元注解有这么几种:

  • @Target
  • @Retention
  • @Inherited
  • @Documented
  • @Repeatable (java1.8 新增)

@Target

@Target 表示这个注解能放在什么位置上,是只能放在类上?还是即可以放在方法上,又可以放在属性上。自定义注解@JDBCConfig 这个注解上的@Target是:@Target({METHOD,TYPE}),表示他可以用在方法和类型上(类和接口),但是不能放在属性等其他位置。 可以选择的位置列表如下:

参数 解释
ElementType.TYPE 能修饰类、接口或枚举类型
ElementType.FIELD 能修饰成员变量
ElementType.METHOD 能修饰方法
ElementType.PARAMETER 能修饰参数
ElementType.CONSTRUCTOR 能修饰构造器
ElementType.LOCAL_VARIABLE 能修饰局部变量
ElementType.ANNOTATION_TYPE 能修饰注解
ElementType.PACKAGE 能修饰包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package anno;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface JDBCConfig {
String ip();
int port() default 3306;
String database();
String encoding();
String loginName();
String password();
}

@Retention

@Retention 表示生命周期,自定义注解@JDBCConfig 上的值是 RetentionPolicy.RUNTIME, 表示可以在运行的时候依然可以使用。 @Retention可选的值有3个:

参数 解释
RetentionPolicy.SOURCE 注解只在源代码中存在,编译成class之后,就没了。@Override 就是这种注解。
RetentionPolicy.CLASS 注解在java文件编程成.class文件后,依然存在,但是运行起来后就没了。@Retention的默认值,即当没有显式指定@Retention的时候,就会是这种类型。
RetentionPolicy.RUNTIME 注解在运行起来之后依然存在,程序可以通过反射获取这些信息,自定义注解@JDBCConfig 就是这样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package anno;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented

public @interface JDBCConfig {
String ip();
int port() default 3306;
String database();
String encoding();
String loginName();
String password();
}

@Inherited

@Inherited 表示该注解具有继承性。如例,设计一个DBUtil的子类,其getConnection2方法,可以获取到父类DBUtil上的注解信息。

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
package util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import anno.JDBCConfig;

public class DBUtilChild extends DBUtil {

public static Connection getConnection2() throws SQLException, NoSuchMethodException, SecurityException {
JDBCConfig config = DBUtilChild.class.getAnnotation(JDBCConfig.class);
String ip = config.ip();
int port = config.port();
String database = config.database();
String encoding = config.encoding();
String loginName = config.loginName();
String password = config.password();

String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s", ip, port, database, encoding);
return DriverManager.getConnection(url, loginName, password);
}

public static void main(String[] args) throws NoSuchMethodException, SecurityException, SQLException {
Connection c = getConnection2();
System.out.println(c);
}
}

@Documented

@Documented 如图所示, 在用javadoc命令生成API文档后,DBUtil的文档里会出现该注解说明。

: 使用eclipse把项目中的.java文件导成API文档步骤:

  1. 选中项目
  2. 点开菜单File
  3. 点击Export
  4. 点开java->javadoc->点next
  5. 点finish

@Repeatable (java1.8 新增)

当没有@Repeatable修饰的时候,注解在同一个位置,只能出现一次,如例所示:

1
2
@JDBCConfig(ip = "127.0.0.1", database = "test", encoding = "UTF-8", loginName = "root", password = "admin")
@JDBCConfig(ip = "127.0.0.1", database = "test", encoding = "UTF-8", loginName = "root", password = "admin")

重复做两次就会报错了。

使用@Repeatable之后,再配合一些其他动作,就可以在同一个地方使用多次了。

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
package util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import anno.JDBCConfig;

@JDBCConfig(ip = "127.0.0.1", database = "test", encoding = "UTF-8", loginName = "root", password = "admin")
@JDBCConfig(ip = "127.0.0.1", database = "test", encoding = "UTF-8", loginName = "root", password = "admin")
public class DBUtil {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException, NoSuchMethodException, SecurityException {
JDBCConfig config = DBUtil.class.getAnnotation(JDBCConfig.class);
System.out.println(config);

String ip = config.ip();
int port = config.port();
String database = config.database();
String encoding = config.encoding();
String loginName = config.loginName();
String password = config.password();

String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s", ip, port, database, encoding);
return DriverManager.getConnection(url, loginName, password);
}

public static void main(String[] args) throws NoSuchMethodException, SecurityException, SQLException {
Connection c = getConnection();
System.out.println(c);
}
}

@Repeatable 运用举例

比如在查找文件后缀名是.java的文件时,我们把部分代码修改为注解,并且使用@Repeatable 这个元注解来表示,文件后缀名的范围可以是java, html, css, js 等等。

为了紧凑起见,把注解作为内部类的形式放在一个文件里。

  1. 注解FileTypes,其value()返回一个FileType数组
  2. 注解FileType,其@Repeatable的值采用FileTypes
  3. 运用注解:在work方法上重复使用多次@FileType注解
  4. 解析注解: 在work方法内,通过反射获取到本方法上的FileType类型的注解数组,然后遍历本数组
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
package annotation;
import static java.lang.annotation.ElementType.METHOD;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class FindFiles {
@Target( METHOD)
@Retention( RetentionPolicy.RUNTIME )
public @interface FileTypes {
FileType[] value();
}

@Target( METHOD )
@Retention( RetentionPolicy.RUNTIME )
@Repeatable( FileTypes.class )
public @interface FileType {
String value();
};

@FileType( ".java" )
@FileType( ".html" )
@FileType( ".css" )
@FileType( ".js" )
public void work(){

try {
FileType[] fileTypes= this.getClass().getMethod("work").getAnnotationsByType(FileType.class);
System.out.println("将从如下后缀名的文件中查找文件内容");
for (FileType fileType : fileTypes) {
System.out.println(fileType.value());
}
System.out.println("查找过程略。。。");
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public static void main(String[] args) {
new FindFiles().work();
}
}

仿Hibernate注解

hibernate两种配置方式

hibernate有两种配置方式,分别是*.hbm.xml 配置方式注解方式。 虽然方式不一样,但是都是用于解决如下问题:

  1. 当前类是否实体类
  2. 对应的表名称
  3. 主键对应哪个属性, 自增长策略是什么,对应字段名称是什么
  4. 非主键属性对应字段名称是什么

接下来,我会做一套仿hibernate的注解,并且在一个实体类Hero上运用这些注解,并通过反射解析这些注解信息,来解决上述的问题

自定义hibernate注解

参考hibernate的 注解配置方式 ,自定义5个注解,分别对应hibernate中用到的注解:

  1. hibernate_annotation.MyEntity 对应 javax.persistence.Entity
  2. hibernate_annotation.MyTable 对应 javax.persistence.Table
  3. hibernate_annotation.MyId 对应 javax.persistence.Id
  4. hibernate_annotation.MyGeneratedValue 对应 javax.persistence.GeneratedValue
  5. hibernate_annotation.MyColumn 对应 javax.persistence.Column
  • MyEntity.java
1
2
3
4
5
6
7
8
9
10
11
12
package hibernate_annotation;

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 MyEntity {

}
  • MyTable.java
1
2
3
4
5
6
7
8
9
10
11
12
package hibernate_annotation;

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 MyTable {
String name();
}
  • MyId.java
1
2
3
4
5
6
7
8
9
10
11
12
package hibernate_annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyId {

}
  • MyGeneratedValue.java
1
2
3
4
5
6
7
8
9
10
11
12
package hibernate_annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyGeneratedValue {
String strategy();
}
  • MyColumn.java
1
2
3
4
5
6
7
8
9
10
11
12
package hibernate_annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyColumn {
String value();
}

运用在Hero对象上

像以注解方式配置Product类那样,在Hero类上运用这些自定义注解:

当注解的方法是value的时候,给这个注解赋值时,本来应该是:

1
@MyColumn(value="name_")

现在可以简略一点,写为

1
@MyColumn("name_")

只有当名称是value的时候可以这样,其他名称如name,stratgy等不行

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
package pojo;

import hibernate_annotation.MyColumn;
import hibernate_annotation.MyEntity;
import hibernate_annotation.MyGeneratedValue;
import hibernate_annotation.MyId;
import hibernate_annotation.MyTable;

@MyEntity
@MyTable(name="hero_")
public class Hero {
private int id;
private String name;
private int damage;
private int armor;

@MyId
@MyGeneratedValue(strategy = "identity")
@MyColumn("id_")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@MyColumn("name_")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@MyColumn("damage_")
public int getDamage() {
return damage;
}
public void setDamage(int damage) {
this.damage = damage;
}
@MyColumn("armor_")
public int getArmor() {
return armor;
}
public void setArmor(int armor) {
this.armor = armor;
}

}

解析注解

创建一个解析类ParseHibernateAnnotation ,获取Hero类上配置的注解信息,其运行结果如图所示。思路如下:

  1. 首先获取Hero.class类对象
  2. 判断本类是否进行了MyEntity 注解
  3. 获取注解 MyTable
  4. 遍历所有的方法,如果某个方法有MyId注解,那么就记录为主键方法primaryKeyMethod
  5. 把主键方法的自增长策略注解MyGeneratedValue和对应的字段注解MyColumn 取出来,并打印
  6. 遍历所有非主键方法,并且有MyColumn注解的方法,打印属性名称和字段名称的对应关系。

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
package test;

import java.lang.reflect.Method;

import hibernate_annotation.MyColumn;
import hibernate_annotation.MyEntity;
import hibernate_annotation.MyGeneratedValue;
import hibernate_annotation.MyId;
import hibernate_annotation.MyTable;
import pojo.Hero;

public class ParseHibernateAnnotation {

public static void main(String[] args) {

Class<Hero> clazz = Hero.class;
MyEntity myEntity = (MyEntity) clazz.getAnnotation(MyEntity.class);
if (null == myEntity) {
System.out.println("Hero类不是实体类");
} else {
System.out.println("Hero类是实体类");
MyTable myTable= (MyTable) clazz.getAnnotation(MyTable.class);
String tableName = myTable.name();
System.out.println("其对应的表名是:" + tableName);
Method[] methods =clazz.getMethods();
Method primaryKeyMethod = null;
for (Method m: methods) {
MyId myId = m.getAnnotation(MyId.class);
if(null!=myId){
primaryKeyMethod = m;
break;
}
}

if(null!=primaryKeyMethod){
System.out.println("找到主键:" + method2attribute( primaryKeyMethod.getName() ));
MyGeneratedValue myGeneratedValue =
primaryKeyMethod.getAnnotation(MyGeneratedValue.class);
System.out.println("其自增长策略是:" +myGeneratedValue.strategy());
MyColumn myColumn = primaryKeyMethod.getAnnotation(MyColumn.class);
System.out.println("对应数据库中的字段是:" +myColumn.value());
}
System.out.println("其他非主键属性分别对应的数据库字段如下:");
for (Method m: methods) {
if(m==primaryKeyMethod){
continue;
}
MyColumn myColumn = m.getAnnotation(MyColumn.class);
//那些setter方法上是没有MyColumn注解的
if(null==myColumn)
continue;
System.out.format("属性: %s\t对应的数据库字段是:%s%n",method2attribute(m.getName()),myColumn.value());
}
}
}

private static String method2attribute(String methodName) {
String result = methodName; ;
result = result.replaceFirst("get", "");
result = result.replaceFirst("is", "");
if(result.length()<=1){
return result.toLowerCase();
}
else{
return result.substring(0,1).toLowerCase() + result.substring(1,result.length());
}
}
}

注解分类

按照作用域分

根据注解的作用域@Retention,注解分为:

  1. RetentionPolicy.SOURCE: Java源文件上的注解
  2. RetentionPolicy.CLASS: Class类文件上的注解
  3. RetentionPolicy.RUNTIME: 运行时的注解

按照来源分

按照注解的来源,也是分为3类:

  1. 内置注解:如@Override ,@Deprecated 等等
  2. 第三方注解:如Hibernate, Struts等等
  3. 自定义注解:如仿hibernate的自定义注解
感谢您的支持!