反射的应用原理

理论

每个被我们写好的类被编译后都生成了对应的class文件,当被类加载器加载后,就会产生对应的Class类的实例,所以我们写在Java里面的东西自然而然,它也具备,我把它分成三个部分看,成员变量,构造方法,成员方法,然后Java提供了对应方法进行获取。

获取Class类实例的方法

  1. Class.forName(“全限定名”);
    * 将字节码文件加载进内存,返回Class对象
    * 全限定名:包名+类名

  2. 类名.class;
    * 通过类名的属性class获取

  3. 对象实例.getClass();
    * 通过Object对象的方法getClass()获取
    PS:
    在运行过程中,一个字节码文件(xxx.class)只会被加载一次,不论通过哪种方式获取Class对象都会是同一个

Class实例的方法

  1. 获取Class基本信息
    1.String getName();
    * 获取全限定名
    2.String getSimpleName();
    * 获取简单类名

  2. 获取成员变量
    1.Field[] getFileds();
    * 获取所有public修饰的成员变量
    2.Field getField(String name);
    * 获取指定public修饰的成员变量
    3.Field[] getDeclaredFileds();
    * 获取所有声明的成员变量
    4.Field getDeclaredField(String name);
    * 获取指定声明的成员变量

  3. 获取构造方法
    1.Constructor[] getConstructors();
    * 获取所有public修饰的构造方法
    2.Constructor getConstructor(Class… parameterTypes);
    * 获取指定public修饰的构造方法
    3.Constructor[] getDeclaredConstructors();
    * 获取所有声明的构造方法
    4.Constructor getDeclaredConstructor(Class … parameterTypes);
    * 获取指定声明的构造方法

  4. 获取成员方法
    1.Method[] getMethods();
    * 获取所有public修饰的成员方法,包括父类的
    2.Method getMethod(String name,Class… parameterTypes);
    * 获取指定public修饰的成员方法
    3.Method[] getDeclaredMethods();
    * 获取所有声明的成员方法
    4.Method getDeclaredMethod(String name,Class… parameterTypes);
    * 获取指定声明的成员方法

Failed对象功能

  1. 设置值
    void set(Object obj,Object value);

  2. 获取值
    Object get(Object obj);

  3. 忽略访问权限修饰符的安全检查
    void setAccessible(true);

  4. 获取Class类型
    Class getType();

Constructor对象功能

  1. 创建Class对象实例
    T newInstance(Object… initargs);

  2. 如果使用空参构造方法创建,可以简化操作
    使用Class对象的 T newInstance();

  3. 忽略访问权限修饰符的安全检查
    void setAccessible(true);

Method对象功能

  1. 调用(执行)方法
    Object invoke(Object obj, Object… args);

  2. 获取方法名称
    String getName();

  3. 忽略访问权限修饰符的安全检查
    void setAccessible(true);

实践

模拟框架实现调用任意方法,创建任意对象的功能

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

/**
 * 需求:编写一个工具类 提供创建任意类 调用任意方法的功能
 * 分析:
 *
 * @author JianAi
 * @create 2019--01--11 11:54
 */
public class ReflectUtil {
    public static void main(String[] args) throws Exception {
        //创建一个Properties对象
        Properties properties = new Properties();
        //配置文件路径
        String propertiesName = "proper.properties";
        //获取需要实例化的对象的Class对象
        Class<?> aClass =
                Objects.requireNonNull(createNewObject(propertiesName,properties)).getClass();
        //获取需要调用的方法的方法对象
        Method method = invokeMethod(propertiesName, aClass,properties);
        //忽略权限检查
        Objects.requireNonNull(method).setAccessible(true);
        //获取到构造方法
        Constructor<?> constructor = aClass.getDeclaredConstructor();
        //忽略权限检查
        constructor.setAccessible(true);
        //调用方法 实现调用方法的功能 传入的参数是一个实例化对象 如果是有参数的方法 后续还需要加上参数对应的.class 有几个写几个缺一不可
        method.invoke(constructor.newInstance());
        //获取到的参数数组
        List<Class<?>> parameters = getParameters(propertiesName, aClass,properties);
        //打印
        System.out.println(parameters);
    }

    /**
     * 获取传入的方法的参数
     * @param propertiesName 配置文件路径
     * @param clazz Class实例对象
     * @return 参数类型数组
     * @throws IOException
     */
    private static List<Class<?>> getParameters(String propertiesName, Class<?> clazz,Properties properties) throws IOException {
        //获取一个流对象 读取文件
        try (FileInputStream fr = new FileInputStream(propertiesName)) {
            //加载读取到的文件
            properties.load(fr);
            //获取配置文件中的方法名
            String methodName = properties.getProperty("methodName");
            //创建一个数组接收参数类型
            List<Class<?>> list = new ArrayList<>();
            //获取全部方法
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                method.setAccessible(true);
                if (methodName.equals(method.getName())) {
                    //获取配置文件中方法的参数个数
                    Parameter[] parameters = method.getParameters();
                    for (Parameter parameter : parameters) {
                        //将获取到的参数类型添加进数组
                        list.add(parameter.getType());
                        //测试代码
                        Class<?> type = parameter.getType();
                        System.out.println(type);
                    }
                }
            }
            return list;
        }
    }

    /**
     * 
     * @param propertiesName 配置文件路径
     * @param clazz Class实例对象
     * @return 方法对象
     */
    private static Method invokeMethod(String propertiesName, Class<?> clazz,Properties properties) {
        //获取一个流对象 读取文件
        try (FileInputStream fr = new FileInputStream(propertiesName)) {
            //加载读取到的文件
            properties.load(fr);
            //获取配置文件中的方法名
            String methodName = properties.getProperty("methodName");
            //获取到需要被调用的方法(这里其实有bug,如果调用有参数的方法,那还需要在后面传入参数的.class,比如String.class)
            Method method = clazz.getDeclaredMethod(methodName);
            //忽略权限检查
            method.setAccessible(true);
            //返回对应的方法实例对象
            return method;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 
     * @param propertiesName 配置文件路径
     * @return 实例对象
     */
    private static Object createNewObject(String propertiesName,Properties properties) {
        //获取一个流对象 读取文件
        try (FileInputStream fr = new FileInputStream(propertiesName)) {
            //加载读取到的文件
            properties.load(fr);
            //获取配置文件中的类名
            String className = properties.getProperty("className");
            //获取类加载器并加载配置文件中的class实例化对象
            Class<?> clazz =
                    Class.forName("reflect.ReflectUtil").getClassLoader().loadClass(className);
            //获取构造器 这里使用了暴力反射 防止无法获取到私有化的构造器(比如单例设计模式的构造器是私有化) 但是这里破坏了java的封装
            Constructor<?> constructor = clazz.getDeclaredConstructor();
            //忽略权限检查(没有这行代码实际你能获取到的还是只有声明了public的构造器)
            constructor.setAccessible(true);
            // 返回一个实例化对象(个人觉得这有点不完美,因为返回的是Object对象,如果有优化方案希望大佬能教教我)
            return constructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}



className=reflect.Person
methodName=eat
//配置文件

总结

Demo坑没填完,眼睛疼,有时间在抽空填。
总的来说,反射暂时了解就行,这样对使用框架配置文件的时候,心里有个底,为什么这么写就能实现,而不是一脸懵逼的看着大佬们的博客去无脑配置。希望有一天我这个菜鸟也能飞起来,如果哪位大佬对我的代码有优化方案,还希望能留言,衷心感谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值