Jdk19 动态编译 Java 源码为 Class 文件(一)

一.背景

1.Jdk 版本

版本查看命令:java -version

在这里插入图片描述

2.需求

本来想看下项目热部署的实现,比如 SpringBoot 不停机热加载 Jar 实现功能修改;后来看到 Jdk 支持源码动态编译,如果可以实现,那么就可以在线直接修改代码,再利用 SpringBoot 管理起来,替换旧的 Bean,实现功能修改。可能实际应用场景不多,可以做应急修改,线上服务最终还是需要把修改后的代码重新部署更为稳妥。

其实动态修改代码还可以通过 Arthas 实现,包括反编译、编译等更多功能

二.Java 源码动态编译实现

源码编译需要用到的关键类:

说明
JavaCompiler 编译器 ToolProvider.getSystemJavaCompiler();
SimpleJavaFileObject 文件对象类,可以表示源码、类文件
ClassLoader 顶层类加载器,抽象类
ForwardingJavaFileManager 文件管理器

项目结构如图

在这里插入图片描述

1.Maven 依赖

暂时只是一个 Maven 项目,未引入其他依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>DynamicDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>19</maven.compiler.source>
        <maven.compiler.target>19</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

2.源码包装类

基于 SimpleJavaFileObject 扩展,用于封装类名、源码信息

package org.example.demo.util;

import javax.tools.SimpleJavaFileObject;
import java.io.IOException;
import java.net.URI;

/**
 * @author moon
 * @date 2023-02-15 20:32
 * @since 1.8
 */
public class CustomSourceCode extends SimpleJavaFileObject {
   
   

    /**
     * 类名称
     */
    private String className;

    /**
     * 类源码
     */
    private String contents;

    /**
     * 源码初始化
     * @param className
     * @param contents
     */
    public CustomSourceCode(String className, String contents) {
   
   
        super(URI.create("string:///" + className.replace('.', '/')
                + Kind.SOURCE.extension), Kind.SOURCE);
        this.contents = contents;
        this.className = className;
    }

    /**
     * 获取类名
     * @return
     */
    public String getClassName() {
   
   
        return className;
    }

    /**
     * 源码字符序列
     * @param ignoreEncodingErrors ignore encoding errors if true
     * @return
     * @throws IOException
     */
    public CharSequence getCharContent(boolean ignoreEncodingErrors)
            throws IOException {
   
   
        return contents;
    }
}

3.Java 文件对象封装类

基于 SimpleJavaFileObject 实现,封装了类名、类字节输出流信息

package org.example.demo.util;

import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * @author moon
 * @date 2023-02-15 20:52
 * @since 1.8
 */
public class CustomJavaFileObject extends SimpleJavaFileObject {
   
   

    /**
     * 类名称
     */
    private String className;

    /**
     * 输出的字节码流
     */
    private ByteArrayOutputStream toByteArray = new ByteArrayOutputStream();

    /**
     * Construct a SimpleJavaFileObject of the given kind and with the
     * given URI.
     *
     * @param className
     */
    public CustomJavaFileObject(String className) throws URISyntaxException {
   
   
        super(new URI(className), Kind.CLASS);
        this.className = className;
    }

    @Override
    public OutputStream openOutputStream() throws IOException {
   
   
        return toByteArray;
    }

    /**
     * 获取类名
     * @return
     */
    public String getClassName() {
   
   
        return className;
    }

    /**
     * 获取字节信息
     * @return
     */
    public byte[] getByteCode() {
   
   
        return toByteArray.toByteArray();
    }
}

4.文件管理器封装类

package org.example.demo.util;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪悟道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值