源代码在哪里找 在哪能找到Java源码

1. 前言为什么会接触javaAgent呢?
这起源于笔者最近在读Dubbo的源码 。Dubbo有一个很有意思的功能——SPI 。它可以根据运行时的URI参数 。自适应的调用特定的实现类 。大致的原理其实也能猜到 。无非就是生成一个代理类 。反射解析URI参数里的值 。然后再调用对应的实现类 。虽然大概可以猜到实现原理 。但毕竟只是猜想 。抱着科学严谨的精神 。还是想看看Dubbo的实现源码 。此时就有了一个想法 。能不能把Dubbo生成的代理对象的Class类Dump下来 。然后反编译看看它的源码呢?
理论上是完全可行的 。阿里有一个很好用的开源工具Arthas 。它的jad命令就支持对JVM已经加载的类进行反编译查看源码 。笔者把Arthas项目源码down下来了 。查看以后发现 。需要用到JavaAgent技术 。
2. JavaAgent规范在JDK1.5以后 。我们可以使用JavaAgent技术 。以「零侵入」的方式对Java程序做增强 。例如阿里云的Arms应用监控服务 。就可以通过JavaAgent的方式接入一个探针 。它会把应用的运行数据上报到阿里云 。开发者可以在后台查看到应用的运行数据 。这种方式 。不需要我们对应用做任何改动 。就可以轻松实现应用监控 。
JavaAgent是一种规范 。它分为两类:主程序运行前Agent、主程序运行后Agent 。它可以在JVM加载Class文件前 。对字节码做修改 。甚至允许修改已经加载过的Class 。这样我们就可以对应用做增强、以及实现代码热部署 。
主程序运行前Agent的步骤:
1、编写Agent类 。该类必须有静态方法premain() 。
publicclassMyAgentClass{//JVM优先执行该方法publicstaticvoidpremain(StringagentArgs,Instrumentationinst){System.err.println("mainbefore...");}publicstaticvoidpremain(StringagentArgs){System.err.println("mainbefore...");}}
2、在resources/META-INF目录下编写MANIFEST.MF文件 。指定Premain-Class 。然后将程序打成Jar包 。
Manifest-Version:1.0Can-Redefine-Classes:trueCan-Retransform-Classes:truePremain-Class:top.javap.agent.MyAgentClass//注意 。这里必须空一行
使用Maven构建程序时 。也可使用如下配置 。
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><configuration><archive><manifest><addClasspath>true</addClasspath></manifest><manifestEntries><Premain-Class>top.javap.agent.MyAgentClass</Premain-Class><Can-Retransform-Classes>true</Can-Retransform-Classes><Can-Redefine-Classes>true</Can-Redefine-Classes></manifestEntries></archive></configuration></plugin>
3、启动目标程序时 。指定JVM参数 。如下:
java-javaagent:agent-1.0-SNApsHOT.jarJavaapp
主程序运行后Agent的步骤:
这种是针对已经运行的JVM进程 。我们可以通过attach机制 。启动一个新的JVM进程发送指令给它执行 。
1、编写Agent类 。该类必须有静态方法agentmain() 。
publicclassMyAgentClass{publicstaticvoidagentmain(StringagentArgs,Instrumentationinst){System.err.println("mainafter...");}}
2、在resources/META-INF目录下编写MANIFEST.MF文件 。指定Premain-Class 。然后将程序打成Jar包 。
Manifest-Version:1.0Can-Redefine-Classes:trueCan-Retransform-Classes:trueAgent-Class:top.javap.agent.MyAgentClass//注意 。这里必须空一行
3、编写attach程序 。启动并attach到目标JVM进程 。
publicstaticvoidmain(String[]args)throwsException{Virtualmachinevm=VirtualMachine.attach("8080");vm.loadAgent("/dev/agent.jar");}
3. 相关组件3.1 Instrumentation编写的AgentClass类必须有premain()方法 。其中一个比较重要的参数就是Instrumentation 。它是JavaAgent技术用到的主要API 。接口定义如下:
publicinterfaceInstrumentation{/***添加Class文件转换器 。底层采用数组存储*JVM加载Class文件前 。需要依次经过转换*@paramtransformer*@paramcanRetransform是否允许转换*/voidaddTransformer(ClassFileTransformertransformer,booleancanRetransform);voidaddTransformer(ClassFileTransformertransformer);//删除Class文件转换器booleanremoveTransformer(ClassFileTransformertransformer);booleanisRetransformClassesSupported();//重新转换ClassvoidretransformClasses(Class<?>...classes)throwsUnmodifiableClassException;booleanisRedefineClassesSupported();//重新定义Class 。热更新voidredefineClasses(ClassDefinition...definitions)throwsClassNotFoundException,UnmodifiableClassException;booleanisModifiableClass(Class<?>theClass);@SuppressWarnings("rawtypes")Class[]getAllLoadedClasses();@SuppressWarnings("rawtypes")Class[]getInitiatedClasses(ClassLoaderloader);//获取对象大小longgetObjectSize(ObjectobjectToSize);voidappendToBootstrapClassLoaderSearch(JarFilejarfile);voidappendToSystemClassLoaderSearch(JarFilejarfile);booleanisNativeMethodPrefixSupported();voidsetNativeMethodPrefix(ClassFileTransformertransformer,Stringprefix);}

推荐阅读