欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java反射,从配置文件中获取类名、方法名、变量、变量类型并进行方法调用

程序员文章站 2022-05-12 15:01:20
...
  1. 给定类和配置文件,利用反射调用方法。
    以一个Point类和一个简单的配置文件为例。
    Point类和配置文件config.properties:
	package com.part3.reflect.point;

/**
 * Author: Sean
 * Date: Created In 21:34 2019/4/15
 * Title:
 * Description:
 * Version: 0.1
 * Update History:
 * [Date][Version][Author] What has been done;
 */

public class Point {
    private int x;
    private int y;
    private String name;

    /**
     * 无参构造函数
     */
    public Point() {

    }

    /**
     * 带x的构造函数
     *
     * @param x 横坐标
     */
    public Point(int x) {
        this.x = x;
    }

    /**
     * 带x和y的构造函数
     *
     * @param x 横坐标
     * @param y 纵坐标
     */
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    /**
     * 带所有参数的构造函数
     *
     * @param x    横坐标
     * @param y    纵坐标
     * @param name 名称
     */
    public Point(int x, int y, String name) {
        this.x = x;
        this.y = y;
        this.name = name;
    }

    /**
     * 移动点
     *
     * @param dx   水平偏移分量
     * @param dy   垂直偏移分量
     * @param name 新的名称
     */
    public void move(int dx, int dy, String name) {
        x += dx;
        y += dy;
        this.name = name;
    }

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + 31;
        result = prime * result + 31;
        return result;
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Point other = (Point) obj;
        return (x == other.getX() && y == other.getY());
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String toString(){
        return "x: " + x + ", y: " + y + ", name: " + name;
    }
}

	classname=com.part3.reflect.point.Point
	method=move
	args=12,24,"武汉"
	argsType=int,int,String
  1. 首先需要解决的问题是读取配置文件并获得相应的数据
    最开始用的是ResourceBundle方法来读取配置文件,但是发现了一个很大的问题就是中文读取出来是乱码,在网上查找了一下,最后用InputStream的方法解决了,具体代码:
    	// 读取properties文件
        Properties properties = new Properties();
        InputStream inputStream = Thread.currentThread()
                .getContextClassLoader()
                .getResourceAsStream("com/part3/reflect/point/config.properties");
        try {
            if (inputStream == null) {
                System.out.println("null input stream");
            } else {
                properties.load(new InputStreamReader
                        (inputStream, StandardCharsets.UTF_8));
            }
        } catch (IOException e) {
            System.out.println("读取properties文件出错!");
            e.printStackTrace();
        }
         // 从properties文件中读取相应的属性
        String sClassname = properties.getProperty("classname");
        String[] sArgs = properties.getProperty("args").split(",");
        String[] sArgsType = properties.getProperty("argsType").split(",");
        String sMethod = properties.getProperty("method");
    
        // 输出检验
        System.out.println(sClassname);
        System.out.println(Arrays.toString(sArgs));
        System.out.println(Arrays.toString(sArgsType));
        System.out.println(sMethod);
    
  2. 下一步需要解决的问题则是根据参数类型获得相应的class
    在java反射中,获取method的常用方法是getMethod,它需要的参数的是方法名和该方法传入参数的类型Class,而我们通过properties读取出来的参数类型是一个字符串数组,因此我们需要对它进行转换,让它返回对应的Class(这里只涉及到了基本类型,如果有其他类型也可以自行添加)。
    java反射,从配置文件中获取类名、方法名、变量、变量类型并进行方法调用
    具体代码如下:
    // 根据获取到的参数类型字符串列表转换为相应的Class列表
        Class[] oArgsType = new Class[sArgsType.length];
        for (int i = 0; i < sArgsType.length; i++) {
            oArgsType[i] = getPrimitiveClass(sArgsType[i]);
            System.out.println(oArgsType[i].getName());
        }
        
     /**
     * 根据字符串获取基本类型的class
     *
     * @param str 基本类型的名称
     * @return 基本类型对应的Class
     */
    private static Class getPrimitiveClass(String str) {
        str = str.toLowerCase();
        switch (str) {
            case "int":
                return int.class;
            case "double":
                return double.class;
            case "float":
                return float.class;
            case "char":
                return char.class;
            case "string":
                return String.class;
            default:
                System.out.println("undefined!");
                return Object.class;
        }
    }
    
  3. 接下来我们还需要把读取到的字符串参数转换为相应的类型数据并以Object数组的形式保存下来
 // 根据参数类型列表将参数转换为相应的类型
        Object[] oArgs = new Object[sArgs.length];
        for (int i = 0; i < sArgs.length; i++) {
            oArgs[i] = transferArguments(sArgsType[i], sArgs[i]);
            System.out.println(oArgs[i]);
        }
        
      /**
     * 根据type将Value转换为相应类型的数据
     *
     * @param type  数据类型
     * @param value 数据
     * @return 相应数据的object
     */
    private static Object transferArguments(String type, String value) {
        type = type.toLowerCase();
        switch (type) {
            case "int":
                return Integer.valueOf(value);
            case "double":
                return Double.valueOf(value);
            case "float":
                return Float.valueOf(value);
            case "char":
                return (char)value.getBytes()[0];
            case "string":
                return value;
            default:
                System.out.println("undefined!");
                return null;
        }
    }

  1. 最后我们就可以根据读取到的“classname"获取它的Class对象并获得它的对应方法,传入参数进行调用了
		// 根据类名获得它的Class
        Class newClass = null;
        try {
            newClass = Class.forName(sClassname);
        } catch (ClassNotFoundException e) {
            System.out.println("根据类名初始化类出错!");
            System.exit(0);
        }
        if (newClass == null) {
            System.out.println("类指针为空");
            System.exit(0);
        }

        // 根据方法名获取method
        Method method = null;
        try {
            method = newClass.getMethod(sMethod, oArgsType);
        } catch (NoSuchMethodException e) {
            System.out.println("未找到该方法!");
            System.exit(0);
        }

        // 实例化一个对象并对它进行方法调用
        Object p = null;
        if (method != null) {
            try {
                p = newClass.newInstance();
                method.invoke(p, oArgs);
            } catch (IllegalAccessException | InstantiationException e) {
                System.out.println("生成新实例出错!");
                System.exit(0);
            } catch (InvocationTargetException e) {
                System.out.println("调用方法出错!");
                System.exit(0);
            }
        }
        if (p != null) {
            System.out.println(p.toString());
        }

最后的输出如下:
java反射,从配置文件中获取类名、方法名、变量、变量类型并进行方法调用
完整代码可见github: https://github.com/SanKing-Lee/JavaAdvancedProgramDesign/tree/master/src/com/part3/reflect/point

总结:
主要遇到的问题
- 读取properties文件时的中文乱码:改用properties.load()
- 获取方法时传入多个参数类型:根据字符串返回对应的Class和利用Class数组
- 调用方法时传入多个参数:根据字符串返回对应的数据类型Object和利用Object数组