首页 > 文章列表 > Java开发反射机制实例代码分析

Java开发反射机制实例代码分析

java
294 2023-05-06

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");

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

2 - User user = new User();

Class clazz2 = user.getClass();

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打断点,可以看到——

这个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注解的内部属性有如下方式——

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

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

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

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调用成功了。

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

最后,通过反射实现一个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;

    }