java中通过反射获取方法并且调用(getMethod和invoke深入)实践
为了支持业务的快速变更,往往采用可配置的方式,将业务逻辑的处理部分配置在数据库中或者XMl文件里。配置什么,如何配置才更灵活,That's a problem.
以数据库配置为例(xml相同),在数据库中可以配置上java包名+类名,一个类只处理一个功能(符合设计模式中的单一性原则),这样只需要把数据库中的类名读出来,Class.forname("xxxx").newInstance()即可实现,这种方式简单,但会产生大量.java文件,管理一下还是挺麻烦的,并且每个.java文件处理一个单一的功能(即便功能很简单,也会生成一个.java文件),个人觉得有点浪费,并且每个.java文件肯定会有部分重复的地方(如属性变量等),当然如果不嫌烦的话,可以将功能的抽象出来,每写个.java都看下是否需要抽象,无穷尽也!本文讨论的不是这种方式配置,采用配置函数的方式,并且运行配置的函数,来达到相同的目的。
1、先看下我们拥有的函数:
package com.java.reflect; public class ConvertFunction implements IFunction { public final int PRE_ARGS_NUM = 2; //默认参数个数,根据需要自行修改 public final Class<?>[] PRE_ARGS_TYPE = new Class<?>[] {String.class,String.class}; //默认的参数的类型,根据需要自行修改 public int convert_if_exist(String name,String value,String field1,String field2){ //可修改成自个的业务逻辑 System.out.println("name = " + name); System.out.println("value = " + value); System.out.println(field1 + " " + field2); return 0; } public int convert_if_exist(String name,String value,String field1,String field2,String field3){//可修改成自己的业务逻辑 System.out.println("name = " + name); System.out.println("value = " + value); System.out.println(field1 + " " + field2 + " " + field3); return 0; }//要添加函数,均在这个类中添加并配置进配置文件中即可 }
package com.java.reflect; public interface IFunction { }
2、在数据库中的配置(可以改成其他方式)
可以看到数据库中配置了三条记录,仔细点会发现,其实就是1中的两个函数,只是组合了罢了。而且第一条和第二条记录也只是参数不同!!!
接下来只要通过反射,写个通用的代码,来找到并且执行这两个函数,那就万事大吉了。来看下实现:
package com.java.reflect; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.lang.StringUtils; public class Utils { public static boolean initFunctions(IFunction obj,List<Func> dst,String funcStr){ if (StringUtils.isEmpty(funcStr) || StringUtils.isBlank(funcStr)) return true; int PRE_ARGS_NUM = -1; Class<?>[] PRE_ARGS_TYPE = null; try { PRE_ARGS_NUM = obj.getClass().getDeclaredField("PRE_ARGS_NUM").getInt(obj); PRE_ARGS_TYPE = (Class<?>[]) obj.getClass().getDeclaredField("PRE_ARGS_TYPE").get(obj); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } List<String> funcs = getFunctions(funcStr); for (String func : funcs) { String funcName = getFuncName(func); String[] funcParams = getFuncParams(func); Class<?>[] paramsType = new Class[funcParams.length + PRE_ARGS_NUM]; Arrays.fill(paramsType, PRE_ARGS_NUM, paramsType.length, String.class); System.arraycopy(PRE_ARGS_TYPE, 0, paramsType, 0, PRE_ARGS_NUM); try { Method method = obj.getClass().getMethod(funcName, paramsType); //根据函数名 && 参数类型,找到对应的函数 dst.add(new Func(obj, method, PRE_ARGS_NUM, funcParams)); } catch (SecurityException e) { // TODO Auto-generated catch block //LOG.error("Error when parse method " + funcName, e); return false; } catch (NoSuchMethodException e) { // TODO Auto-generated catch block //LOG.error("Error when parse method " + funcName, e); return false; } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return dst.size() > 0; } public static List<String> getFunctions(String funcStr){ List<String> ret = new ArrayList<String>(); if (StringUtils.isEmpty(funcStr) || StringUtils.isBlank(funcStr)) return ret; int preIndex = 0; // 截取函数指针 boolean in = false; // 留着扩展,取函数中的参数 for (int i = 0; i <= funcStr.length();) { if (!in && (i == funcStr.length() || funcStr.charAt(i) == ';') && preIndex != i) { String func = funcStr.substring(preIndex, i).trim(); if (StringUtils.isNotEmpty(func) && StringUtils.isNotBlank(func)) { ret.add(func); } i = i + 1; preIndex = i; continue; } if (i < funcStr.length() && funcStr.charAt(i) == '\"' && (i - 1 < 0 || funcStr.charAt(i - 1) != '\\')) in ^= true; i = i + 1; } return ret; } public static String[] getFuncParams(String func) { int idx = func.indexOf('('); if (idx != -1) { String params = func.substring(idx + 1, func.length() - 1); int count = getParamsCount(params); String[] args = new String[count]; for (int i = 0, j = 0; i < count && j < params.length();) { boolean in = false; for (int k = j; k <= params.length(); k++) { if (!in && (k == params.length() || params.charAt(k) == ',')) { args[i] = params.substring(j, k).trim(); if (args[i].startsWith("\"")) args[i] = args[i].substring(1); if (args[i].endsWith("\"")) args[i] = args[i].substring(0, args[i].length() - 1); args[i] = args[i].replaceAll("\\\\\"", "\""); i = i + 1; j = k + 1; break; } if (params.charAt(k) == '\"' && (k - 1 < 0 || params.charAt(k - 1) != '\\')) in ^= true; } } return args; } return new String[0]; } public static String getFuncName(String func) { int idx = func.indexOf('('); if (idx != -1) return func.substring(0, idx).toLowerCase(); return null; } public static int getParamsCount(String params) { if (StringUtils.isEmpty(params) || StringUtils.isBlank(params)) return 0; int cnt = 0; boolean in = false; for (int i = 0; i < params.length(); i++) { if (params.charAt(i) == '\"' && (i - 1 < 0 || params.charAt(i - 1) != '\\')) in ^= true; if (!in && params.charAt(i) == ',') cnt++; } return cnt + 1; } }
package com.java.reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Func { private IFunction _obj; private Method _method; private Object[] _args; //函数需要的参数,包含两部分(1、默认的参数个数及类型;2、数据库配置中的参数个数及类型) private int _preArgsNum; public Func(IFunction obj, Method method, int preArgsNum, String... args) { this._obj = obj; this._method = method; this._preArgsNum = preArgsNum; this._args = new Object[args.length + preArgsNum]; System.arraycopy(args, 0, this._args, preArgsNum, args.length); //保存数据库中配置的参数个数及类型 } public Object call(Object... args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { if (args.length != this._preArgsNum) throw new IllegalArgumentException("Illegal number of the arguments, need " + this._preArgsNum + " but " + args.length + "."); System.arraycopy(args, 0, this._args, 0, args.length); //保存默认的参数个数及类型 return this._method.invoke(this._obj, this._args); //调用并运行配置中的函数 } }
3、使用(非常简单,main调用下即可)
package com.java.reflect; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; public class ReflectMethod { public static void main(String[] args) { ConvertFunction convert = new ConvertFunction(); List<Func> dst = new ArrayList<Func>(); String funcStr = "convert_if_exist("field1","field2");convert_if_exist("field1\","field2","field3");";//假设从数据库中读出出来了 Utils.initFunctions(convert, dst, funcStr); for(int i = 0;i<dst.size();i++){ try { dst.get(i).call("defaultKey","defaultValue");//调用,默认的两个参数此时传入,和数据库配置中的field1,field2无关,视业务而定 } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
4、运行结果
总结:
这样业务的变更不会有很多的.java文件产生,只会在ConvertFunction.java中不断的添加自定义的函数,并且将添加的函数配置的文件或者数据库中即可生效。而且很多成员变量都能共用!有人会质疑这样ConvertFunction.java文件会越来越大,不错,这是肯定的,各有各的好处,看大家是希望管理多个.java文件呢,还是只关注一个.java文件,n个函数。看自己的需求而定,没有那种最好,只有那种最适合自己的业务。