java注解的实现原理(1)

时间:2021-07-21 17:30:29   收藏:0   阅读:0

java注解的实现原理(1)

注解的本质就是一个继承了Annotation接口的接口

写在前面,在前面总结了java反射和动态代理的一些知识,同时之前没有仔细研究注解这块,只知道注解的实现原理是基于动态代理的,主要作用有一下:

之前的理解一直差不多这种,最近刚好不忙想相似的了解一下java注解的实现原理,在开始看自定义注解中,看到很多博客里面写的都是如下这样:

自定义注解Info.java

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

使用自定义的注解:People.java

public class People{
   @Info("张三") 
   private String name;
    
   public String getName(){
       return this.name;
   } 
    
   public void setName(String name){
       this.name = name;
   }
}

然后一般到这里就完了,但是当我照着这样写了一遍再用测试类来运行生成People类对象并调用getName()方法时,我们并得不到我们设置的张三,同时我也看不到这个动态代理在哪里。为此有翻阅了好久的资料发现注解不是这样用的。我们自定义的注解其实还需要一个中间配置类来配置的我的注解解析。

如下我们自定义了2个注解@LoadProperty 用来配置加载我们的配置文件,@ConfigField用来配置下面的字段赋值。中间用来配合注解解析的类AnnoResolve.java,使用这两个注解的User.java和测试主函数存在的类TestUser.java。具体代码实现如下所示:

@LoadProperty 用来配置加载我们的配置文件

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoadProperty {
    String value(); // 配置文件的路径
}

@ConfigField用来配置下面的字段赋值

import java.lang.annotation.*;

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

使用以上两个注解的User.java

package com.ths.annotaion;

@LoadProperty("D:\\JAVA_HOME\\bevisStudy\\src\\com\\ths\\annotaion\\config.properties")
public class User {
    //在类中使用注解

    @ConfigField("user.id")
    private String id;

    @ConfigField("name")
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

中间配置类AnnoResolve.java

public class AnnoResolve {

    public static <T> void loadProperty(T t){
        // 通过传入的user对象来获取User的Class对象cls
        Class<? extends Object> cls =t.getClass();
        // 通过isAnnotationPresent()方法判断LoadProperty注解是否存在于此元素上面
        boolean hasLoadPropertyAnno = cls.isAnnotationPresent(LoadProperty.class);
        if(hasLoadPropertyAnno){
            //为属性赋值
            try {
                configField(cls,t);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private static <T> void configField(Class<? extends Object> cls,T t) throws IOException, IllegalAccessException {
        // 获取到cls资源上的注解代理类,使用其中的value()方法得到其中的配置文件路径
        String filePath = cls.getAnnotation(LoadProperty.class).value();
        Properties properties = new Properties();
//        InputStream is = AnnoResolve.class.getClassLoader().getResourceAsStream(filePath);
        InputStream is = new FileInputStream(filePath);
        System.out.println(is);
        properties.load(is);
        // 通过反射获取到user上面的字段
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            // 遍历找到字段上含有注解的字段
            boolean hasConfigField = field.isAnnotationPresent(ConfigField.class);
            String fieldValue = null;
            // 如属性上有注解,使用注解的值作为key去配置文件中查找
            if(hasConfigField){
                // 获取注解的值
                Object annoValue = field.getAnnotation(ConfigField.class).value();
                fieldValue = properties.getProperty(annoValue.toString());
            // 如属性上没有值
            }else{
                fieldValue = properties.getProperty(field.getName());
            }
            // 如果是私有成员变量需要设置为true
            field.setAccessible(true);
            field.set(t,fieldValue);
        }
        is.close();
    }

}

测试注解在User中使用的TestUser.java

public class TestUser {

    public static void main(String[] args) {
        User user = new User();
        AnnoResolve.loadProperty(user);
        System.out.println(user.getId());
        System.out.println(user.getName());
    }
}

其中还要写一个config.properties配置文件

技术图片

其所有在的位置就是在User中需要在注解上传入的值。通过运行TestUser就可以得到如下结果:

技术图片

到此说明通过注解的方法,我们确实给我们创建的User对象赋了我们在配置文件中设置的字符,自定义注解使用成功,下面我们就详细分析一下定义注解的一个过程:

以上只是自定义注解的使用整体理解,对于其中的更近一层的细究在后面继续进行。如在getAnnotation()在java源码中是如何生成动态代理中。在程序使用debug你会发现通过getAnnotation()返回的对象属性是$Prxoy1,不想继续往下看,这样记住返回出来的值就是一个该注解的一个动态代理的代理类。

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!