通过注解和反射实现一个简单的@Value注解

Java注解是什么?

Java注解(Annotation)就是一种java标注,并且能够携带数据,是在JDK5.0被引入的。

Java的注解可以标注Java语言中的类、变量、方法、参数、包等等。

值得注意的是:上面所说的Java注解只是一种标注,所以注解需要配合反射来使用才能发挥出强大作用。

注解的成员变量只支持八种基本数据类型(byte、short、int、long、float、double、char、boolean)、String、Class、Enum、Annotation

还有的就是,所有的注解都是Annotation接口的实现类,可以把Annotation接口看成是所有注解的超类

java内置的注解

java.lang包中注解

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

    Target(ElementType.METHOD)//代表这个注解只能标注在方法上
    @Retention(RetentionPolicy.SOURCE)//代表这个注解只在源代码中有效
    public @interface Override {
    }
    
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)//代表这个注解可以保留到运行期间
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})//代表这个注解可以标注在构造方法、成员变量、局部变量、成员方法、包、参数以及类上
    public @interface Deprecated {
    }
    
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。

    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SuppressWarnings {
        String[] value();
    }
    

java.lang.annotation中的注解

下面四个注解也称**元注解(即标注在注解上的注解)**
  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        RetentionPolicy value();
    }
    
  • @Documented - 标记这些注解是否包含在用户文档中。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)//代表这个注解只能标注在注解上
    public @interface Documented {
    }
    
  • @Target - 标记这个注解应该是哪种 Java 成员。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        ElementType[] value();
    }
    
    
  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

自定义一个注解

/**
 * 注解的所有属性都是按照下列类似方法的形式指定的,
 *  比如String name() default "myFirstAnnotation"; 意思就是
 *      一个类型为String 名字为name 默认值为myFirstAnnotation的属性
 */
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyFirstAnnotation {
    
    //注解的名字,可以通过default指定属性的默认值
    String name() default "myFirstAnnotation";
    //注解的描述
    String description();
    //注解的取值
    String value();
    
}

上面就是定义了一个可以标注在类或者注解以及方法上的,保留到运行期的注解。但是也仅仅是定义了一个注解而已,一个注解要发挥它自己的作用,还需要反射的配合。

使用自定义注解

//注解直接标注在对应可以标注的东西上就可以了,然后配合反射就可以定义一个注解的作用
@MyFirstAnnotation(value = "class", description = "my class")
public class TestAnnotation{
    @MyFirstAnnotation(value = "method", description = "my method")
    public void method(){
        
    };
}

什么是反射?

反射是java中的一种机制,通过这种机制我们能够在运行时获取到一个类的一切信息(继承的类、实现的接口、属性、方法等),以及注解信息和注解所携带的数据

通过获取到的类信息,我们可以构造一个新的对象、获取到某个对象的属性值、执行某个对象的方法等。

反射的常用API

获取Class对象的三种方法

  • 对象.getClass():这种方式会执行类的初始化,即类静态属性的赋值操作

  • 类名.class:这种方式不会执行类的初始化

  • Class.forName(全类名):这种方式也会执行类的初始化操作,但是需要知道加载的类的全类名

通过Class对象获取类的常见信息(继承的类、实现的接口等)

//获取这个类上标注的指定的注解类型
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
//获取这个类上标注的的所有注解,包括继承的
public Annotation[] getAnnotations()
//返回直接存在于此元素上的所有注解
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)

//获取继承的父类
public native Class<? super T> getSuperclass()    

//获取这个类实现的所有接口
public Class<?>[] getInterfaces()

//获取全类名
public String getName()
//获取类名
public String getSimpleName() 

通过Class对象获取类的构造方法

//获取指定的public构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取所有的public构造方法
public Constructor<?>[] getConstructors()

//获取当前类声明的指定方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//获取当前类声明的所有构造方法
public Constructor<?>[] getDeclaredConstructors()

通过Class对象获取类的属性

//通过属性名获取类中的public的属性
public Field getField(String name)
//获取指定类中的所有public的属性
public Field[] getFields()

//通过属性名,获取指定类中的直接声明的属性   
public Field getDeclaredField(String name)
//获取指定类中的直接声明的所有属性   
public Field[] getDeclaredFields()

通过Class对象获取类的方法

//通过方法签名来获取类中的public方法
public Method getMethod(String name, Class<?>... parameterTypes)
//获取类中的所有public方法
public Method[] getMethods()

    
//通过方法签名来获取类中直接声明的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//获取类中直接声明的所有方法
public Method[] getDeclaredMethods()

通过注解和反射自己来实现简单@Value注解

定义value注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
    String value();
}

定义pojo User类

public class User {
    @Value("root")
    private String username;

    @Value("12345678")
    private String password;

    @Value("25")
    private String age;

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }

    public User(String username, String password, String age) {
        this.username = username;
        this.password = password;
        this.age = age;
    }

    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 getAge() {
        return age;
    }

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

测试并使用注解

public class TestAnnotationForValue {
    public static User getUser() throws Exception{
        //通过全类名的方式加载类对象
        Class<?> userClass = Class.forName("annotation.User");

        //获取User的无参构造器
        Constructor<?> constructor = userClass.getConstructor();
        //通过无参构造器获取一个对象
        User user = (User)constructor.newInstance();

        //获取User的所有属性
        Field[] fields = userClass.getDeclaredFields();
        for (Field field : fields) {
            //判断属性上是否存在@Value注解
            if(field.isAnnotationPresent(Value.class)){
                //给予私有对象的访问权限
                field.setAccessible(true);
                //获取到属性上指定的注解
                Value valueAnnotation = (Value)field.getAnnotation(Value.class);
                //将注解携带的数据,注入到user对象的属性当中
                field.set(user, valueAnnotation.value());
            }
        }

        return user;
    }

    @Test
    public void test() throws Exception {
        User user = getUser();
        System.out.println(user);
        //User{username='root', password='12345678', age=25}
    }

}

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容