java 注解详解

注解(Annotation)简介
Annotation(注解)是JDK5.0 及以后版本引入的一个特性 。注解是java 的一个新的类型(与接口很相似),它与类、接口、枚举是在同一个层次,它们都称作为java 的一个类型(TYPE)。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。它的作用非常的多,例如:进行编译检查、生成说明文档、代码分析等。

JDK提供的几个基本注解
a. @SuppressWarnings
该注解的作用是阻止编译器发出某些警告信息。
它可以有以下参数:
deprecation:过时的类或方法警告。
unchecked:执行了未检查的转换时警告。
fallthrough:当Switch 程序块直接通往下一种情况而没有Break 时的警告。
path:在类路径、源文件路径等中有不存在的路径时的警告。
serial:当在可序列化的类上缺少serialVersionUID 定义时的警告。
finally:任何finally 子句不能完成时的警告。
all:关于以上所有情况的警告。

b. @Deprecated
  该注解的作用是标记某个过时的类或方法。

c. @Override
  该注解用在方法前面,用来标识该方法是重写父类的某个方法。

元注解
a. @Retention
它是被定义在一个注解类的前面,用来说明该注解的生命周期。
它有以下参数:
RetentionPolicy.SOURCE:指定注解只保留在一个源文件当中。
RetentionPolicy.CLASS:指定注解只保留在一个class 文件中。
RetentionPolicy.RUNTIME:指定注解可以保留在程序运行期间。


b. @Target
它是被定义在一个注解类的前面,用来说明该注解可以被声明在哪些元素前。
它有以下参数:
ElementType.TYPE:说明该注解只能被声明在一个类前。
ElementType.FIELD:说明该注解只能被声明在一个类的字段前。
ElementType.METHOD:说明该注解只能被声明在一个类的方法前。

ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE:说明该注解只能声明在一个包名前。


注解的生命周期
   一个注解可以有三个生命周期,它默认的生命周期是保留在一个CLASS 文件,但它也可以由一个@Retetion 的元注解指定它的生命周期。

a. java 源文件
  当在一个注解类前定义了一个@Retetion(RetentionPolicy.SOURCE)的注解,那么说明该注解只保留在一个源文件当中,当编译器将源文件编译成class 文件时,它不会将源文件中定义的注解保留在class 文件中。

b. class 文件中
  当在一个注解类前定义了一个@Retetion(RetentionPolicy.CLASS)的注解,那么说明该注解只保留在一个class 文件当中,当加载class 文件到内存时,虚拟机会将注解去掉,从而在程序中不能访问。

c. 程序运行期间
  当在一个注解类前定义了一个@Retetion(RetentionPolicy.RUNTIME)的注解,那么说明该注解在程序运行期间都会存在内存当中。此时,我们可以通过反射来获得定义在某个类上的所有注解。


a . 注解可以有哪些成员
  注解和接口相似,它只能定义final 静态属性和公共抽象方法。

b. 注解的方法
1.方法前默认会加上public abstract
2.在声明方法时可以定义方法的默认返回值。
  例如:
  String color() default "blue";
  String[] color() default {"blue", "red",......}
3.方法的返回值可以有哪些类型
  8 种基本类型,String、Class、枚举、注解及这些类型的数组。

c. 使用注解(参照下面的注解使用)
注解的使用
  注解的使用分为三个过程。
  定义注解-->声明注解-->得到注解

a. 定义注解(参照上面的注解定义)
b. 声明注解
1. 在哪些元素上声明注解

如果定义注解时没有指定@Target 元注解来限制它的使用范围,那么该注解可以使用在ElementType 枚举指定的任何一个元素前。否则,只能声明在@Target 元注解指定的元素前。
一般形式:
@注解名()

2. 对注解的方法的返回值进行赋值

对于注解中定义的每一个没有默认返回值的方法,在声明注解时必须对它的每一个方法的返回值进行赋值。
一般形式:
@注解名(方法名=方法返回值,、、、、、、)
如果方法返回的是一个数组时,那么将方法返回值写在{}符号里
@注解名(方法名={返回值1,返回值2,、、、、、、},、、、、、、、)

3. 对于只含有value 方法的注解,在声明注解时可以只写返回值。

c. 得到注解
对于生命周期为运行期间的注解,都可以通过反射获得该元素上的注解实例。
1、声明在一个类中的注解
可以通过该类Class 对象的getAnnotation 或getAnnotations 方法获得。
2、声明在一个字段中的注解
通过Field 对象的getAnnotation 或getAnnotations 方法获得
3、声明在一个方法中的注解
通过Method 对象的getAnnotation 或getAnnotations 方法获得

注解可以看成是一个接口,注解实例就是一个实现了该接口的动态代理类。注解大多是用做对某个类、方法、字段进行说明,标识的。以便在程序运行期间我们通过反射获得该字段或方法的注解的实例,来决定该做些什么处理或不该进行什么处理。

-------------------------------------------------------------------------------
一个demo
定义注解

该注解可以验证成员属性是否为空,长度,提供了几种常见的正则匹配,也可以使用自定义的正则去判断属性是否合法,同时可以为该成员提供描述信息。


package org.xdemo.validation.annotation;

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

import org.xdemo.validation.RegexType;

/**
* 数据验证
* @author Goofy
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
public @interface DV {
    
    //是否可以为空
    boolean nullable() default false;
    
    //最大长度
    int maxLength() default 0;
    
    //最小长度
    int minLength() default 0;
    
    //提供几种常用的正则验证
    RegexType regexType() default RegexType.NONE;
    
    //自定义正则验证
    String regexExpression() default "";
    
    //参数或者字段描述,这样能够显示友好的异常信息
    String description() default "";

}
注解的解析


package org.xdemo.validation.annotation.support;

import java.lang.reflect.Field;

import org.xdemo.validation.RegexType;
import org.xdemo.validation.annotation.DV;
import org.xdemo.validation.utils.RegexUtils;
import org.xdemo.validation.utils.StringUtils;

/**
* 注解解析
* @author Goofy
*/
public class ValidateService {
    
    private static DV dv;
    
    public ValidateService() {
        super();
    }
    
    //解析的入口
    public static void valid(Object object) throws Exception{
        //获取object的类型
        Class<? extends Object> clazz=object.getClass();
        //获取该类型声明的成员
        Field[] fields=clazz.getDeclaredFields();
        //遍历属性
        for(Field field:fields){
            //对于private私有化的成员变量,通过setAccessible来修改器访问权限
            field.setAccessible(true);
            validate(field,object);
            //重新设置会私有权限
            field.setAccessible(false);
        }
    }
    
    
    public static void validate(Field field,Object object) throws Exception{

        String description;
        Object value;

        //获取对象的成员的注解信息
        dv=field.getAnnotation(DV.class);
        value=field.get(object);
        
        if(dv==null)return;
        
        description=dv.description().equals("")?field.getName():dv.description();
        
        /*************注解解析工作开始******************/
        if(!dv.nullable()){
            if(value==null||StringUtils.isBlank(value.toString())){
                throw new Exception(description+"不能为空");
            }
        }
        
        if(value.toString().length()>dv.maxLength()&&dv.maxLength()!=0){
            throw new Exception(description+"长度不能超过"+dv.maxLength());
        }
        
        if(value.toString().length()<dv.minLength()&&dv.minLength()!=0){
            throw new Exception(description+"长度不能小于"+dv.minLength());
        }
        
        if(dv.regexType()!=RegexType.NONE){
            switch (dv.regexType()) {
                case NONE:
                    break;
                case SPECIALCHAR:
                    if(RegexUtils.hasSpecialChar(value.toString())){
                        throw new Exception(description+"不能含有特殊字符");
                    }
                    break;
                case CHINESE:
                    if(RegexUtils.isChinese2(value.toString())){
                        throw new Exception(description+"不能含有中文字符");
                    }
                    break;
                case EMAIL:
                    if(!RegexUtils.isEmail(value.toString())){
                        throw new Exception(description+"地址格式不正确");
                    }
                    break;
                case IP:
                    if(!RegexUtils.isIp(value.toString())){
                        throw new Exception(description+"地址格式不正确");
                    }
                    break;
                case NUMBER:
                    if(!RegexUtils.isNumber(value.toString())){
                        throw new Exception(description+"不是数字");
                    }
                    break;
                case PHONENUMBER:
                    if(!RegexUtils.isPhoneNumber(value.toString())){
                        throw new Exception(description+"不是数字");
                    }
                    break;
                default:
                    break;
            }
        }
        
        if(!dv.regexExpression().equals("")){
            if(value.toString().matches(dv.regexExpression())){
                throw new Exception(description+"格式不正确");
            }
        }
        /*************注解解析工作结束******************/
    }
}
用到的几个类

package org.xdemo.validation;

/**
* 常用的数据类型枚举
* @author Goofy
*
*/
public enum RegexType {
    
    NONE,
    SPECIALCHAR,
    CHINESE,
    EMAIL,
    IP,
    NUMBER,
    PHONENUMBER;
    
}
其中正则验证类和字符串工具类请参考以下链接:

SuperUtil之RegexUtils
SuperUtil之StringUtils
使用方法

package org.xdemo.validation.test;

import org.xdemo.validation.RegexType;
import org.xdemo.validation.annotation.DV;

public class User {
    
    @DV(description="用户名",minLength=6,maxLength=32,nullable=false)
    private String userName;
    
    private String password;
    
    @DV(description="邮件地址",nullable=false,regexType=RegexType.EMAIL)
    private String email;
    
    
    public User(){}
    
    public User(String userName, String password, String email) {
        super();
        this.userName = userName;
        this.password = password;
        this.email = email;
    }
    
    
    
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
}
测试代码


package org.xdemo.validation.test;

import org.xdemo.validation.annotation.support.ValidateService;

/**
* @author Goofy
*/
public class Test {
    public static void main(String[] args){
        User user=new User("张三", "xdemo.org", "[email protected]");
        try {
            ValidateService.valid(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        user=new User("zhangsan","xdemo.org","xxx@");
        try {
            ValidateService.valid(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        user=new User("zhangsan","xdemo.org","");
        try {
            ValidateService.valid(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


猜你喜欢

转载自haidaoqi3630.iteye.com/blog/2203375