Java安全第一篇 | 反射看这一篇就够了
什么是反射?
Java安全可以从反序列化漏洞说起,反序列化漏洞又可以从反射说起。反射是⼤多数语⾔⾥都必不可少的组成部分,对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私有),拿到的⽅法可以调⽤,总之通过“反射”,我们可以将Java这种静态语⾔附加上动态特性。可能说完这一两句话大家还是不知道反射是个啥玩意,现在为了让大家容易理解,先为大家提出一个需求,通过这个需要来引出反射。需求如下:
根据配置文件re.properties指定信息,创建对象并调用方法。
classfullpath=com.lxflxf.Cat
method=hi
这样的需求在学习框架时很多,即在通过外部文件配置,在不修改源码的情况下,来控制程序。
我们使用现有技术可以做到吗?咱们可以动手写一下。
首先创建配置文件,写入上述内容,然后创建一个类,写入如下内容:
public class Cat {
private String name = "小猫";
public void hi(){
System.out.println("hi" + name);
}
}
传统的方法是不是我们可以先new一个对象,然后再调用它的方法。写法如下:
Cat cat = new Cat();
cat.hi();
通过传统方法,确实可以调用hi()方法,但是这和我们的需求不一样,这里我们是要根据配置文件re.properties指定信息来完成。到了这里,有同学就说了,咱们可以通过IO流的方式来读取配置文件的信息。好,咱们用代码来写一下。
使用Properties来读写配置文件。案例代码如下:
Properties properties = new Properties();
properties.load(new FileInputStream("src//re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
System.out.println("classfullpath" + classfullpath);
System.out.println("methodName=" + methodName);
运行一下,发现成功读取到内容。
然后需要创建对象,怎么创建对象呢?有同学就说了,咱们可以直接new classfullpath
,这样不就好了嘛?嗯,想法不错,下回不要想了。不要忘记了,我们现在的classfullpath可是字符串类型,怎么能去new
呢。所以现有技术是做不到这个事情的。那么这里就要引入我们要将的重点——反射机制。
为了能更好的理解反射,这里先写一个小案例,然后在去解释。
第一步、加载类,返回Class类型的对象cls
Class cls = Class.forName(classfullpath);
第二步、通过cls得到你加载的类 com.lxflxf.Cat 的对象实例
Object o = cls.newInstance();
可能有同学会问,你怎么知道这里拿到的是com.lxflxf.Cat呢,我们可以打印一下来看看,System.out.println(o.getClass())
输出结果如下:
第三步、通过cls得到你加载的类 com.lxflxf.Cat 的 methodName 的方法对象,我们可以在反射中,把方法视为对象。
Method method1 = cls.getMethod(methodName);
最后、通过method1调用方法、也就是通过方法对象来实现调用方法
method1.invoke(o);
在这里我们也能发现反射和传统方法的区别了,传统方法是对象.方法(),反射中呢,是方法.invoke(对象)。那我们运行一下,看看能否输出方法里的内容呢,如下:
说到这里大家脑海里应该也有了反射的概念。其实反射机制还有一个优点,那就是可以通过外部文件配置,在不修改源码的情况下,来控制程序。比如这里,我在Cat类下面再写一个方法,cry()方法,代码如下:
public void cry(){
System.out.println(name + "......喵喵喵");
}
如果我们使用传统方法,要调用这个方法,是不是就要修改代码了,比如cat.cry();
这样的,那通过反射,我们只需要修改配置文件就可以了,在配置文件re.properties中,将method=hi改为method=cry,就可以了。
运行,发现成功调用并输出了内容,实现了改配置文件,不改代码,完成了解藕。
反射机制
上文中,通过一个小案例来简单的了解了一下反射,现在来系统的说一下。反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量、构造器、成员方法等等),并能操作对象的属性及方法。加载完类后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。为了便于理解,在这里为大家画一下Java反射机制原理示意图。如下:
然后现在做一个小小的总结,Java反射机制可以完成:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射相关的主要类如下:
1、Java.long.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
2、Java.lang.reflect.Method:代表类的方法
3、Java.lang.reflect.Field:代表类的成员变量
4、Java.lang.reflect.Constructor:代表类的构造方法
上文的案例代码中,我们使用了Method和Class相关的方法,现在演示一下,通过Field来拿到成员变量,代码如下:
Field name = cls.getField("name");
System.out.println(name.get(o));
发现成功拿到了成员变量的值
Class类分析
接下来对Class类特点进行一下梳理。先看看Class类图
我们发现它的父类仍然是Object。
然后第二点是,Class类对象不是new出来的,而是系统创建的。这里怎么理解呢,还记得上面咱们画的原理图吗?Class类是由loadClass()方法完成类加载,生成了某个类对应的Class类对象。现在为大家演示一下。写如下案例代码:
Class<?> aClass = Class.forName("com.lxflxf.Cat");
然后这这句代码的前面下一个断点,进行调试。成功进入ClassLoader类中,到了loadClass()方法。如下:
接下来说第三点,对于某个类的Class类对象,在内存中只有一份,因为类只加载一次。现在写一个小案例来验证一下这个事情,通过ha shCode来判断,写如下几行代码:
Class<?> cls1 = Class.forName("com.lxflxf.Cat");
Class<?> cls2 = Class.forName("com.lxflxf.Cat");
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
执行结果如下图,值相同
最后关于Class类对象还有两点说一下,一是每个类的实例都会记得自己是由哪个Class实例所生成,二是Class对象可以完整地得到一个类的完整结构,通过一系列的API。
Class类常用方法
这里通过写小案例的方式,为大家说说Class类常用方法,首先新建一个Car类,代码如下:
public class Car {
public String brand;
public int price;
public String color;
}
然后我要获取到Car类对应的Class对象,这里用到的就是forName()方法:
String classAllPath = "com.lxflxf.Car";
//获取到Car类对应的Class对象
Class cls = Class.forName(classAllPath);
我们可以输出一下
System.out.println(cls);
System.out.println(cls.getClass());
第一个输出的是cls对象,是哪个类的Class对象,第二个输出的是cls运行类型,如下图:
如果我想要得到包名,可以通过getPackageName()方法,可以通过System.out.println(cls.getPackageName())
,输出内容为com.lxflxf
。如果想得到类名,可以通过getName()方法。还有一个很重要的方法,那就是创建对象实例:newInstance(),案例如:Object o = cls.newInstance();
,这里也需要注意一点,在JDK1.9往上,不再使用newInstance()。还可以通过getField()获取到属性。还有一写其他方法,这里就不一一举例了。列了一个表格,如下:
前面说了这么多,那哪些类型有Class对象呢?如下列表:
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- interface:接口
- 数组
- enum: 枚举
- annotation: 注解
- 基本数据类型
- void
案例代码如下:
Class<String> cls1 = String.class; //外部类
Class<Serializable> cls2 = Serializable.class; //接口
Class<Integer[]> cls3 = Integer[].class; //数组
Class<Deprecated> cls4 = Deprecated.class; //注解
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
输出结果如下:
动态加载
在文章最开始,就说了一下,通过“反射”,我们可以将Java这种静态语⾔附加上动态特性,换句话说,就是反射机制是Java实现动态语言的关键,也就是通过反射实现类动态加载。怎么理解呢,就是在运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。
举个例子吧
新建一个Java文件,命名为ClassLoad,写入如下代码
Scanner scanner = new Scanner(System.in);
System.out.println("请输入数字");
String key = scanner.next();
switch (key){
case "1":
System.out.println("我等于1");
case "2":
Class<?> cls = Class.forName("Person");
Object o = cls.newInstance();
Method m = cls.getMethod("hi");
m.invoke(o);
System.out.println("ok!");
break;
}
这里,我没有写Person类,但是程序编译的时候是不会报错的。也就是说,等到程序执行到case "2"
,里面时才会发生报错,也就是上文中提到的在运行时加载需要的类,如果运行时不用该类,则不报错,这就是动态加载。我们现在来运行看一眼。先输入1程序正常,然后输入2报错。
现在是不是理解了动态加载了呢。
类加载
可能还有一些同学想要了解,比如,类加载过程到底是怎么样的呢?其实类加载大体分为三个阶段(加载阶段(Loading)、链接阶段(验证、准备、解析)、初始化阶段(initalization)),这里画一张图来便于理解。
具体的这个内容咱们后续再说,现在这里就不做探讨了。
参考
Java安全漫谈 - 01.反射篇
Java安全第一篇 | 反射看这一篇就够了的更多相关文章
- 【转】成为Java顶尖程序员 ,看这11本书就够了
成为Java顶尖程序员 ,看这11本书就够了 转自:http://developer.51cto.com/art/201512/503095.htm 以下是我推荐给Java开发者们的一些值得一看的好书 ...
- 【java编程】ServiceLoader使用看这一篇就够了
转载:https://www.jianshu.com/p/7601ba434ff4 想必大家多多少少听过spi,具体的解释我就不多说了.但是它具体是怎么实现的呢?它的原理是什么呢?下面我就围绕这两个问 ...
- 【转】成为Java顶尖程序员 ,看这10本书就够了
“学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一本好书的价值远超 ...
- java开发环境配置,看这一篇就足够了!
可能平时大家对于安装环境的需求不是那么强烈,但是当你换了一台新电脑时,你就会发现怎么也得花费你几个小时乃至半天一天的时间.故此整理此篇文章,给有需要的小伙伴 注:本文皆win10环境 (1).JDK的 ...
- java 线程基础篇,看这一篇就够了。
前言: Java三大基础框架:集合,线程,io基本是开发必用,面试必问的核心内容,今天我们讲讲线程. 想要把线程理解透彻,这需要具备很多方面的知识和经验,本篇主要是关于线程基础包括线程状态和常用方法. ...
- Java并发编程入门,看这一篇就够了
Java并发编程一直是Java程序员必须懂但又是很难懂的技术内容.这里不仅仅是指使用简单的多线程编程,或者使用juc的某个类.当然这些都是并发编程的基本知识,除了使用这些工具以外,Java并发编程中涉 ...
- 成为Java顶尖程序员 ,看这11本书就够了(转)
学习的最好途径就是看书",这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一本好书的 ...
- 成为Java顶尖程序员 ,看这11本书就够了
以下是我推荐给Java开发者们的一些值得一看的好书.但是这些书里面并没有Java基础.Java教程之类的书,不是我不推荐,而是离我自己学习 Java基础技术也过去好几年了,我学习的时候看的什么也忘了, ...
- 图灵社区 书单推荐:成为Java顶尖程序员 ,看这11本书就够了
java书单推荐 转自 http://www.ituring.com.cn/article/211418 “学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两 ...
随机推荐
- Nginx中的 location 匹配和 rewrite 重写跳转
Nginx中的location匹配和rewrite重写跳转 1.常用的Nginx正则表达式 2.location 3.rewrite 4.rewrite实例 1.常用的Nginx正则表达式: ^ :匹 ...
- Redis性能管理
Redis性能管理 目录 Redis性能管理 一.查看Redis内存使用 二.内存碎片率 三.内存使用率 四.避免内存交换发生的方法 1. Hash数据类型 1.1 HSET/HGET/HDEL/HE ...
- python——虚拟环境管理大合集
个人常用:pipenv 安装 pip3 install pipenv 创建虚拟环境 # 默认安装在~/.local/virtualenv下 mkdir project cd project pipen ...
- [USACO4.2]工序安排Job Processing
两种想法: (样例是真的良心,卡掉了两种错误做法)洗完一件马上塞一件到最快的空闲烘干机去?X,因为最后一件洗完的衣服决定了第二问的答案,但它并不一定得到最优待遇--最快的烘干机. 给最后一件洗完的 ...
- VSCode官方的配置同步方案
前言 这几天在迁移电脑工作环境,对于VSCode,我实在不想从头做下载插件.配置代码规则这样的事情,于是求助百度,搜索结果靠前的解决方案基本都是使用Setings Sync插件,于是我就从了. 经过好 ...
- ThinkPHP5中使用第三方类库
在TP5中有两种方式使用第三方类库,如果类库支持composer方式安装那就很方便了,使用composer安装的类库存储在Vendor目录下,可以直接使用,以phpmailer为例,使用如下命令安装: ...
- NeurIPS 2017 | TernGrad: Ternary Gradients to Reduce Communication in Distributed Deep Learning
在深度神经网络的分布式训练中,梯度和参数同步时的网络开销是一个瓶颈.本文提出了一个名为TernGrad梯度量化的方法,通过将梯度三值化为\({-1, 0, 1}\)来减少通信量.此外,本文还使用逐层三 ...
- Spring Data JPA应用 之查询分析
在Spring Data JPA应用之常规CRUD操作初体验 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)尾附上了JpaRepository接口继承关系及方法,可以知道JpaRepos ...
- 数据缓存Cache
在MyBatis - 随笔分类 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中有关于Mybatis中Cache技术实现及应用介绍.Cache技术实现都是implements Cache ...
- MyBatis分页插件PageHelper使用方法
1.导入相关依赖坐标 <!-- MyBatis分页插件 --> <dependency> <groupId>com.github.pagehelper</gr ...