Java注解浅谈

来源:互联网 发布:电脑怎么连网络机顶盒 编辑:程序博客网 时间:2024/06/06 04:26
       自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 <T extends Annotation> T getAnnotation(Class<T> annotationClass),

如果存在此注释,则返回此元素的指定类型的注释,否则为空。

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 获取classClass 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 blocke.printStackTrace();}//如果值为null或者值为Integer类型并且等于0,则不生成sqlif(fieldValue==null||(fieldValue instanceof Integer && (Integer)fieldValue==0)){continue;}// 4.3 组装sqlbuilder.append(" and ").append(fieldName);//if(fieldValue instanceof String){//生成模糊查询的sqlif(((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基本一致。

五、总结。

   通过本篇文章,我们对注解有一个新的认识!如果你觉得很炫,那么赶紧动手吧!


0 0