当前位置:首页 > 生活百科

java项目源码哪里找(java初学者练手项目)

栏目:生活百科日期:2025-03-02浏览:0

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()。

public class MyAgentClass {// JVM优先执行该方法public static void premain(String agentArgs, Instrumentation inst) {System.err.println("main before...");}public static void premain(String agentArgs) {System.err.println("main before...");}}

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.jar JavaApp

主程序运行后Agent的步骤:

这种是针对已经运行的JVM进程,我们可以通过attach机制,启动一个新的JVM进程发送指令给它执行。

1、编写Agent类,该类必须有静态方法agentmain()。

public class MyAgentClass {public static void agentmain(String agentArgs, Instrumentation inst) {System.err.println("main after...");}}

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进程。

public static void main(String&[] args) throws Exception {    VirtualMachine vm = VirtualMachine.attach("8080");    vm.loadAgent("/dev/agent.jar");}

3. 相关组件

3.1 Instrumentation

编写的AgentClass类必须有premain()方法,其中一个比较重要的参数就是Instrumentation。它是JavaAgent技术用到的主要API,接口定义如下:

public interface Instrumentation {    /** * 添加Class文件转换器,底层采用数组存储 * JVM加载Class文件前,需要依次经过转换 * @param transformer * @param canRetransform 是否允许转换 */    void addTransformer(ClassFileTransformer transformer, boolean canRetransform);    void addTransformer(ClassFileTransformer transformer);    // 删除Class文件转换器    boolean removeTransformer(ClassFileTransformer transformer);    boolean isRetransformClassesSupported();    // 重新转换Class    void retransformClasses(Class&<?&>... classes) throws UnmodifiableClassException;    boolean isRedefineClassesSupported();    // 重新定义Class,热更新    void redefineClasses(ClassDefinition... definitions)        throws  ClassNotFoundException, UnmodifiableClassException;    boolean isModifiableClass(Class&<?&> theClass);    @SuppressWarnings("rawtypes")    Class&[] getAllLoadedClasses();    @SuppressWarnings("rawtypes")    Class&[] getInitiatedClasses(ClassLoader loader);    // 获取对象大小    long getObjectSize(Object objectToSize);    void appendToBootstrapClassLoaderSearch(JarFile jarfile);    void appendToSystemClassLoaderSearch(JarFile jarfile);    boolean isNativeMethodPrefixSupported();    void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix);}

重要的方法笔者已经写上注释了,本文会用到的方法主要是addTransformer()。它可以用来添加Class转换器,JVM在加载Class前,会先经过这些转换器进行加工。

3.2 ClassFileTransformer

Class文件转换器,JVM加载某个Class前,会先经过它转换,我们可以在这里去修改字节码以达到功能增强的目的。它只有一个方法transform():

public interface ClassFileTransformer{        /** * 转换Class * @param loader 类加载器 * @param className 类名 * @param classBeingRedefined 原始Class * @param ProtectionDomain  * @param classfileBuffer Class文件字节数组 */byte&[] transform(  ClassLoader loader,                String className,                Class&<?&>  classBeingRedefined,                ProtectionDomain protectionDomain,                byte&[]  classfileBuffer)        throws IllegalClassFormatException;}

本文主要用到的就是classfileBuffer,有了Class的字节数组,只要把它导出到磁盘,通过IDEA反编译就能看到源码了。

4. 实战

【需求】

支持将任意Java对象的Class文件导出到磁盘,通过反编译查看源码,包括动态生成的类。

【实现】

1、编写InstrumentationHolder,持有Instrumentation实例,后续操作全靠它。

public class InstrumentationHolder {private static Instrumentation INSTANCE;public static void init(Instrumentation ins) {INSTANCE = ins;}public static Instrumentation get() {if (INSTANCE == null) {throw new RuntimeException("检查 -javaagent 配置");}return INSTANCE;}}

2、编写MyAgentClass,保存Instrumentation实例。

public class MyAgentClass {public static void premain(String agentArgs, Instrumentation inst) {System.err.println("main before...");InstrumentationHolder.init(inst);}}

3、编写ClassDumpTransformer,获取Class文件字节数组,导出到磁盘。

public class ClassDumpTransformer implements ClassFileTransformer {private final File file;private final Set&<Class&<?&>&> classes = new HashSet&<&>();public ClassDumpTransformer(String path, Class&<?&>... classes) {this.file = new File(path);this.classes.addAll(Arrays.asList(classes));}@Overridepublic byte&[] transform(ClassLoader loader, String className, Class&<?&> classBeingRedefined, ProtectionDomain protectionDomain, byte&[] classfileBuffer) throws IllegalClassFormatException {if (classes.contains(classBeingRedefined)) {FileUtil.writeBytes(classfileBuffer, file);}return null;}}

4、编写ClassUtil工具类,支持导出Class文件。

public class ClassUtil {public static void classDump(Class&<?&> c, String path) {ClassDumpTransformer transformer = new ClassDumpTransformer(path, c);Instrumentation inst = InstrumentationHolder.get();inst.addTransformer(transformer, true);try {inst.retransformClasses(c);} catch (UnmodifiableClassException e) {e.printStackTrace();} finally {inst.removeTransformer(transformer);}}}

5、编写MANIFEST.MF文件,构建Jar包。

Manifest-Version: 1.0Can-Redefine-Classes: trueCan-Retransform-Classes: truePremain-Class: top.javap.agent.MyAgentClass

6、编写测试类,利用JDK动态代理生成代理类,然后将代理类的Class文件导出。

public class AgentDemo {public static void main(String&[] args) throws Exception {Object instance = Proxy.newProxyInstance(A.class.getClassLoader(), new Class&[]{A.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object&[] args) throws Throwable {return null;}});ClassUtil.classDump(instance.getClass(),"/target/X.class");}public static interface A {void a();}}

7、设置-javaagent参数并启动程序。

java -javaagent:agent.jar AgentDemo

此时,target目录下就会生成X.class文件,通过IDEA打开即可看到JDK生成的代理类源码。

5. 总结

JavaAgent十分强大,通过它可以在JVM加载Class文件前修改字节码,甚至修改JVM已经加载的Class。基于此,我们可以「零侵入」的对应用程序做增强,服务实现热部署等等。

本文通过一个小示例,编写ClassFileTransformer实现类导出对象的Class文件,反编译查看其源码。这对于ASM操作字节码、JDK动态代理等动态生成类的场景下,而我们又想看对象的具体实现时,提供了帮助。

“java项目源码哪里找(java初学者练手项目)” 的相关文章

云计算就业前景报告(云计算未来的发展趋势)

首先,从行业领域的发展前景来看,云计算相关人才的需求量还是比较大的,随着云计算技术体系的逐渐成熟,未来在云计算广泛落地的过程中,行业领域会释放出大量的技能型人才...

nba直播软件哪个好(免费的看球直播软件推荐)

NBA季后赛直播可以通过腾讯体育来观看最新NBA季后赛的直播,在以往,央视CCTV5频道也是可以观看的,但因为“莫雷事件”发生之后,央视暂停转播NBA的比赛。除...

如何做好品牌的市场推广计划,完整的品牌策划方案范文分

在我们日常消费的过程中,我们往往会把更高的信赖度给与品牌更大的产品。那么这些大品牌一般都是怎么建立起来的呢?品牌策划是指人们为了达成某种特定的品牌,借助一定的科...

opengl安装教程(手机查看opengl版本)

1说明:1.1利用python的pygame和OpenGL制作动态正方体cube。1.2安装python3.8和pygame(省略)。1.3安装OpenGL:p...

word文档怎么设置密码保护(详解给文档加密设置密码)

WPS的基本操作(WPS的版本2016版)一,新建文档二,保存文档三,打开和关闭文档四,选择文本五,复制与粘贴文本六,删除和移动文本七,为文档设置密码八,删除文...

2023年国产十大放心奶粉排名(热销榜排名前十款奶粉)

随着国产乳业的不断发展,出现了越来越多值得信赖的品牌,奶粉无论是在品质还是营养成分方面,都是值得各位家长信赖的。那么今天就由排行榜123小编来为大家列出2019...

在中国现在最欢迎什么手机(广受大众喜爱的10款手机品牌

在巨量引擎刚刚发布的五月热门机型排行榜中。可以看到,在国产品牌之中,竟然有五款机型上榜前十,小米11Ultra和RedmiK40游戏增强版更是拿下了前二名!小米...

天猫网店转让费用是多少,天猫店铺转让流程

很多创业者都是从开网店开始的,大多都想开天猫店铺,但是天猫店铺入驻非常困难,而且个人是不能入驻天猫的。网店转让形式的兴起让许多创业者又看到了希望,入驻天猫已经可...

保温壶外壁发热是不保温现象吗(解析保温壶外壁发热征兆

保温测试:将开水倒入保温杯内顺时针旋紧瓶塞或杯盖2-3分钟后用手触摸杯身外表面,若杯身有明显的温热现象,尤其是杯体下部发热,说明产品已失去真空度,不能达到良好的...

plc编程软件有哪些(plc编程软件手机版)

软件特色有了许多新的指令,编程现在比以往任何时候都更容易、更快、更直观。改进的查看选项和可定制的工具栏使编程和监控您的PLC系统变得更容易。使用DirectSO...