Java注解浅谈
自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分。注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解。
Java SE5内置了三种标准注解:
@Override,表示当前的方法定义将覆盖超类中的方法。
@Deprecated,使用了注解为它的元素编译器将发出警告,因为注解@Deprecated是不赞成使用的代码,被弃用的代码。
@SuppressWarnings,关闭不当编译器警告信息。
上面这三个注解多少我们都会在写代码的时候遇到。Java还提供了4中注解,专门负责新注解的创建。即元注解。
一.元注解。Java5.0定义的4种元注解:
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
下面,分别看看这4种注解的详细说明。
1.1 @Target。(注解用于什么地方)
@Target说明了Annotation所修饰的对象范围,注解用于什么地方。如果不明确指出,该注解可以放在任何地方。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类、接口(包括注解类型)或enum声明
1.2 @Retention。(什么时候使用该注解。定义该注解的生命周期)
@Retention定义了该Annotation被保留的时间长短。表示需要在什么级别保存该注解信息。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
SOURCE: 注解将被编译器丢弃(在源文件中有效,即源文件保留)
CLASS: 注解在class文件中可用,但会被VM丢弃(在class文件中有效,即class保留)
RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息(在运行时有效,即运行时保留)
1.3 @Documented。(将注解包含在Javadoc中)
@Documented一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。因此可以被javadoc此类的工具文档化。@Documented是一个标记注解,没有成员。有关标记注解,下文有说明。
1.4 @Inherited。(是否允许子类继承该注解)
@Inherited阐述了某个被标注的类型是被继承的。@Inherited是一个标记注解,没有成员。有关标记注解,下文有说明。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
二. 自定义注解。
使用@interface自定义注解。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
定义注解格式:
public @interface 注解名 {定义体}
注解参数的可支持数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组
Annotation类型里面的参数该怎么设定:
第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;
第二,参数成员数据类型如上所述;
第三,如果只有一个参数成员,最好把参数名称设为”value”。
实例代码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
}
ps: 1. 没有元素的注解称为标记注解,上面的@Table就是一个标记注解。
2. 如果只有一个参数成员,最好把参数名称设为"value",例如,
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
在使用注解时,可以不写属性名和等于号,直接写值即可,例如可以这么写@Table(“user”)。
自定义完注解后,最核心的东西便是要实现注解解析!注解解析主要是通过反射机制实现的。有关反射机制,可以看这篇文章,Java反射浅谈。此处不再详说。
三. 注解解析。
首先,看看JDK提供的有关注解相关的API方法,
3.1 isAnnotationPresent(Class<? extends Annotation> annotationClass),
如果指定的注解位于此元素上,则返回‘true’,否则返回‘false’。
3.2
如果存在此注释,则返回此元素的指定类型的注释,否则为空。
3.3 Annotation[] getAnnotations(),
返回此元素上的所有注释。如果这个元素上没有注释,则返回值是一个长度为0的数组。此方法的调用方可以自由修改返回的数组;它将对返回给其他调用方的数组没有影响。
3.4 Annotation[] getDeclaredAnnotations(),
返回直接在该元素上的注释。此方法忽略了继承的注释。如果在这个元素上没有直接存在的注释,返回值是一个长度为0的数组。此方法的调用方可以自由修改返回的数组;它将对返回给其他调用方的数组没有影响。
下文,结合具体的场景,来实现注解以及注解解析。
从原理上讲,注解解析器就是通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理。
四. 注解实战。
打算使用注解生成SQL语句。
4.1 自定义注解。
(1) 自定义一个Table(表)注解,作用域是类,在运行时有效,只有一个属性,所有命名为‘value’,
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
(2) 自定义一个Column(列)注解,作用域是类,在运行时有效,只有一个属性,所有命名为‘value’,
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
4.2 定义一个实体类,使用注解,具体代码如下,
@Table("user")
public class User {
@Column("id")
private int id;
@Column("user_name")
private String userName;
@Column("nick_name")
private String nickName;
@Column("age")
private int age;
@Column("city")
private String city;
@Column("email")
private String email;
@Column("mobile")
private String mobile;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
其中‘@Table(“user”)’表示数据库中表名,而类似‘@Column(“id”) private int id;’表示 该字段(属性)在表中对应的列名。
注解部分已经实现了,接下来就需要实现注解解析,具体代码如下,
4.3 注解解析,具体代码如下,
/**
* 生成sql
* @param f
* @return
*/
private static String query(Object f) {
StringBuilder builder=new StringBuilder();
//1 获取class
Class c=f.getClass();
//是否有Table注解
boolean exists=c.isAnnotationPresent(Table.class);
if(!exists){
return null;
}
//2 获取table的名字
Table t=(Table)c.getAnnotation(Table.class);
String tableName=t.value();
builder.append("select * from ").append(tableName).append(" where 1=1");
//3 遍历所有字段
Field[] fArray=c.getDeclaredFields();
for(Field field:fArray)
{
//4 处理每个字段对应的sql
// //是否有Column注解
boolean fExists=field.isAnnotationPresent(Column.class);
if(!fExists){
continue;
}
// 4.1 拿到字段名
Column column=field.getAnnotation(Column.class);
String columnName=column.value();
//4.2 拿到字段值
String fieldName=field.getName();
String getMethodName="get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Method getMethod;
Object fieldValue = null;
try {
getMethod = c.getMethod(getMethodName);
fieldValue=(Object) getMethod.invoke(f);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//如果值为null或者值为Integer类型并且等于0,则不生成sql
if(fieldValue==null||(fieldValue instanceof Integer && (Integer)fieldValue==0)){
continue;
}
// 4.3 组装sql
builder.append(" and ").append(fieldName);
//
if(fieldValue instanceof String){
//生成模糊查询的sql
if(((String) fieldValue).contains(",")){
String[] values=((String) fieldValue).split(",");
builder.append(" in(");
for (String v:values) {
builder.append("'").append(v).append("'").append(",");
}
builder.deleteCharAt(builder.length()-1);
builder.append(")");
}else{
builder.append("=").append("'").append(fieldValue).append("'");
}
}else if(fieldValue instanceof Integer){
builder.append("=").append(fieldValue);
}
}
return builder.toString();
}
代码都有注释,不多说了。下面,我们测试一下这个注解解析器生成的sql,
(1) 测试代码1,
public static void main(String[] args) {
User f1=new User();
f1.setId(10);
User f2=new User();
f2.setUserName("xingxing");
User f3=new User();
f3.setEmail("123456789@163.com");
String sql1=query(f1);
String sql2=query(f2);
String sql3=query(f3);
System.out.println(sql1);
System.out.println(sql2);
System.out.println(sql3);
}
运行结果截图如下,
(2) 测试代码2
public static void main(String[] args) {
User f1=new User();
f1.setEmail("123456789@163.com,wangwei@sina.com,zhuhe@qq.com");
String sql1=query(f1);
System.out.println(sql1);
}
运行结果截图如下,
通过注解生成的sql与我们想要的sql基本一致。
五、总结。
通过本篇文章,我们对注解有一个新的认识!如果你觉得很炫,那么赶紧动手吧!
还没有评论,来说两句吧...