使用JavaLangAccess和SharedSecrets来获取JVM中的实例
2016-11-02 11:22
453 查看
使用场景实例:
最近一段时间在mybatis框架的源码,看到框架的日志模块的时候,发现其集成了JDK的原生日志java.util.logging,于是我就去看了一下java.util.logging实现源码,在其中的LogRecord.java文件中看到其中有一段代码:// Private method to infer the caller's class and method names private void inferCaller() { needToInferCaller = false; JavaLangAccess access = SharedSecrets.getJavaLangAccess(); Throwable throwable = new Throwable(); int depth = access.getStackTraceDepth(throwable); boolean lookingForLogger = true; for (int ix = 0; ix < depth; ix++) { // Calling getStackTraceElement directly prevents the VM // from paying the cost of building the entire stack frame. StackTraceElement frame = access.getStackTraceElement(throwable, ix); String cname = frame.getClassName(); boolean isLoggerImpl = isLoggerImplFrame(cname); if (lookingForLogger) { // Skip all frames until we have found the first logger frame. if (isLoggerImpl) { lookingForLogger = false; } } else { if (!isLoggerImpl) { // skip reflection call if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) { // We've found the relevant frame. setSourceClassName(cname); setSourceMethodName(frame.getMethodName()); return; } } } } // We haven't found a suitable frame, so just punt. This is // OK as we are only committed to making a "best effort" here. }
这个函数的目的是推断调用日志log的类,被public String getSourceClassName()方法使用。通常来说一个方法在调用log时一般都会显式的给当前类的信息作为log的输入参数,当没有给定的时候,就需要日志框架来自己推测,这时候就是用到了上面这个函数。当然这个推测不能保证找到的调用类实例一定正确。
SharedSecrets和JavaLangAccess的作用
当我们需要对使用Log的类名进行的推断的时候,我们就需要知道JVM里面的实例对象了,这时候我们就需要使用到SharedSecrets和JavaLangAccess,通过这两个类来获取Java栈帧中存储的类信息,然后进行挑选,从而找出调用的类。接下来看一下SharedSecrets和JavaLangAccess的使用方式:
测试环境:为了方便,在mybatis源码工程里面利用junit进行测试,创建新类testLang.java,也可以自己创建java工程进行测试。
测试代码:
package org.apache.ibatis.logging; import sun.misc.JavaLangAccess; import sun.misc.SharedSecrets; /** * Created by 茂升 on 2016/11/2. */ public class testLang { public void testPrint() { JavaLangAccess access = SharedSecrets.getJavaLangAccess(); Throwable throwable = new Throwable(); int depth = access.getStackTraceDepth(throwable); //输出JVM栈帧中的所有类实例 for (int i = 0; i < depth; i++) { StackTraceElement frame = access.getStackTraceElement(throwable, i); System.out.println(frame); } } } 在LogFactoryTest.java文件中添加方法,这里使用了junit: @Test public void yumsTest() { //System.err.println("Yumaosheng TEST"); testLang t = new testLang(); t.testPrint(); }
执行结果:
org.apache.ibatis.logging.testLang.testPrint(testLang.java:12) org.apache.ibatis.logging.LogFactoryTest.yumsTest(LogFactoryTest.java:52) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) java.lang.reflect.Method.invoke(Method.java:497) org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) org.junit.runners.ParentRunner.run(ParentRunner.java:309) org.junit.runner.JUnitCore.run(JUnitCore.java:160) com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117) com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262) com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) java.lang.reflect.Method.invoke(Method.java:497) com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Process finished with exit code 0
我们会发现代码输出了所有JVM栈帧中的实例对象的类名。也就是说我们使用JavaLangAccess和SharedSecrets可以获取栈帧中的所有实例对象的类名称,接下来我们需要剔除掉不可能是调用类的名字。
剔除不可能的调用类
首先日志框架自身是不会调用的,所以先判断当前在栈中找到的类是不是日志框架自身,使用isLoggerImplFrame()方法(这个方法比较简单就不做过多解释了)。然后我们还要排除在获取栈帧过程中和日志框架使用过程中的额外引入的类,像java.lang.reflect.*和sun.reflect.*这些类都是额外引入的类,不会是调用类。剔除这些类以后,我们就找到了调用log的类。相关文章推荐
- Java使用SOAP获取webservice实例解析
- Java使用SOAP获取webservice实例解析
- 使用Java获取IP地址实例
- Java按行读取正在被动态写入的大文件实例--使用RandomAccessFile(1)
- 在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法
- 使用Struts2获取请求参数:Ljava.lang.String; cannot be cast to java.lang.String
- Java.lang.reflect 下的Class,Method,Field的使用实例
- JavaWeb学习(5.5)---在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法
- Java使用SOAP获取webservice实例解析
- JAVA反射系列之Field,java.lang.reflect.Field使用获取方法。
- 使用eclipse JDT compile class,解决 无法确定 X 的类型参数;对于上限为 X,java.lang.Object 的类型变量 X,不存在唯一最大实例
- 在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法
- JAVA反射系列之Field,java.lang.reflect.Field使用获取方法。
- Java使用SOAP获取webservice实例解析
- 使用反射创建Bean、Spring中是如何根据类名配置创建Bean实例、Java提供了Class类获取类别的字段和方法,包括构造方法
- 使用eclipse JDT compile class,解决 无法确定 X 的类型参数;对于上限为 X,java.lang.Object 的类型变量 X,不存在唯一最大实例
- java.lang.reflec 运用及反射方法获取实例
- Java使用SOAP获取webservice实例解析
- 在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法
- JAVA反射系列之Field,java.lang.reflect.Field使用获取方法