java中通过反射获取方法并且调用(getMethod和invoke深入)实践

news/2024/5/19 3:41:05 标签: 方法, reflect, java, 调用, get, 实践, 反射, 获取
原文地址为: java中通过反射获取方法并且调用(getMethod和invoke深入)实践

为了支持业务的快速变更,往往采用可配置的方式,将业务逻辑的处理部分配置在数据库中或者XMl文件里。配置什么,如何配置才更灵活,That's a problem.

以数据库配置为例(xml相同),在数据库中可以配置上java包名+类名,一个类只处理一个功能(符合设计模式中的单一性原则),这样只需要把数据库中的类名读出来,Class.forname("xxxx").newInstance()即可实现,这种方式简单,但会产生大量.java文件,管理一下还是挺麻烦的,并且每个.java文件处理一个单一的功能(即便功能很简单,也会生成一个.java文件),个人觉得有点浪费,并且每个.java文件肯定会有部分重复的地方(如属性变量等),当然如果不嫌烦的话,可以将功能的抽象出来,每写个.java都看下是否需要抽象,无穷尽也!本文讨论的不是这种方式配置,采用配置函数的方式,并且运行配置的函数,来达到相同的目的。

1、先看下我们拥有的函数:

java">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;
	}//要添加函数,均在这个类中添加并配置进配置文件中即可
}


java">package com.java.reflect;

public interface IFunction {

}


2、在数据库中的配置(可以改成其他方式)



可以看到数据库中配置了三条记录,仔细点会发现,其实就是1中的两个函数,只是组合了罢了。而且第一条和第二条记录也只是参数不同!!!

接下来只要通过反射,写个通用的代码,来找到并且执行这两个函数,那就万事大吉了。来看下实现:

java">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;
	}

}

java">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调用下即可)

java">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个函数。看自己的需求而定,没有那种最好,只有那种最适合自己的业务。



转载请注明本文地址: java中通过反射获取方法并且调用(getMethod和invoke深入)实践

http://www.niftyadmin.cn/n/1167098.html

相关文章

编译libnl时候的问题

编译libnl时候的问题 linux下面的无线配置命令已经更新到iw时代。 很多人还在用iwpriv。 编译iw需要用一个库&#xff1a;libnl。独立的nelink项目。 这个项目从git仓库中拿到的代码&#xff0c;我是没法编译。倒是从http://www.infradead.org/~tgr/libnl/files/libnl-1.1.tar.…

聚类之详解FCM算法原理及应用

原文地址为&#xff1a; 聚类之详解FCM算法原理及应用【之前】 该文的pdf清晰版已被整理上传&#xff0c;方便保存学习&#xff0c;下载地址&#xff1a; https://download.csdn.net/download/on2way/10394655 &#xff08;一&#xff09;原理部分 模糊C均值&#xff08;Fuzzy …

OpenGL绘制函数

#include <windows.h> // Windows的头文件#include <gl\gl.h> // OpenGL32库的头文件#include <gl\glu.h> // GLu32库的头文件#include <gl\glaux.h> // GLaux库的头文件#include <gl\glut.h> // Glut库头文件#include &…

java web开发--------Servlet学习之HelloWorld 从部署到开发全过程

原文地址为&#xff1a; java web开发--------Servlet学习之HelloWorld 从部署到开发全过程今天终于搞出来来了Servlet的HelloWorld&#xff0c;记录下过程。 下图是个整体框架图&#xff1a; 开发流程图&#xff1a; 第一步&#xff1a;配置TOMCAT 在tomcat的安装目录下&#…

Android View系统解析(上)

原文地址为&#xff1a; Android View系统解析(上)注意&#xff1a;上图中的公式有误&#xff0c;正确的应该为&#xff1a; x left translationX y top translationY 转载请注明本文地址&#xff1a; Android View系统解析(上)

sed -i命令详解

原文地址为&#xff1a; sed -i命令详解[rootwww ~]# sed [-nefr] [动作] 选项与参数&#xff1a; -n &#xff1a;使用安静(silent)模式。在一般 sed 的用法中&#xff0c;所有来自 STDIN 的数据一般都会被列出到终端上。但如果加上 -n 参数后&#xff0c;则只有经过sed 特殊处…

安装程序配置服务器失败。参考服务器错误日志和C:\WINDOWS\sqlstp.log

原文地址为&#xff1a; 安装程序配置服务器失败。参考服务器错误日志和C:\WINDOWS\sqlstp.log安装好SQL Server 2000安装失败提示&#xff1a;安装程序配置服务器失败。参考服务器错误日志和C:\WINDOWS\sqlstp.log 解决方法&#xff1a; Google了一下可能是安装目录有文件&…

java按字节截取带有汉字的字符串的解法

原文地址为&#xff1a; java按字节截取带有汉字的字符串的解法由于接口使用的oracle字段长度为固定字节数&#xff0c;然后传进来的字符串估计比数据库字段的总字节数要大&#xff0c;那么截取小于数据库字节数的字符串。 自己参考网上的例子&#xff0c;整了个递归调用就可以…