Java 反射簡單實例

反射

什麼是反射,反射有什麼用,反射該怎麼用?java

一些概念性的東西,這裏就不細說了,下面主要給出一個很是簡單的反射的調用工具類;spring

後續會提供一個基於Spring框架的反射工具類,主要用於反射代理bean對象,執行相關的方法apache

這樣有什麼好處?json

設想一下,你的工程運行在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 具體的反射執行方法

下面的代碼有幾個缺陷:ui

  • 要求反射代理的方法必須有無參構造函數
  • 對於基本類型和封裝類型處理得不夠友好

(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對象,此外就是可否反射執行一個私有的方法

參數的指定可否更加靈活一些呢?

相關文章
相關標籤/搜索