最近看Spark源码,看到在Spark这种底层架构中用到很多Scala/Java的反射机制,在网上看了一些关于Scala/Java反射机制的原理和用处,总结如下。scala的多线程的实现依赖于JVM的,在反射机制中也是完全套用了Java的反射机制,所以本文讨论的实际上就是Java的反射机制。
反射机制的用处:1、在已有一个类的对象,在运行之前并不能确定这个对象对应的Class的时候,需要在运行时动态的确定所需要的类的时候
2、在给定的接口,当时无法得到接口内部的信息的时候,需要通过反射的方法来定义接口,这个在JDBC中使用的比较多。
3、网上有很多说反射在黑客技术中使用的比较多,个人猜测是可以通过反射获得对象队形的class的内部所有的信息和类结构,field、constructor、method等。
在你编写好并且编译过后,会生成Class文件,这些Class文件在被JVM的ClassLoader加载之后都会有对应的java.lang.Class<T>这个类的实例。每个类的自有的方法属性(类的结构)会自然的包含在这个实例上,因此就可以获取到对应的方法属性,无论这写方法属性是否private.
首先,Class文件是以8位字节为基础的二进制文件,这些二进制文件是按照严格的结构顺序保存的,里面的结构顺序为:
版本号、常量池、访问标志、类索引、字段表集合等
知道了Class文件的物理结构了,就可以随心所欲的获取关于这些类的所有信息了。
从上面可以得知,在反射获取一个类的信息(Fields/Method/Constructor)之前,需要加载这个类的class文件,通过class文件中的物理结构来获取这个类的所有信息。
假设有类
class Secret(sec:String){ private val secrecy = sec def getSecrecy(){ return "Inner Secret" } }我们有了一个Secret类的对象:
val s = new Secret("Top Secret")
我们可以通过以下方法Secret的class对象:val clz = Class.forName("Secret") // 在forName内填写需要的类名称,如果是在同一个包中,但是不是同一个文件内则需要填写完整的包路径,比如com.test.Secret
此时,Java虚拟机会加载Secret的class文件,然后在方法区创建Secret类实例,即Secret的class对象。
然后我们使用getDeclaredFiled()来获取这个类的field
val f = clz.getDeclaredField("secrecy")
设置这个field的访问权限
f.setAccessible(true)
println(f.get(s)) //输出为Top Secret 通过这种方法就能够范围s对象的私有成员变量了
以上是Field的反射方法,Constructor和Method的反射方法类似。
至于Class.forName是如何实现通过类名获得对应的class的,这个在JVM中是native方法,使用的是cpp实现的。总之就是取实现是的字节码文件,然后blalabla....没有细看,有兴趣的同学可以探究下JVM中这方面的实现。
贴上自己做实验用的代码:
class Secret(sec: String = "defalut") { def this(seret: Secret) { this(seret.getSecrecy()) } private val secrecy = sec def getSecrecy() = { "Inner Secret" } } object testReflection { def main(args: Array[String]): Unit = { // val semaphore = new Semaphore(5) // (1 to 10).foreach { _ => // new Thread(new EtaThread(semaphore)).start() // } val first = new Secret() val cls = Class.forName("Secret") val constructor = cls.getConstructor(classOf[Secret]) val s = constructor.newInstance(first) val f = s.getClass.getDeclaredField("secrecy") f.setAccessible(true) println(f.get(s)) val m: Method = Class.forName("Secret").getDeclaredMethod("getSecrecy") m.setAccessible(true) println(m.invoke(s)) } }
【参考文献】
http://a.codekk.com/detail/Android/Mr.Simple/公共技术点之 Java 反射 Reflection
https://www.zhihu.com/question/46883050