Java热加载(二)—— Instrumentation实现热加载

本篇文章我们讨论一下Instrumentation如何实现热加载以及使用过程中需要注意的一些地方。

Instrumentation基础

关于instrumentation的基础知识 。可以阅读此篇文章。小编也是通过阅读此篇文章,加深对instrumentation理解的。

在实际项目中的应用及其需要注意的问题

那instrumentation怎么在热加载中使用呢?又有哪些注意的地方呢?接下来我们一一去说这些问题。

项目中的实际应用

public final class PremainReload {
    private static Instrumentation inst = null;

    public PremainReload () {
    }

    public static void premain(String premainArgs, Instrumentation i) {
        inst = i;
    }
     
    public staitic void reload(Class<?> cls, File file) throws Exception {
        byte[] code = loadBytes(file);
        ClassDefinition def = new ClassDefinition(cls, code);
        inst.redefineClasses(new ClassDefinition[]{def});
    }
    
    private static byte[] loadBytes(File file) throws Exception {
        byte[] buffer = new byte[(int)file.length()];
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);
        bis.read(buffer);
        return buffer;
    }
}

上面的代码写的比较简单,思路就 是缓存instrumentation实例,然后通过调用reload方法加载要热更新的class文件。
再说一下MANIFEST.MF的设置内容

// 是否能够重新定义类(必须手动设置,否则报错,因为默认为false)
Can-Redefine-Classes: true
// 是否能够重新定义Class文件的转换器(只是更代码可以不用)
Can-Retransform-Classes: true
// premian所在类
Premain-Class: com.pch.reload.PremainReload 

例如:我们把包含PremainReload类的打到一个reload.jar中,这样我们在项目启动时加入javaagent即可

java .... -cp -javaagent:./reload.jar ... Main

最后,我们需要建立一个文件夹存放需要热加载的class文件,通过读取里面的文件进行热加载

public class ReloadManager {
    public static boolean reloadClass() {
        String reloadPath = "D:/pch/reload/";
        File dir = new File(reloadPath );
        File[] files = dir.listFiles((f) -> f.getName().endsWith(".class"));
        for(File f : files) {
             FileInputStream in= new FileInputStream(f);
			 // 使用了asm直接解析类的完整路径
			 ClassReader cr = new ClassReader(in);
			 in.close();
			 Class cls = Class.forName(cr.getClassName().replaceAll("/", "."));
			 PremainReload.reload(cls, f);
        }
    }
}

需要注意的地方

通过阅读上面代码我们知道Instrumentation通过调用redefineClasses热加载代码,那redefineClasses有哪些限制呢?
下面摘自redefineClasses注释

/**
     * Redefine the supplied set of classes using the supplied class files.
     *
     * <P>
     * 此方法用于在不引用现有类文件字节的情况下替换类的定义,就像从源代码重新编译以进行修复并继续调试时所做的那样。
     * 将转换现有类文件字节的位置(例如在字节码检测中)
     * This method is used to replace the definition of a class without reference
     * to the existing class file bytes, as one might do when recompiling from source
     * for fix-and-continue debugging.
     * Where the existing class file bytes are to be transformed (for
     * example in bytecode instrumentation)
     * {@link #retransformClasses retransformClasses}
     * should be used.
     *
     * <P>
     * 此方法对集合进行操作,以便允许同时对多个类进行相互依赖的更改(重新定义a类可能需要重新定义B类)
     * This method operates on
     * a set in order to allow interdependent changes to more than one class at the same time
     * (a redefinition of class A can require a redefinition of class B).
     *
     * <P>
     * 如果重新定义的方法具有活动堆栈帧,则这些活动帧将继续运行原始方法的字节码。他重新定义的方法将用于新的调用。
     * If a redefined method has active stack frames, those active frames continue to
     * run the bytecodes of the original method.
     * The redefined method will be used on new invokes.
     *
     * <P>
     * 此方法不会导致任何初始化,除非是在常规JVM语义下发生的初始化。换句话说,重新定义一个类不会导致它的初始值设定项被运行。
     * 静态变量的值将保持调用之前的值。
     * This method does not cause any initialization except that which would occur
     * under the customary JVM semantics. In other words, redefining a class
     * does not cause its initializers to be run. The values of static variables
     * will remain as they were prior to the call.
     *
     * <P>
     * 重新定义的类实例不受影响
     * Instances of the redefined class are not affected.
     *
     * <P>
     * 重新定义可能改变方法体、常量池和属性(除非明确禁止的)
     * The redefinition may change method bodies, the constant pool and attributes
     * (unless explicitly prohibited).
     * 重新定义不能增加,删除、重命名字段或者方法,改变方法签名或者更改继承
     * The redefinition must not add, remove or rename fields or methods, change the
     * signatures of methods, or change inheritance.
     * 重定义不能改变NestHost或NestMembers属性。
     * The redefinition must not change the <code>NestHost</code> or
     * <code>NestMembers</code> attributes.
     * 这些限制将来的版本可能会取消
     * These restrictions may be lifted in future versions.
     * 在应用转换之前,不会检查、验证和安装类文件字节,如果结果字节出错,此方法将引发异常。
     * The class file bytes are not checked, verified and installed
     * until after the transformations have been applied, if the resultant bytes are in
     * error this method will throw an exception.
     *
     * <P>
     * If this method throws an exception, no classes have been redefined.
     * <P>
     * This method is intended for use in instrumentation, as described in the
     * {@linkplain Instrumentation class specification}.
     *
     * @param definitions array of classes to redefine with corresponding definitions;
     *                    a zero-length array is allowed, in this case, this method does nothing
     * @throws java.lang.instrument.UnmodifiableClassException if a specified class cannot be modified
     * ({@link #isModifiableClass} would return <code>false</code>)
     * @throws java.lang.UnsupportedOperationException if the current configuration of the JVM does not allow
     * redefinition ({@link #isRedefineClassesSupported} is false) or the redefinition attempted
     * to make unsupported changes
     * @throws java.lang.ClassFormatError if the data did not contain a valid class
     * @throws java.lang.NoClassDefFoundError if the name in the class file is not equal to the name of the class
     * @throws java.lang.UnsupportedClassVersionError if the class file version numbers are not supported
     * @throws java.lang.ClassCircularityError if the new classes contain a circularity
     * @throws java.lang.LinkageError if a linkage error occurs
     * @throws java.lang.NullPointerException if the supplied definitions array or any of its components
     * is <code>null</code>
     * @throws java.lang.ClassNotFoundException Can never be thrown (present for compatibility reasons only)
     *
     * @see #isRedefineClassesSupported
     * @see #addTransformer
     * @see java.lang.instrument.ClassFileTransformer
     */
    void redefineClasses(ClassDefinition... definitions)
        throws  ClassNotFoundException, UnmodifiableClassException;

通过阅读方法的注释,我们知道了这种热加载的限制,使用的时候一定要注意。

在做热加载的时候我们会把编译好的class文件放到指定目录下,然后,调用ReloadManager.reloadClass()即可。我们可以通过发送http请求,执行此方法;也可写一个定时任务不断进行检查是否有需要热更的文件,当有热更文件时执行此方法。

好,Instrumentation实现热加载就说到这里,说的不对的地方欢迎指正。在热加载的最后,我们用ASM获取类的绝对路径,下篇文章介绍一下ASM。

Java Agent是Arthas使用的技术,是Skywalking使用的技术,是一份十分重要的技术。 课程的稀缺性在此之前,市面上并没有针对Java Agent进行系统介绍的课程。 通过搜索引擎查找,会发现与Java Agent相关的内容大多是个人知识总结分享的内容。这些内容有如下特点:内容质量不一详略程度不一学习难度千差万别总体上来说,学习者很难有一个整体认知、系统学习的过程。 课程的设计目标 在构思课程内容时,本课程带有以下目标:课程学习梯度:从简单到复杂,让学习者有一个循序渐进的理解过程。构造完整、统一的知识体系:不是零散的知识点堆砌,而是有一个统一的贯穿始终的知识框架。具有可操作性的代码示例,不只是讲概念,更注意于实践。课程内容安排 本课程通过四章内容对Java Agent相关知识进行讲解:第一章,介绍Agent Jar的三个组成部分:Manifest、Agent Class和ClassFileTransformer。第章,介绍Agent Jar的两种启动方式:从命令行启动和使用Attach机制启动。第三章,介绍如何利用Instrumentation API来实现Agent Jar的功能。第四章,Java Agent的应用与技巧。 通过本课程的学习,让同学们更好地建立起一个完整的知识体系:  讲师介绍我叫刘森,南京师范大学研究生毕业,2015年获得信息系统项目管理师(高级),2014年获得系统集成项目管理工程师(中级)。 目前,我的课程都是围绕着“Java字节码”技术展开: 《Java Agent基础篇》是在一个运行JVM当中提供修改字节码的机会《Java ASM系列》(免费课程)是一个操作字节码的类库《Java 8 ClassFile》专注于字节码的理论知识,入选为“51CTO数字化人才证书项目认证课程” 因此,我对字节码技术有较为深入的研究和理解,大家想学习字节码的技术可以找我:字节码技术找刘森,轻松学习又省心~~~ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值