文档章节

Java 反射简单实例

小灰灰Blog
 小灰灰Blog
发布于 2016/04/14 23:50
字数 2028
阅读 233
收藏 13

反射

什么是反射,反射有什么用,反射该怎么用?

一些概念性的东西,这里就不细说了,下面主要给出一个非常简单的反射的调用工具类;

后续会提供一个基于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对象,此外就是能否反射执行一个私有的方法

参数的指定能否更加灵活一些呢?

© 著作权归作者所有

小灰灰Blog
粉丝 203
博文 212
码字总数 382524
作品 0
武汉
程序员
私信 提问
Java面试基础篇——第十篇:反射的原理

什么是Java的反射机制?指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能调用它的任意一个方法。这种动态获取信息,以及动态调用对象方法的功能叫Jav...

developlee的潇洒人生
2018/07/24
149
0
【免费】全网独家:详解Java反射机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/silencezwm/article/details/85115991 【免费】全网独家:这是一份非常值得珍藏的Android知识体系!!! 本文...

silencezwm
2018/12/20
0
0
黑马程序员--JDK1.5新特性(二)

----------------------android培训、java培训、期待与您交流!---------------------- Java反射机制定义: Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性、方法...

长平狐
2013/07/01
58
0
最最最常见的Java面试题总结——第二周

String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的? String和StringBuffer、StringBuilder的区别 可变性   String类中使用字符数组:保存字符串,所以String对象是...

Amsour丶
2018/08/13
0
0
云妹总结了一些最最最常见的Java面试题供大家参考

云妹导读:String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的? String和StringBuffer、StringBuilder的区别 可变性 String类中使用字符数组:private final cha...

阿里云科技快讯
2018/08/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS7.6中安装使用fcitx框架

内容目录 一、为什么要使用fcitx?二、安装fcitx框架三、安装搜狗输入法 一、为什么要使用fcitx? Gnome3桌面自带的输入法框架为ibus,而在使用ibus时会时不时出现卡顿无法输入的现象。 搜狗和...

技术训练营
昨天
5
0
《Designing.Data-Intensive.Applications》笔记 四

第九章 一致性与共识 分布式系统最重要的的抽象之一是共识(consensus):让所有的节点对某件事达成一致。 最终一致性(eventual consistency)只提供较弱的保证,需要探索更高的一致性保证(stro...

丰田破产标志
昨天
8
0
docker 使用mysql

1, 进入容器 比如 myslq1 里面进行操作 docker exec -it mysql1 /bin/bash 2. 退出 容器 交互: exit 3. mysql 启动在容器里面,并且 可以本地连接mysql docker run --name mysql1 --env MY...

之渊
昨天
10
0
python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
昨天
6
0
PHP+Ajax微信手机端九宫格抽奖实例

PHP+Ajax结合lottery.js制作的一款微信手机端九宫格抽奖实例,抽奖完成后有收货地址添加表单出现。支持可以设置中奖概率等。 奖品列表 <div class="lottery_list clearfix" id="lottery"> ......

ymkjs1990
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部