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; }
总结
到此这篇关于Java反射机制的文章就介绍到这了,更多相关Java开发反射机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!