• Java反射机制开发经验总结


    我在实际项目当中有经常用到反射机制,故而将学会的反射用法做一些汇总笔记,当做以后复盘所用。

    存在这样一个类:

    package com.example.demo;
    import com.alibaba.fastjson.annotation.JSONField;
    public class User {
        private String name;
        @Value( value ="age_a")
        private String age;
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
         public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    }
    
    

    一、创建Class的三种方式

    1 - Class clazz = Class.forName("com.example.demo.User");

    image

    注意一点,这里的forName("xxx")的类名需要全名,且为接口或类,否则加载不了。

    2 - User user = new User();

    Class clazz2 = user.getClass();

    image

    3 - Class clazz3 = User.class;

    以上三种方式,都可以获取到类User的Class对象,通过Class,即可以开始玩反射了。


    二、反射获取类的所有属性和属性类型

    Class clazz = User.class;
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        System.out.println("属性名:"+field.getName());
        System.out.println("属性的类型:"+field.getGenericType().getTypeName());
    }
    

    打印输出User的属性和属性类型——

    属性名:name
    属性的类型:java.lang.String
    属性名:age
    属性的类型:java.lang.String

    利用反射获取到类的字段属性后,是不是可以利用反射来创建一个对象呢?答案是肯定的。

    例如,可以类似下面代码,通过反射得到的字段属性,进而创建一个对象。

    Map<String,Object> fileds = new HashMap<>();
    fileds.put("name","张三");
    fileds.put("age","10");
    Object o = User.class.newInstance();
     Field[] fields = o.getClass().getDeclaredFields();
     for (Field field : fields) {
         //设置后可用反射访问访问私有变量
         field.setAccessible(true);
         //通过反射给属性赋值
         field.set(o,fileds.get(field.getName()));
     }
     User user1 = (User) o;
     System.out.println(user1.toString());
    

    什么场景下可能需要这样做的呢?像一些内部数据与外部数据字段的映射,就可以通过类似的字段反射方式,将源数据映射给目标数据,进而得到可以插入数据库的目标对象。


    三、反射动态修改类属性的注解值

    注意一点,我们在设置User类时,对其中一个字段加了注解:@Value( value ="age_a")。这是一种设置值的注解,既然是设置值,是否还可以在代码运行过程中,根据不同情况来动态修改呢?

    字段上的注解,其实都存放在一个memberValues属性里,这是一个map,可以这样来获取——

    Field[] fields = User.class.getDeclaredFields();
    for (Field field : fields) {
        //设置后可用反射访问访问私有变量
        if ("age".equals(field.getName() )){
            field.setAccessible(true);
           //获取 annotation 这个代理实例所持有的 InvocationHandler
           InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class));
           // 获取 InvocationHandler 的 memberValues 字段
            Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
            memberValues.setAccessible(true);
            Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
            System.out.println(values);
        }
    }
    

    debug打断点,可以看到——

    image

    这个Map<String,Object>存储的是该@注解里的所有属性值,这里,@Value只有一个value属性——

    public @interface Value {
        String value();
    }
    

    若把它换成类似@JSONField(name="age_a"),把上边的代码稍微修改下,如:

    Field[] fields = User.class.getDeclaredFields();
    for (Field field : fields) {
        if ("age".equals(field.getName() )){
            field.setAccessible(true);
              InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(JSONField.class));
    		......
        }
    }
    

    @JSONField注解的内部属性有如下方式——
    image

    再运行刚刚的代码,可以看到,这里Map<String,Object>获取存储到的,便是这个注解里所有的属性与对应的属性值。

    image

    到了这一步,回到先前上边的问题,若要动态改变这个注解的值,怎么处理呢?

    其实,很简单,只需要直接进行值设置就可以了,例如——

    InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class));
    Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
    memberValues.setAccessible(true);
    Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
    values.put("value","new_age");
    memberValues.setAccessible(false);
    

    只是,注意一点是,这里的key需要对应上注解里是属性值。


    四、反射获取类的方法及调用方式

     Object o=User.class.newInstance();
    //通过反射获取到User的setAge方法,后面的String.class表示这个setAge方法的参数类型,若有多个,则按顺序列出
    //同时,若为其他类型,如List,Long,则为List.class,Long.class
     Method m =  (Method) o.getClass().getMethod("setAge",String.class);
     m.invoke(o,"name");
     User user = (User) o;
     System.out.println(user);
    

    打印可见,age已为name,说明setAge调用成功了。

    image
    这类使用场景,在代理当中出现比较多。

    最后,通过反射实现一个Map转成对象的封装工具——

       public Object MapToObject(Object object,Map<String, Object> map) throws IllegalAccessException {
            Class cla =  object.getClass();
            Field[] fields = cla.getDeclaredFields();
            for(Field field:fields){
                field.setAccessible(true);
                if("serialVersionUID".equals(field.getName()))continue;
                if(map.get(field.getName())!=null) {
                    Object value=map.get(field.getName());
                    value=convertValType(value,field.getType());
                    field.set(object, value);
                }
            }
            return object;
        }
    
    
        private static Object convertValType(Object value, Class<?> fieldTypeClass) {
            Object o = null;
            if (Long.class.getName().equals(fieldTypeClass.getName())
                    || long.class.getName().equals(fieldTypeClass.getName())) {
                o = Long.parseLong(value.toString());
            } else if (Integer.class.getName().equals(fieldTypeClass.getName())
                    || int.class.getName().equals(fieldTypeClass.getName())) {
                o = Integer.parseInt(value.toString());
            } else if (Float.class.getName().equals(fieldTypeClass.getName())
                    || float.class.getName().equals(fieldTypeClass.getName())) {
                o = Float.parseFloat(value.toString());
            } else if (Double.class.getName().equals(fieldTypeClass.getName())
                    || double.class.getName().equals(fieldTypeClass.getName())) {
                o = Double.parseDouble(value.toString());
            } else {
                retVal = o;
            }
            return retVal;
        }
    

    欢迎关注公众号,关于思考,关于文化,关于成长——

    image

    作者:朱季谦
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    1031.查询-集合操作
    1030.查询-select子句
    1029.查询-复杂分组grouping子句
    1028.查询-group by和having子句
    1027.查询-where子句
    1026.查询-from子句2
    1025.查询-from子句
    1024.查询-概述
    1023.表-数据操作
    图片隐写之stegsolve使用(转载)
  • 原文地址:https://www.cnblogs.com/zhujiqian/p/14979486.html
Copyright © 2020-2023  润新知