随着应用复杂度增加,后期的应用更倾向于模块化、插件化来尽量减少主程序的容量,对此有几种方法来解决:1.使用so来封装共同模块,通过加载库的形式实现代码共享2.使用jar形式跟so一样,不过容易被反编译3.简化代码编写可以使用反射机制和注解来实现,这篇文章来介绍这种方法4.插件,使用sharedUserId来实现共享进程的方式,这部分以后讲解。
通过反射来实现简化代码之前我们需要了解反射机制以及如何实现反射,然后了解注解以及自定义注解,最后通过反射来解释注解,由于内容比较多,下面会分为4部分来说明。
Java语言的反射机制
定义
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及获取调用对象的方法的功能称为Java语言的反射机制。
功能
Java反射机制主要提供了以下的功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
Java Reflection API介绍
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中。
- Class类:代表一个类。
- Field类:代表类的成员变量(成员变量也称为类的属性)。
- Method类:代表类的方法。
- Constructor类:代表类的构造方法。
- Array类:提供了动态创建数组,以及访问数组元素的静态方法。
在DumpMethods类演示了Reflection API的基本作用,它读取命令行参数指定的类名,然后打印这个类所具有的方法信息:
- <span style="font-size:14px;">package com.jwzhangjie.reflection;
- import java.lang.reflect.Method;
- public class DumpMethods {
- public static void main(String args[]) throws Exception{
- //加载 并初始化命令行参数指定的类
- Class classType = Class.forName(args[0]);
- //获取类的所有方法
- Method method[] = classType.getDeclaredMethods();
- for (int i = 0; i < method.length; i++) {
- System.out.println(method[i].toString());
- }
- }
- }
- </span>
运行命令“java com/jwzhangjie/reflection/DumpMethods java.util.Stack”就会显示java.util.Stack类所具有的方法,程序的打印结果如下:
下面我们使用ReflectTester类进一步演示Reflection API的基本使用方法。ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object同样类型的对象,然后把object对象中的所有属性复制到新建的对象中,并将它返回。
这个例子只能复制简单的JavaBean,假定JavaBean的每个属性都有public类型的getXXX()和setXXX()方法。
- <span style="font-size:14px;">package com.jwzhangjie.reflection;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- public class ReflectTester {
- public Object copy(Object object) throws Exception{
- //获取对象类型
- Class classType = object.getClass();
- //获得类的完整名字
- System.out.println("Class:"+classType.getName());
- //通过默认构造方法创建一个新的对象
- Object objectCopy = classType.getConstructor().newInstance();
- //获取对象的所有属性
- Field fields[] = classType.getDeclaredFields();
- for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
- String fieldName = field.getName();
- String firstLetter = fieldName.substring(0, 1).toUpperCase();
- //获得和属性对应的getXXX()方法的名字
- String getMethodName = "get"+firstLetter+fieldName.substring(1);
- //获得和属性对应的setXXX()方法的名字
- String setMethodName = "set"+firstLetter+fieldName.substring(1);
- //获得和属性对应的getXXX()方法
- Method getMethod = classType.getMethod(getMethodName, new Class[]{});
- //获得和属性对应的setXXX()方法
- Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});
- //调用原对象的getXXX()方法
- Object value = getMethod.invoke(object, new Object[]{});
- System.out.println(fieldName+": "+value);
- //调用复制对象的setXXX()方法
- setMethod.invoke(objectCopy, new Object[]{value});
- }
- return objectCopy;
- }
- public static void main(String[] args) throws Exception{
- ReflectTester test = new ReflectTester();
- Customer customer = new Customer("Tom", 21);
- customer.setId(new Long(1));
- Customer customerCopy = (Customer)test.copy(customer);
- System.out.println("Copy infomation:"+customerCopy.getName()+" : "+customerCopy.getAge());
- }
- public static class Customer{
- private Long id;
- private String name;
- private int age;
- public Customer(){}
- public Customer(String name, int age){
- this.name = name;
- this.age = age;
- }
- public Long getId(){return id;}
- public void setId(Long id){this.id = id;}
- public String getName(){return name;}
- public void setName(String name){this.name = name;}
- public int getAge(){return age;}
- public void setAge(int age){this.age = age;}
- }
- }</span>
在这个例子里需要注意几点,例子里面的Customer是一个内部类:
1.内部类Customer的实现,上面的例子里面我修饰Customer使用的是static,调用内部类的另一个方式就是Customer customer = test.new Customer();使用外部类的对象.new来实例化
2.对于上面的例子如果使用外部类的对象.new来实例化Customer对象,那么会出现一个Exception in thread "main" java.lang.NoSuchMethodException: com.jwzhangjie.reflection.ReflectTester$Customer.<init>()的错误,所以这里我使用static来修饰Constomer类
下面我们来分析一下上面例子代码。
ReflectTester类的copy(Object object)方法一次执行以下步骤。
(1)获得对象的类型:
- <span style="font-size:14px;"> Class classType = object.getClass();
- System.out.println("Class:"+classType.getName());</span>
在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API中的核心类,它有以下方法。
- getName():获得类的完整名字。
- getFields():获得类的public类型的属性。
- getDeclaredFields():获得类的所有属性。
- getMethods():获得类的public类型的方法。
- getDeclaredMethod():获得类的所有方法。
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
- getConstrutors():获得类的public类型的构造方法。
- getConstrutor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
(2)通过默认构造方法创建一个新的对象:
- <span style="font-size:14px;">Object objectCopy = classType.getConstructor().newInstance();</span>
以上代码先调用Class类的getConstructor()方法获得一个Constructor对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。
(3)获得对象的所有属性:
- <span style="font-size:14px;">Field fields[] = classType.getDeclaredFields();</span>
Class类的getDeclareFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性。
(4)获得每个属性相应的getXXX()和setXXX()方法,然后执行这些方法,把原来对象的属性复制给新的对象中:
- <span style="font-size:14px;"> for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
- String fieldName = field.getName();
- String firstLetter = fieldName.substring(0, 1).toUpperCase();
- //获得和属性对应的getXXX()方法的名字
- String getMethodName = "get"+firstLetter+fieldName.substring(1);
- //获得和属性对应的setXXX()方法的名字
- String setMethodName = "set"+firstLetter+fieldName.substring(1);
- //获得和属性对应的getXXX()方法
- Method getMethod = classType.getMethod(getMethodName, new Class[]{});
- //获得和属性对应的setXXX()方法
- Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});
- //调用原对象的getXXX()方法
- Object value = getMethod.invoke(object, new Object[]{});
- System.out.println(fieldName+": "+value);
- //调用复制对象的setXXX()方法
- setMethod.invoke(objectCopy, new Object[]{value});
- }</span>
以上代码假定每个属性都有相应的getXXX()和setXXX()方法,并且在方法名中,“get”和"set"的后面一个字母为大写。例如,Customer类的name属性对应getName()和setName()方法。Method类的invoke(Object obj, Object args[])方法用于动态执行一个对象的特定方法,它的第一个obj参数指定具有该方法的对象,第二个args参数指定向该方法传递的参数。
在InvokeTester类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法。
- package com.jwzhangjie.reflection;
- import java.lang.reflect.Method;
- public class InvokeTester {
- public int add(int param1, int param2){
- return param1+param2;
- }
- public String echo(String msg){
- return "echo:"+msg;
- }
- public static void main(String[] args) throws Exception{
- Class classType = InvokeTester.class;
- Object invokeTester = classType.newInstance();
- //调用InvokeTester对象的add()方法
- Method addMethod = classType.getMethod("add", int.class, int.class);
- Object result = addMethod.invoke(invokeTester, 100, 200);
- System.out.println((Integer)result);
- //调用InvokeTester对象的echo()方法
- Method echoMethod = classType.getMethod("echo", String.class);
- result = echoMethod.invoke(invokeTester, "Hello");
- System.out.println((String)result);
- }
- }
add()方法的两个参数为int类型,获得表示add()方法的Method对象的代码如下:
- Method addMethod = classType.getMethod("add", int.class, int.class);
- package com.jwzhangjie.reflection;
- import java.lang.reflect.Array;
- public class ArrayTester1 {
- public static void main(String args[]) throws Exception{
- Class classType = Class.forName("java.lang.String");
- //创建一个长度为10的字符串数组
- Object array = Array.newInstance(classType, 10);
- //把索引位置为5的元素设为"hello"
- Array.set(array, 5, "hello");
- //读取索引位置为5的元素的值
- String s = (String)Array.get(array, 5);
- System.out.println(s);
- }
- }
- package com.jwzhangjie.reflection;
- import java.lang.reflect.Array;
- public class ArrayTester2 {
- public static void main(String[] args) throws Exception{
- int dims[] = new int[]{5, 10, 15};
- Object array = Array.newInstance(Integer.TYPE, dims);
- //使用arrayObj引用array[3]
- Object arrayObj = Array.get(array, 3);
- Class cls = arrayObj.getClass().getComponentType();
- System.out.println(cls);
- //使用arrayObj引用array[3][5]
- arrayObj = Array.get(arrayObj, 5);
- //把元素array[3][5][10]设为37
- Array.setInt(arrayObj, 10, 37);
- int arrayCast[][][] = (int[][][])array;
- System.out.println(arrayCast[3][5][10]);
- }
- }
Java反射的基本使用如上。