首页 > 技术文章 > 20.java自定义注解

wlxslsb 2019-05-23 16:55 原文

1.注解
含义:注解是作用于类、方法和参数等前面的特定元素,为这个方法增加的说明或功能,可自定义.
原理:通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理.
创建注解:在指定项目下点击右键 new -- > Annotation --> 输入注解名,如 public @interface Author{}
     在注解中创建参数,形式为: 访问修饰符 参数类型 参数名() [default 参数值],如 public String value()
     访问修饰符只能使用public和默认权限修饰符.
     参数类型支持基本类型、String、Class、enum、Annotation、以上类型的数组.
     在注解前加上元注解@Retention、@Target、@Documented、@Inherited.
使用注解:通过@符号加上注解名,如@Author
     在注解名后的括号内输入参数与参数值,多个参数用逗号隔开,如@Author(value = "yt",age=18)
     一个参数为数组类型时,值用{值,值}的形式,如 @Author(hobby={"run","jump"})
     如果注解只有一个参数且名为value,那么在使用的时候可以直接指定属性值,如@Author("yt")
     注解参数必须有确定的值,要么在定义的时候给默认值,要么在使用注解的时候指定参数值.

2.元注解:
@Retention 该注解的保留策略,参数值如下
  RetentionPolicy.SOURCE -- 注解仅存在于源码中,在class字节码文件中不包含
  RetentionPolicy.CLASS -- 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
  RetentionPolicy.RUNTIME -- 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target 该注解的作用目标 ,参数值数组如下
  ElementType.TYPE -- 接口、类、枚举、注解
  ElementType.FIELD -- 字段、枚举的常量
  ElementType.METHOD -- 方法
  ElementType.PACKAGE -- 包
  ElementType.PARAMETER -- 方法参数
  ElementType.CONSTRUCTOR -- 构造函数
  ElementType.LOCAL_VARIABLE -- 局部变量
  ElementType.ANNOTATION_TYPE -- 注解
@Documented 该注解将包含在javadoc中
@Inherited 该注解可以被继承

3.注解解析器:用来解析自定义注解。

 

实例:自定义注解、使用、解析

Author.java
/**
 * 
 * @Descript TODO (作者注解)
 * @author Administrator
 * @date 2019年5月23日
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Inherited
public @interface Author {
    public int age() default 0;
    public String name() default "";
    public String tel() default "";
    public SexType sex() default SexType.SECRET;
    public String[] hobby() default "";
    public String createDate() default "";
    public String address() default "湖南省长沙市岳麓区";
}
SexType.java
public enum SexType {
    
    BOY,
    
    GIRL,
    
    SECRET
}
DefaultIntValue.java
/**
 * 
 * @Descript TODO (整型默认值注解)
 * @author Administrator
 * @date 2019年5月23日
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Documented
@Inherited
public @interface DefaultIntValue {
    int value();
}
DefaultStringValue.java
/**
 * 
 * @Descript TODO (字符串默认值注解)
 * @author Administrator
 * @date 2019年5月23日
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Documented
@Inherited
public @interface DefaultStringValue {
    String value();
}
CheckParameter.java
/**
 * 
 * @Descript TODO (校验方法参数注解)
 * @author Administrator
 * @date 2019年5月23日
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
@Inherited
public @interface CheckParameter {
    int maxLength();
    int minLength() default 0;
    String description() default "";
}

  Person.java

/**
 * 
 * @Descript TODO (注解作用的类)
 * @author Administrator
 * @date 2019年5月23日
 *
 */
public class Person {

    @DefaultStringValue("汤姆")
    private String name;
    
    @DefaultIntValue(2)
    private int age;
    
    @DefaultIntValue(56)
    private int weight;
    
    @DefaultIntValue(168)
    private int height;

    @Author(name="bushiren",age=18,tel="13787216345",createDate="2019-05-22 14:03:05",sex=SexType.BOY,hobby= {"篮球","LOL"})
    public void eat(String food) {
        System.out.println(new Date() + ":" + this.name + " is eating " + food);
    }
    
    public void sing(@CheckParameter(minLength=5,maxLength = 100,description="歌曲名称长度在5~100之间") String songName) {
        System.out.println(new Date() + ":" + this.name + " is singing " + songName);
    }

    @Author(name="woshiren",age=19,tel="13787216345",createDate="2019-05-23 14:03:05",sex=SexType.GIRL,hobby= {"看书","刷剧"})
    @Override
    public String toString() {
        return new Date() + ":" + "Person [name=" + name + ", age=" + age + ", weight=" + weight + ", height=" + height + "]";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}
TestAnnantion.java
package com.wulss.annatation;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;

/**
 * 
 * @Descript TODO (模拟注解解析器)
 * @author Administrator
 * @date 2019年5月23日
 *
 */
public class TestAnnantion {

    public static void main(String[] args) {
        TestAnnantion test = new TestAnnantion();
        
        Person person = (Person)test.getObjectAndSetDefaultValue(Person.class, "把耳朵叫醒");
        
        System.out.println(person.toString());
        
    }
    
    /**
     * 构造一个对象,通过注解给属性赋初值,并执行某些特定的方法
     * @param clazz 类
     * @param checkString 校验的参数值
     * @return 返回一个对象
     */
    public Object getObjectAndSetDefaultValue(Class clazz,String checkString)  {
        DefaultIntValue defaultIntValue = null;
        DefaultStringValue defaultStringValue = null;
        Author author = null;
        CheckParameter checkParameter = null;
        
        try {
            //需要构造出该类的一个对象
            Constructor constructor = clazz.getDeclaredConstructor();
            Object object = constructor.newInstance();
            
            Field[] fields = clazz.getDeclaredFields();
            for(Field field:fields) {
                //默认是不能访问到private修饰的属性,所以通过setter方法给有注解的属性赋值
                if(field.isAnnotationPresent(DefaultIntValue.class)) {
                    defaultIntValue = field.getAnnotation(DefaultIntValue.class);
                    
                    String setName = "set" + TestAnnantion.cap(field.getName());
                    Method setter = clazz.getDeclaredMethod(setName, field.getType());
                    setter.invoke(object, defaultIntValue.value());
                }
                
                if(field.isAnnotationPresent(DefaultStringValue.class)) {
                    defaultStringValue = field.getAnnotation(DefaultStringValue.class);
                    
                    String setName = "set" + TestAnnantion.cap(field.getName());
                    Method setter = clazz.getDeclaredMethod(setName, field.getType());
                    setter.invoke(object, defaultStringValue.value());
                }
            }
            
            Method[] methods = clazz.getDeclaredMethods();
            for(Method method:methods) {
                //将有作者注解的方法上的作者信息打印输出
                if(method.isAnnotationPresent(Author.class)) {
                    author = method.getAnnotation(Author.class);
                    System.out.println(new Date() + ":" + method.getName()
                                    + "方法创建时间:" + author.createDate() + "," 
                                    + "作者姓名:" + author.name() + ","
                                    + "年龄:" + author.age() + ","
                                    + "性别:" + author.sex() + ","
                                    + "手机号:" + author.tel() + ","
                                    + "工作地址:" + author.address()
                                    );
                    
                }
                
                //校验只且仅有一个带有注解的参数的方法的参数注解上的值并执行该方法
                if(method.getParameters().length==1) {
                    Parameter parameter = method.getParameters()[0];

                    if(parameter.isAnnotationPresent(CheckParameter.class)) {
                        checkParameter = parameter.getAnnotation(CheckParameter.class);
                        
                        System.out.println(new Date() + ":" + "开始校验" + method.getName() + "()方法中的参数,校验值为:" + checkString);
                        
                        if(checkString==null || checkString.length() < checkParameter.minLength() || checkString.length() > checkParameter.maxLength()) {
                            System.out.println(new Date() + ":" + "校验不通过," + checkParameter.description());
                            
                        }else {
                            System.out.println(new Date() + ":" + "校验通过,开始执行该方法");
                            method.invoke(object, checkString);
                        }
                    }
                }
            }
            
            return object;
            
        } catch (Exception e) {
            e.printStackTrace();
            
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 首字母大写
     * @param name
     * @return
     */
    public static String cap(String name) {
        return name.substring(0, 1).toUpperCase() + name.substring(1);
    }
}

  执行结果:

 

推荐阅读