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

來源:互聯網
上載者:User

為了支援業務的快速變更,往往採用可配置的方式,將商務邏輯的處理部分配置在資料庫中或者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 blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NoSuchFieldException e) {// TODO Auto-generated catch blocke.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 blocke.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 blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}


4、運行結果

總結:

這樣業務的變更不會有很多的.java檔案產生,只會在ConvertFunction.java中不斷的添加自訂的函數,並且將添加的函數配置的檔案或者資料庫中即可生效。而且很多成員變數都能共用。有人會質疑這樣ConvertFunction.java檔案會越來越大,不錯,這是肯定的,各有各的好處,看大家是希望管理多個.java檔案呢,還是只關注一個.java檔案,n個函數。看自己的需求而定,沒有那種最好,只有那種最適合自己的業務。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.