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、在數據庫中的配置(可以改成其他方式)

java中通過反射獲取方法並且調用(getMethod和invoke深入)實踐


可以看到數據庫中配置了三條記錄,仔細點會發現,其實就是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個函數。看自己的需求而定,沒有那種最好,只有那種最適合自己的業務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章