Java 反射简单实例
反射
什么是反射,反射有什么用,反射该怎么用?
一些概念性的东西,这里就不细说了,下面主要给出一个非常简单的反射的调用工具类;
后续会提供一个基于Spring框架的反射工具类,主要用于反射代理bean对象,执行相关的方法
这样有什么好处?
设想一下,你的工程运行在Spring框架上,你需要实时查看某些方法的返回结果的时候,可以怎么办?
在工程上开一个端口,接手外部传入的参数,利用反射去执行某些类的方法,并将结果打印出来,这样的工具是不是很赞?
一个实例工程
1. Params 类
反射相关的信息(类名,方法名,参数对象)---》 有了这些东西才能唯一的指定要执行的目的
传入给程序的是一个String类型的参数,对其进行解析,获取传说
2. ParamUtil 即params 的解析工具,用于获取上面的参数
传入给程序的是一个String类型的参数,对其进行解析,获取params对象; 还有一个方法则是对方法的参数进行解析
这里对于基本类型和封装类型的处理比较初级,可以尝试让两者可以兼容,灵活的处理一些方法
package com.mushroom.hui.common.invoke;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Created by hui on 16/4/10.
*/
public class ParamUtil {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ParamUtil.class);
/**
* 将传入的参数,转换为Params对象
* @param strPamras {"target":"invoke","class":"com.mushroom.hui.test.biz.Calculate","method":"add","parameter":"10"}
* @return
*/
public static Params buildParams(String strPamras) {
if (StringUtils.isBlank(strPamras)) {
return null;
}
try {
Map<String, String> map = JSON.parseObject(strPamras, Map.class);
if (CollectionUtils.isEmpty(map)) {
return null;
}
Params params = new Params();
params.setTarget(map.get("target"));
params.setCls(map.get("class"));
params.setMethod(map.get("method"));
String args = map.get("params");
if (StringUtils.isBlank(args)) {
return params;
}
Map<String, String> argMap = JSON.parseObject(args, Map.class);
params.setParams(argMap);
return params;
}catch (Exception e) {
logger.error("parse params to object error!");
logger.error("Exception: {}", e);
return null;
}
}
/**
* 获取反射的方法对应参数信息
* @param params 传入的反射信息
* @param argTypes 参数类型
* @param argValues 参数value
* @return true 表示解析成功; false 表示解析失败
*/
public static boolean buildArgInfos(Params params, Class<?>[] argTypes, Object[] argValues) {
Map<String, String> argus = params.getParams();
try {
Class<?> clz;
String value;
int index = 0;
for (Map.Entry<String, String> arg : argus.entrySet()) {
clz = getBaseClass(arg.getKey());
argTypes[index] = clz;
value = arg.getValue();
argValues[index++] = JSON.parseObject(value, clz);
}
return true;
} catch (Exception e) {
return false;
}
}
private static Map<String, Class<?>> baseClass;
static {
baseClass = new HashMap<>(8);
baseClass.put("short", short.class);
baseClass.put("int", int.class);
baseClass.put("long", long.class);
baseClass.put("byte", byte.class);
baseClass.put("float", float.class);
baseClass.put("double", double.class);
baseClass.put("boolean", boolean.class);
baseClass.put("char", char.class);
}
private static Class<?> getBaseClass(String arg) throws ClassNotFoundException {
if (StringUtils.isBlank(arg)) {
return null;
}
if (baseClass.containsKey(arg)) {
return baseClass.get(arg);
} else {
return Class.forName(arg);
}
}
}
3. InvokeUtil 具体的反射执行方法
下面的代码有几个缺陷:
- 要求反射代理的方法必须有无参构造函数
- 对于基本类型和封装类型处理得不够友好
(add(int, int)与add(Integer, Integer) 被认为两个不同的方法,其实一般也确实是两种不同的方法,很明显的与我们的认知是不一样的)
package com.mushroom.hui.common.invoke;
import java.lang.reflect.Method;
/**
* Created by hui on 16/4/10.
*/
public class InvokeUtil {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(InvokeUtil.class);
public static Object invoke(String strParams) {
Params params = ParamUtil.buildParams(strParams);
if (params == null || ! params.isValiad()) {
return null;
}
try {
Class clz = Class.forName(params.getCls());
Object obj = clz.newInstance(); // 无参构造函数必须要有
int size = params.getParams().size();
if (size == 0) { // 没有参数
Method method = clz.getMethod(params.getMethod());
Object ans = method.invoke(obj);
return ans;
}
Class<?>[] argTypes = new Class<?>[size];
Object[] argValues = new Object[size];
if (!ParamUtil.buildArgInfos(params, argTypes, argValues)) {
return null;
}
Method method = clz.getMethod(params.getMethod(), argTypes);
Object ans = method.invoke(obj, argValues);
return ans;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
测试方法
package com.mushroom.hui.test.biz;
import java.util.List;
/**
* Created by yihui on 16/4/10.
*/
public class Calculate {
public int add(int num) {
return num << 2;
}
public String array(List<String> list, int size) {
String str = list.toString();
return str + " size: " + size;
}
}
package com.mushroom.hui.test.invoke;
import com.alibaba.fastjson.JSON;
import com.mushroom.hui.common.invoke.InvokeUtil;
import com.mushroom.hui.common.invoke.ParamUtil;
import com.mushroom.hui.common.invoke.Params;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by hui on 16/4/10.
*/
public class InvokeTest {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(InvokeTest.class);
@Test
public void invokeTest() {
String clz = "com.mushroom.hui.test.biz.Calculate";
String mtd = "add";
try {
Class cls = Class.forName(clz);
Method[] methods = cls.getDeclaredMethods();
Method method = cls.getMethod(mtd, int.class);
Object ans = method.invoke(cls.newInstance(), 20);
logger.info("The ans is : {}", ans);
} catch (Exception e) {
e.printStackTrace();
logger.warn("e: {}", e);
}
}
private String getAddArg() {
Map<String, String> map = new HashMap<>(3);
map.put("target", "invoke");
map.put("class", "com.mushroom.hui.test.biz.Calculate");
map.put("method", "add");
Map<String, String> args = new HashMap<>();
args.put("java.lang.Integer", "10");
map.put("params", JSON.toJSONString(args));
String strParams = JSON.toJSONString(map);
System.out.println(strParams);
return strParams;
}
private String getArrayArg() {
Map<String, String> map = new HashMap<>(3);
map.put("target", "invoke");
map.put("class", "com.mushroom.hui.test.biz.Calculate");
map.put("method", "array");
List<String> list = new ArrayList<>();
list.add("123");
list.add("hello");
list.add("world");
Map<String, String> args = new HashMap<>();
args.put("java.util.List", JSON.toJSONString(list));
args.put("int", "100");
map.put("params", JSON.toJSONString(args));
String strParams = JSON.toJSONString(map);
System.out.println(strParams);
return strParams;
}
@Test
public void paramsTest() {
// invoke
Object obj = InvokeUtil.invoke(getAddArg());
System.out.println(obj);
obj = InvokeUtil.invoke(getArrayArg());
System.out.println(obj);
}
}
实例场景 & 代码分析
上面贴出了代码,但是这些代码是干嘛用的,为什么要这么用,这样拥有什么好处呢,又可以用在什么地方呢
从上面的代码出发,一个一个分析
1. Params 类
Params类中存储了一些有意思的东西,我们主要的聚焦点放在 cls,method和params上,这三个也是我们必须的参数
- cls: 表示的是要反射调用的类,比如
com.mushroom.hui.test.biz.Calculate
, 全路径的类名 - method:待反射类中的方法名
- params:是一个map,存的是方法参数的value以及类型
有上面的三个东西,就可以利用jdk的放射相关类来完成调用,大致的流程是:
- 根据 cls 可以创建一个指定类的对象
- 根据method和cls以及参数类型来获取method对象
- 通过执行method的invoke方法就可以完成改方法的调用
2. ParamUtil 类
其实通过上面的说明,我们也就知道实现反射的大致流程了,util类则只是一个工具类,将String封装为上面的Params对象,解析参数的类型和value
我们设定的传入字符串形式的参数,其格式为:
{
"target": "invoke",
"class": "com.mushroom.hui.test.biz.Calculate",
"method": "array",
"params": {
"int": "100",
"java.util.List": [
"123",
"hello",
"world"
]
}
}
从json字符串反序列化,从上面的结构中可以很容易的创建一个Parmas对象,对此我们可以很容易的get到 class, method
而对于方法的参数 params,则稍稍有点特殊
因为在具体的确定Method对象的时候,我们需要知道参数的类型;而在执行方法的时候,我们则是需要参数的value
因此,我们需要从上面的json串中获得 参数类型列表 & 参数value列表
也就是com.mushroom.hui.common.invoke.ParamUtil#buildArgInfos
的目的,具体怎么玩的可以看上面的代码
需要注意的一点是基本类型和封装类型之间是有区别的,特别是在获取Method对象的时候
todo:如何优化一下基本类型和封装类型,保证两者最终的效果一致?
3. InvokeUtil 具体的反射调用工具类
这个类的作用其实很清晰了,传入参数,返回反射调用的结果,基本流程就是上面列的:
- 根据Json字符串获取Params对象
- 根绝Params对象 创建反射实例对象;方法的Method对象,参数类型&参数值
- 具体执行
- 返回结果
注意一下没有参数和有参数是区分开来的,why?
4. Test类
上面三个类完成了一个简单的反射工具,那么如何进行测试呢?
首先是构造一个参数,然后调用 InvokeUtil的方法即可,这里没什么技术点,就不多说了
5. 这个东西有什么用?
框架层面对于反射用的比较多,这个就不多说了;我们接下来给一个简单的应用场景
假设你有一个java应用,对外提供rpc接口,假设现在某一个接口返回的数据不对了,那么你可以怎么办,远程debug(线程阻塞,会被打死的)
用反射就是一个思路,开一个端口,接受请求,将要执行的对象的方法和参数丢进去,查看输出结果,这样就不会局限于rpc接口的调用了,可以愉快的调用各种内部接口了
后续
spring框架是有一个beanFactory, 可以用这个东西来创建一个bean对象,此外就是能否反射执行一个私有的方法
参数的指定能否更加灵活一些呢?
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 使用libimobiledevice&ifuse提取iOS沙盒文件
- 你不知道的LinkedList(二):LinkedList的增删操作真的会比ArrayList快吗
- jupyter notebook中的魔法命令%run和%timeit
- js 常用正则
- 非常实用的 Python 技巧
- JavaScript中arguments
- js通过class name获得元素
- 基于JavaScript的rce反弹shell漏洞
- 《剑指offer》第11天:矩形覆盖
- 《剑指offer》第12天:旋转数组的最小数字
- 不同版本的Google Analytics做跨站跟踪
- 记一次 base64 图片存储引发后端查询接口性能断崖式下降问题全程解析
- JDBC+MySQL实战
- LeetCode 08字符串转整数&&09回文数
- React 17 RC 版发布:无新特性,却有新期待!