Java反射机制
一 反射定义
反射是运行时动态获取Class对象信息的方式,Java世界中给定任意的类名便能获取获取类的所有信息,包括构造器、成员变量、方法,Java的反射机制可以在程序的运行时加载、探知和使用编译期未知的类,增强类的功能。反射技术可以大大提高程序的灵活性,使得原本属于静态编译型语言的Java具有了动态语言的特性,因此反射技术常常是许多开源组件的基础。加载完类后,堆内存中将会产生一个Class类型的对象(一个类仅有一个Class对象),这个对象包含了类的结构信息,仿佛一面镜子,透过其得到类的结构,所以被形象的称之为“反射”。
二 Class文件机构及加载过程
Java编译环境中,源代码经过编译器编译生成.Class的二进制文件,该文件内包含了类的结构信息。通过读取本地字节码和网络传输的方式,可以获取类的字节码文件,该文件包含了类的所有信息,Java支持运行时修改甚至生成类的字节码,从而动态的改变类结构。
三 从Class对象中获取信息
Class对象中可以获取类的一切信息,包括构造器、方法、属性等,其中常用方法如下表。
获取Class内部信息
判断自身信息的方法
创建对象
传统方式常通过new关键字来创建对象,利用反射机制可以通过Class clazz=Class.forName(String className); Object instance=clazz.newInstance()来创建指定类型对象,这种方法要求该Class对象对应类有默认构造器。JDBC数据库编程中,常用Class.forName(String driverName)加载数据库驱动,此处应用了反射技术。
获取类信息、成员方法返回类型及参数列表
//获取类信息,打印类名称 Class c = Person.class; System.out.println(c.getName()); //Java世界中,万物皆对象,方法是Method类的对象 //获取所有Public的方法 Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { System.out.println(method.getReturnType() + "," + method.getReturnType().getName()); System.out.println(method.getName() + "("); //获取参数类型得到参数列表 Class[] parameterTypes = method.getParameterTypes(); for (Class parameterType : parameterTypes) { System.out.println(parameterType.getName() + ","); } System.out.println(")"); }
获取成员变量、构造函数及参数列表
//成员变量是Field类的对象,Field类封装了对于成员变量的操作 Field[] fields = c.getDeclaredFields(); //遍历获取成员变量的类型及名称 for (Field field : fields) { System.out.println(field.getType().getName() + "," + field.getName()); } //构造函数是Constructor类的对象,可通过其获得构造函数名称及参数列表 Constructor[] constructors = c.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "("); //获取构造函数参数列表 Class[] parameterTypes = constructor.getParameterTypes(); for (Class type : parameterTypes) { System.out.println(type.getName() + ","); } System.out.println(")"); }
四 反射应用案例
反射机制因其动态灵活性被广泛应用于开源框架中,Spring框架的强大得益于DI和AOP两大核心理念,容器的存在解耦了对象的创建和依赖关系,大大提高程序开发的效率和代码的可维护性。下面自己动手模拟Spring实现简单的一个对象池,对象池读取JSON文件然后创建对象,放入Map中,该过程类似于Spring applicationContext读取配置文件并创建对象的过程,可加深对反射机制的理解。
ObjectPool
public class ObjectPool { private Map<String, Object> pool; //存储对象 private ObjectPool(Map<String, Object> pool) { this.pool = pool; }
//获取对象实例的静态方法 private static Object getInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { return Class.forName(className).newInstance(); } //读取JSON文件 private static JSONArray getObjects(String config) throws IOException { Reader reader = new InputStreamReader(ClassLoader.getSystemResourceAsStream(config)); return JSONObject.parseObject(CharStreams.toString(reader)).getJSONArray("objects"); } // 根据指定的JSON配置文件来初始化对象池 public static ObjectPool init(String config) { try { //获取配置文件的JSON数组 JSONArray objects = getObjects(config); ObjectPool pool = new ObjectPool(new HashMap<String, Object>()); if (objects != null && objects.size() != 0) { for (int i = 0; i < objects.size(); ++i) { JSONObject object = objects.getJSONObject(i); if (object == null || object.size() == 0) { continue; } String id = object.getString("id"); String className = object.getString("class"); pool.putObject(id, getInstance(className)); } } return pool; } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } public Object getObject(String id) { return pool.get(id); } public void putObject(String id, Object object) { pool.put(id, object); } public void clear() { pool.clear(); } }
JSON文件
{ "objects": [ { "id": "user", "class": "com.domain.User" }, { "id": "bean", "class": "com.domain.Bean" } ] }
POJO
public class User { private int id; private String name; private String password; ...set/get方法省略 }
public class Bean { private Boolean usefull; private BigDecimal rate; private String name;
...set/get方法省略
}
测试类
@Test public void mockClassCreate() { ObjectPool pool = ObjectPool.init("config.json"); User user = (User) pool.getObject("user"); System.out.println(user);
Bean bean = (Bean) pool.getObject("bean"); System.out.println(bean); }
以上程序从配置文件中读取JSON文件,并模拟Spring容器完成对象的创建与初始化,当调用 pool.putObject(id, getInstance(className))方法时,利用反射机制动态创建并获取目标对象并将对象放入Map中。
当获取到某个类对应的Class对象之后就可以通过该Class对象的getMethod方法来获取一个Method数组或是Method对象,·获取到Method对象之后可以通过调用invoke方法来调用该Method对象对应的方法;以上案例中Client获取到对象的成员变量都是默认值,现对其功能进行增强,实现通过配置文件完成对象创建时的变量赋值。
JSON配置文件
{ "objects": [ { "id": "user", "class": "com.domain.User", "fields": [ { "name": "id", "value": 101 }, { "name": "name", "value": "zhangsan" }, { "name": "password", "value": "ICy5YqxZB1uWSwcVLSNLcA==" } ] }, { "id": "bean", "class": "com.domain.Bean", "fields": [ { "name": "usefull", "value": true }, { "name": "rate", "value": 3.14 }, { "name": "name", "value": "bean-name" } ] }, { "id": "complexBean", "class": "com.domain.ComplexBean", "fields": [ { "name": "name", "value": "complex-bean-name" }, { "name": "refBean", "ref": "bean" } ] } ] }
以上配置文件中fields描述Bean属性,其中name表示属性名,value表示属性值,ref表示引用一个对象。
Method对象调用invoke方法
/** * @author lfq * @since 7/1/2017 22:00 */ public class ObjectPool { private Map<String, Object> pool; private ObjectPool(Map<String, Object> pool) { this.pool = pool; }
private static JSONArray getObjects(String config) throws IOException { Reader reader = new InputStreamReader(ClassLoader.getSystemResourceAsStream(config)); return JSONObject.parseObject(CharStreams.toString(reader)).getJSONArray("objects"); } private static Object getInstance(String className, JSONArray fields) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { // 配置的Class Class<?> clazz = Class.forName(className); // 目标Class的实例对象 Object targetObject = clazz.newInstance(); if (fields != null && fields.size() != 0) { for (int i = 0; i < fields.size(); ++i) { JSONObject field = fields.getJSONObject(i); // 需要设置的成员变量名 String fieldName = field.getString("name"); // 需要设置的成员变量的值 Object fieldValue; if (field.containsKey("value")) { fieldValue = field.get("value"); } else if (field.containsKey("ref")) { String refBeanId = field.getString("ref"); fieldValue = OBJECTPOOL.getObject(refBeanId); } else { throw new RuntimeException("neither value nor ref"); } String setterName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // 需要设置的成员变量的setter方法 Method setterMethod = clazz.getMethod(setterName, fieldValue.getClass()); // 调用setter方法将值设置进去 setterMethod.invoke(targetObject, fieldValue); } } return targetObject; } private static ObjectPool OBJECTPOOL; // 创建一个对象池的实例(保证是多线程安全的) private static void initSingletonPool() { if (OBJECTPOOL == null) { synchronized (ObjectPool.class) { if (OBJECTPOOL == null) { OBJECTPOOL = new ObjectPool(new ConcurrentHashMap<String, Object>()); } } } } // 根据指定的JSON配置文件来初始化对象池 public static ObjectPool init(String config) { // 初始化pool initSingletonPool(); try { JSONArray objects = getObjects(config); for (int i = 0; objects != null && i < objects.size(); ++i) { JSONObject object = objects.getJSONObject(i); if (object == null || object.size() == 0) { continue; } String id = object.getString("id"); String className = object.getString("class"); // 初始化bean并放入池中 OBJECTPOOL.putObject(id, getInstance(className, object.getJSONArray("fields"))); } return OBJECTPOOL; } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } }
public Object getObject(String id) { return pool.get(id); } public void putObject(String id, Object object) { pool.put(id, object); } public void clear() { pool.clear(); } }
//包含对象成员的复合类 public class ComplexBean { private String name; private Bean refBean; ...set/get方法省略 }
//单元测试类 public class Client { @Test public void client() { ObjectPool pool = ObjectPool.init("config.json"); User user = (User) pool.getObject("id1"); System.out.println(user); Bean bean = (Bean) pool.getObject("id2"); System.out.println(bean); ComplexBean complexBean = (ComplexBean) pool.getObject("id3"); System.out.println(complexBean); } }
Spring框架即利用以上所示原理将对象间的依赖及成员变量放置于配置文件中,读取配置文件并有容器管理创建对象的属性赋值和创建。
总结,反射技术的高效运用可以提高程序运行的灵活性,并获取到Class对象信息,其中Class对象中包含构造器、成员变量、成员方法的一切信息即可以实现运行时对象的创建、方法的调用及方法的赋值。