Jdk8 动态编译 Java 源码为 Class 文件(三)

news2024/11/25 10:34:51

Jdk8 动态编译 Java 源码为 Class 文件

  • 一.JDK版本
  • 二.工程介绍
    • 1.依赖
    • 2.启动类
    • 3.配置类(用于测试依赖注入)
    • 4.工具类
      • 1.Java 源码文件读取类
      • 2.SpringBoot 容器实例管理类
    • 5.测试类
      • 1.抽象类
      • 2.接口类
      • 3.默认抽象实现
      • 4.默认接口实现
    • 6.接口类
      • 1.测试接口
      • 2.类重载控制接口
    • 7.动态编译类
      • 1.类加载器
      • 2.类管理器
      • 3.类对象
      • 4.Java 文件类
    • 8.配置文件
  • 三.测试
    • 1.测试用类
      • 1.测试类原类修改
    • 2.测试
      • 1.原类直接打印
      • 2.原类修改
  • 四.Jar 反编译记录

一.JDK版本

在这里插入图片描述

二.工程介绍

动态源码编译需要自定义类加载器,JVM会根据所属类加载器和全类名判断是否为同一个类,所以动态编译和加载时,同一个类无法用同一个类加载器加载两次,除非从 JVM 层面移除旧的类。
同一个类由不同类加载器加载时,JVM 会判断为非同类,所以无法直接实例化后强转为同一类型的实例,需要基于接口、抽象类来实现动态替换

在这里插入图片描述

1.依赖

<?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>spring-dynamic</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>2.7.4</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8.0_341</version>
            <scope>system</scope>
            <systemPath>${JAVA_HOME}\lib\tools.jar</systemPath>
        </dependency>
    </dependencies>
    <build>
        <finalName>dynamic-demo</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.7.4</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <!-- for tools.jar -->
<!--                <configuration>-->
<!--                    <includeSystemScope>true</includeSystemScope>-->
<!--                </configuration>-->
            </plugin>
        </plugins>
    </build>
</project>

2.启动类

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author
 * @date 2023-08-10 9:51
 * @since 1.8
 */
@SpringBootApplication
public class DynamicApp {
    public static void main(String[] args) {
        SpringApplication.run(DynamicApp.class,args);
    }
}

3.配置类(用于测试依赖注入)

package com.example.config;

import org.springframework.stereotype.Component;

/**
 * @author moon
 * @date 2023-08-30 14:58
 * @since 1.8
 */
@Component
public class KafkaConfig {

    public void getConfig(){
        System.out.println("kafka config");
    }
}

4.工具类

1.Java 源码文件读取类

package com.example.util;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

/**
 * @author moon
 * @date 2023-08-31 9:25
 * @since 1.8
 */
public class FileUtil {

    public static String readJson(String filePath){

        if (org.springframework.util.StringUtils.hasLength(filePath)){
            InputStream inputStream = null;
            StringBuilder builder = new StringBuilder();
            try {
                int batchSize = 2048;
                inputStream = new FileInputStream(filePath);
                byte[] temp = new byte[batchSize];
                int read;
                while ((read = inputStream.read(temp)) != -1){
                    if (read < batchSize){
                        byte[] tail = Arrays.copyOf(temp,read);
                        builder.append(new String(tail));
                    } else {
                        builder.append(new String(temp));
                    }
                }
                return builder.toString();
            } catch (IOException e) {
                return "";
            } finally {
                if (null != inputStream){
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }

        }
        return "";
    }


}

2.SpringBoot 容器实例管理类

package com.example.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author moon
 * @date 2023-08-31 11:18
 * @since 1.8
 */
@Component
public class SpringBeanUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    private static ConfigurableApplicationContext context ;

    /**
     * 获取 Bean 工厂
     */
    private static DefaultListableBeanFactory beanFactory ;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringBeanUtil.applicationContext = applicationContext;
        SpringBeanUtil.context= (ConfigurableApplicationContext) applicationContext;
        SpringBeanUtil.beanFactory= (DefaultListableBeanFactory) context.getBeanFactory();
    }

    /**
     * 替换 bean 并获取新的
     * @param beanName
     * @param clazz
     * @return
     */
    public static Object replace(String beanName,Class clazz){
        //卸载
        unregister(beanName);
        //注册
        register(beanName,clazz);
        //获取
        return getBean(beanName);
    }

    /**
     * 注册 Bean
     * @param clazz
     */
    public static void register(String beanName,Class clazz){

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);

        BeanDefinition definition = builder.getBeanDefinition();

        //为 definition 设置额外属性
        definition.setScope("singleton");

        //注册
        beanFactory.registerBeanDefinition(beanName,definition);
    }

    /**
     * 卸载 Bean
     * @param beanName
     */
    public static void unregister(String beanName){
        if (beanFactory.containsBean(beanName)){
            beanFactory.removeBeanDefinition(beanName);
        }
    }

    /**
     * 获取所有 Bean
     * @return
     */
    public static List<String> getBeans(){
        String[] names = applicationContext.getBeanDefinitionNames();
        List<String> beans = new ArrayList<>(names.length);
        for (String name:names){
            beans.add(applicationContext.getBean(name).getClass().getName());
        }
        return beans;
    }

    /**
     * bean 是否存在
     * @param name
     * @return
     */
    public static boolean isBeanExist(String name){
        return applicationContext.containsBean(name);
    }

    /**
     * 通过名称获取 Bean
     * @param name
     * @return
     * @param <T>
     * @throws BeansException
     */
    public static <T> T getBean(String name) throws BeansException{
        return (T) applicationContext.getBean(name);
    }

    /**
     * 通过类型获取 Bean
     * @param clazz
     * @return
     * @param <T>
     * @throws BeansException
     */
    public static <T> T getBean(Class<?> clazz) throws BeansException{
        return (T) applicationContext.getBean(clazz);
    }

    /**
     * 获取指定类型的 Bean 的名称
     * @param className
     * @return
     * @throws BeansException
     */
    public static List<String> getBeanName(String className) throws BeansException, ClassNotFoundException {
        Class<?> clazz = Class.forName(className);
        return Arrays.asList(applicationContext.getBeanNamesForType(clazz));
    }
}

5.测试类

1.抽象类

package com.example.service;

/**
 * @author moon
 * @date 2023-08-30 14:15
 * @since 1.8
 */
public abstract class TestAbstract {

    /**
     * 抽象方法
     * @param str
     */
    public abstract void hand(String str);
}

2.接口类

package com.example.service;

/**
 * @author moon
 * @date 2023-08-31 10:58
 * @since 1.8
 */
public interface TestService {

    /**
     * 处理
     * @param str
     */
    void hand(String str);

}

3.默认抽象实现

package com.example.service.impl;

import com.example.config.KafkaConfig;
import com.example.service.TestAbstract;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author moon
 * @date 2023-08-31 11:01
 * @since 1.8
 */
@Component
public class TestAbstractImpl extends TestAbstract {

    @Autowired
    KafkaConfig config;

    @Value("${my.ip}")
    String ip;

    @Override
    public void hand(String str) {
        config.getConfig();
        System.out.println(str);
        System.out.println(ip);
    }
}

4.默认接口实现

package com.example.service.impl;

import com.example.config.KafkaConfig;
import com.example.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author moon
 * @date 2023-08-31 10:59
 * @since 1.8
 */
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    KafkaConfig config;

    @Override
    public void hand(String str) {
        config.getConfig();
        System.out.println("hand: " + this);
    }
}

6.接口类

1.测试接口

package com.example.controller;

import com.example.service.TestAbstract;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author moon
 * @date 2023-08-30 17:27
 * @since 1.8
 */
@RestController
@RequestMapping("/test")
public class TestController {

    /**
     * 引入抽象类依赖
     */
    @Resource(name = "testAbstractImpl")
    TestAbstract testAbstract;

    @GetMapping("/print")
    public void print(String content){
        testAbstract.hand("Hello : " + content);
    }
}

2.类重载控制接口

package com.example.controller;


import com.example.dynamic.MemoryClassLoader;
import com.example.service.TestAbstract;
import com.example.util.FileUtil;
import com.example.util.SpringBeanUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;

/**
 * @author moon
 * @date 2023-08-30 14:10
 * @since 1.8
 */
@RestController
@RequestMapping("/reload")
public class Reload extends ClassLoader{


    @GetMapping("/re")
    public void re(String name,String beanName) throws InstantiationException, IllegalAccessException, InvocationTargetException, MalformedURLException, NoSuchMethodException, ClassNotFoundException {

        String className = "com.example.service.impl." + name;
        String classPath = "C:\\Users\\administrator\\Desktop\\jar\\"+name+".java";
        String javaStr = FileUtil.readJson(classPath);

        /**
         * 定义新的 MemoryClassLoader 同一个 MemoryClassLoader 不能两次加载同一个类
         */
        MemoryClassLoader loader = new MemoryClassLoader();
        loader.registerJava(className,javaStr);
        Class clazz = loader.findClass(className);

        TestAbstract handler = (TestAbstract) clazz.getDeclaredConstructor().newInstance();

        // 将外部Jar包中的Bean注入到Spring容器中

        Object obj = SpringBeanUtil.replace(beanName,clazz);

        TestAbstract test = (TestAbstract) obj;

        test.hand("sss");

        System.out.println();

    }

}

7.动态编译类

1.类加载器

package com.example.dynamic;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Component;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author moon
 */
@Slf4j
@Component
public class MemoryClassLoader extends URLClassLoader {

    /**
     * 缓存字节码
     */
    private Map<String, byte[]> classBytesMap = new ConcurrentHashMap<>();

    /**
     * 构造
     */
    public MemoryClassLoader() {
        super(new URL[0], MemoryClassLoader.class.getClassLoader());
    }

    /**
     * 注册 Java 字符串到内存类加载器中
     *
     * @param className 类名字
     * @param javaStr   Java字符串
     */
    public void registerJava(String className, String javaStr) {
        try {
            this.classBytesMap.putAll(compile(className, javaStr));
        } catch (Exception e) {
            log.error("register java class exception:");
        }
    }

    /**
     * 编译 Java 源码
     *
     * @param className 类名字
     * @param javaStr   Java代码
     * @return class 二进制
     */
    private Map<String, byte[]> compile(String className, String javaStr) {
        //初始化编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //获取Java文件管理器
        try (MemoryJavaFileManager manager = new MemoryJavaFileManager()) {
            JavaFileObject javaFileObject = manager.makeStringSource(className, javaStr);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
            if (task.call()) {
                return manager.getClassBytes();
            }
        } catch (Exception e) {
            log.error("compile java str exception:",e);
        }
        return null;
    }

    /**
     * 获取 Class
     * @param name the name of the class
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] buf = classBytesMap.get(name);
        if (buf == null) {
            return super.findClass(name);
        }
        classBytesMap.remove(name);
        return defineClass(name, buf, 0, buf.length);
    }

    /**
     * 获取jar包所在路径
     *
     * @return jar包所在路径
     */
    public static String getPath() {
        ApplicationHome home = new ApplicationHome(MemoryJavaFileManager.class);
        String path = home.getSource().getPath();
        return path;
    }

    /**
     * 判断是否jar模式运行
     *
     * @return
     */
    public static boolean isJar() {
        return getPath().endsWith(".jar");
    }

}

2.类管理器

package com.example.dynamic;

import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
import org.springframework.boot.loader.jar.JarFile;
import javax.tools.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.stream.Collectors;

/**
 * Java 文件管理器
 * 用于加载 SpringBoot 下面的依赖资源
 *
 * @author moon
 * @date 2023-08-10 9:58
 * @since 1.8
 */
public class MemoryJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {

    /**
     * 缓存字节码
     */
    final Map<String, byte[]> classBytesMap = new ConcurrentHashMap<>();

    /**
     * 缓存文件对象
     */
    final Map<String, List<JavaFileObject>> classObjectPackageMap = new ConcurrentHashMap<>();

    /**
     * 文件管理器
     */
    private JavacFileManager javaFileManager;

    /**
     * 包名 / JavaFile(.java)
     */
    public final static Map<String, List<JavaFileObject>> CLASS_OBJECT_PACKAGE_MAP = new ConcurrentHashMap<>();

    /**
     * 锁对象
     */
    private static final Object lock = new Object();

    /**
     * 初始化标识
     */
    private static boolean isInit = false;

    /**
     * 初始化
     */
    public void init() {
        try {
            JarFile tempJarFile;
            List<JavaFileObject> javaFiles;
            String packageName,className;
            //获取当前 Jar 包
            String jarBaseFile = MemoryClassLoader.getPath();
            //加载 Jar 包
            JarFile jarFile = new JarFile(new File(jarBaseFile));
            //取包自身文件
            for (JarEntry entry:jarFile){
                //SpringBoot repackage 打包 class 文件带一个 BOOT-INF/classes/ 之后才是包名
                String name = entry.getName().replace("BOOT-INF/classes/","");
                String classPath = name.replace("/", ".");
                //如果不是 class 文件跳过
                if (name.endsWith(".class")){
                    //取出包名
                    packageName = classPath.substring(0, name.lastIndexOf("/"));
                    //取类名
                    className = classPath.replace(".class", "");
                    //创建集合
                    javaFiles = Optional.ofNullable(CLASS_OBJECT_PACKAGE_MAP.get(packageName)).orElse(new ArrayList<>()) ;
                    //取 JavaFile
                    filterClass(packageName,className,jarFile.getUrl(),entry.getName(),javaFiles);
                }
            }
            //遍历取内部 Jar 包
            List<JarEntry> entries = jarFile.stream().filter(jarEntry -> {
                return jarEntry.getName().endsWith(".jar");
            }).collect(Collectors.toList());
            // Jar File
            for (JarEntry entry : entries) {
                //取内部文件
                tempJarFile = jarFile.getNestedJarFile(jarFile.getEntry(entry.getName()));
                //跳过工具包 Jar
                if (tempJarFile.getName().contains("tools.jar")) {
                    continue;
                }
                //遍历 Jar 文件
                Enumeration<JarEntry> tempEntriesEnum = tempJarFile.entries();
                while (tempEntriesEnum.hasMoreElements()) {
                    JarEntry jarEntry = tempEntriesEnum.nextElement();
                    String classPath = jarEntry.getName().replace("/", ".");
                    //如果不是 class 文件跳过
                    if (!classPath.endsWith(".class") || jarEntry.getName().lastIndexOf("/") == -1) {
                        continue;
                    } else {
                        //取出包名
                        packageName = classPath.substring(0, jarEntry.getName().lastIndexOf("/"));
                        //取类名
                        className = jarEntry.getName().replace("/", ".").replace(".class", "");
                        //创建集合
                        javaFiles = Optional.ofNullable(CLASS_OBJECT_PACKAGE_MAP.get(packageName)).orElse(new ArrayList<>()) ;
                        //取 JavaFile
                        filterClass(packageName,className,tempJarFile.getUrl(),jarEntry.getName(),javaFiles);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        isInit = true;
    }

    /**
     * 取 class
     * @param packageName
     * @param className
     * @param url
     * @param entryName
     * @param javaFiles
     */
    private void filterClass(String packageName,String className,URL url,String entryName,List<JavaFileObject> javaFiles) throws MalformedURLException {
        //取 JavaFile
        javaFiles.add(new MemorySpringBootInfoJavaClassObject(className, new URL(url, entryName), javaFileManager));
        //缓存 Package / JavaFile
        CLASS_OBJECT_PACKAGE_MAP.put(packageName, javaFiles);
    }

    /**
     * 构造
     */
    MemoryJavaFileManager() {
        super(getStandardFileManager(null, null, null));
        this.javaFileManager = (JavacFileManager) fileManager;
    }

    /**
     * 获取文件对象集合
     * @param packageName
     * @return
     */
    public List<JavaFileObject> getLibJarsOptions(String packageName) {
        synchronized (lock) {
            if (!isInit) {
                init();
            }
        }
        return CLASS_OBJECT_PACKAGE_MAP.get(packageName);
    }

    @Override
    public Iterable<JavaFileObject> list(Location location,
                                         String packageName,
                                         Set<JavaFileObject.Kind> kinds,
                                         boolean recurse)
            throws IOException {


        if ("CLASS_PATH".equals(location.getName()) && MemoryClassLoader.isJar()) {
            List<JavaFileObject> result = getLibJarsOptions(packageName);
            if (result != null) {
                return result;
            }
        }

        Iterable<JavaFileObject> it = super.list(location, packageName, kinds, recurse);

        if (kinds.contains(JavaFileObject.Kind.CLASS)) {
            final List<JavaFileObject> javaFileObjectList = classObjectPackageMap.get(packageName);
            if (javaFileObjectList != null) {
                if (it != null) {
                    for (JavaFileObject javaFileObject : it) {
                        javaFileObjectList.add(javaFileObject);
                    }
                }
                return javaFileObjectList;
            } else {
                return it;
            }
        } else {
            return it;
        }
    }

    @Override
    public String inferBinaryName(Location location, JavaFileObject file) {
        if (file instanceof MemoryInputJavaClassObject) {
            return ((MemoryInputJavaClassObject) file).inferBinaryName();
        }
        return super.inferBinaryName(location, file);
    }

    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind,
                                               FileObject sibling) throws IOException {
        if (kind == JavaFileObject.Kind.CLASS) {
            return new MemoryOutputJavaClassObject(className);
        } else {
            return super.getJavaFileForOutput(location, className, kind, sibling);
        }
    }

    /**
     * 设置源码
     * @param className
     * @param code
     * @return
     */
    JavaFileObject makeStringSource(String className, final String code) {
        String classPath = className.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension;

        return new SimpleJavaFileObject(URI.create("string:///" + classPath), JavaFileObject.Kind.SOURCE) {
            @Override
            public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
                return CharBuffer.wrap(code);
            }
        };
    }

    /**
     * 设置字节码
     * @param className
     * @param bs
     */
    void makeBinaryClass(String className, final byte[] bs) {
        JavaFileObject javaFileObject = new MemoryInputJavaClassObject(className, bs);
        String packageName = "";
        int pos = className.lastIndexOf('.');
        if (pos > 0) {
            packageName = className.substring(0, pos);
        }
        List<JavaFileObject> javaFileObjectList = classObjectPackageMap.get(packageName);
        if (javaFileObjectList == null) {
            javaFileObjectList = new LinkedList<>();
            javaFileObjectList.add(javaFileObject);

            classObjectPackageMap.put(packageName, javaFileObjectList);
        } else {
            javaFileObjectList.add(javaFileObject);
        }
    }

    /**
     * 内部输入类
     */
    class MemoryInputJavaClassObject extends SimpleJavaFileObject {
        final String className;
        final byte[] bs;

        MemoryInputJavaClassObject(String className, byte[] bs) {
            super(URI.create("string:///" + className.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS);
            this.className = className;
            this.bs = bs;
        }

        @Override
        public InputStream openInputStream() {
            return new ByteArrayInputStream(bs);
        }

        public String inferBinaryName() {
            return className;
        }
    }

    /**
     * 内部输出类
     */
    class MemoryOutputJavaClassObject extends SimpleJavaFileObject {
        final String className;

        MemoryOutputJavaClassObject(String className) {
            super(URI.create("string:///" + className.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS);
            this.className = className;
        }

        @Override
        public OutputStream openOutputStream() {
            return new FilterOutputStream(new ByteArrayOutputStream()) {
                @Override
                public void close() throws IOException {
                    out.close();
                    ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
                    byte[] bs = bos.toByteArray();
                    classBytesMap.put(className, bs);
                    makeBinaryClass(className, bs);
                }
            };
        }
    }

    /**
     * 获取编译结果
     * @return
     */
    public Map<String, byte[]> getClassBytes() {
        return new HashMap<>(this.classBytesMap);
    }

    /**
     * 刷新
     * @throws IOException
     */
    @Override
    public void flush() throws IOException {
    }

    /**
     * 关闭
     * @throws IOException
     */
    @Override
    public void close() throws IOException {
        classBytesMap.clear();
    }

    /**
     * 自定义 Java 文件管理器
     *
     * @param var1
     * @param var2
     * @param var3
     * @return
     */
    public static SpringJavaFileManager getStandardFileManager(DiagnosticListener<? super JavaFileObject> var1, Locale var2, Charset var3) {
        Context var4 = new Context();
        var4.put(Locale.class, var2);
        if (var1 != null) {
            var4.put(DiagnosticListener.class, var1);
        }
        PrintWriter var5 = var3 == null ? new PrintWriter(System.err, true) : new PrintWriter(new OutputStreamWriter(System.err, var3), true);
        var4.put(Log.outKey, var5);
        return new SpringJavaFileManager(var4, true, var3);
    }
}

3.类对象

package com.example.dynamic;

import com.sun.tools.javac.file.BaseFileObject;
import com.sun.tools.javac.file.JavacFileManager;

import javax.tools.JavaFileObject;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

/**
 * 用来读取 spring boot 的 class
 *
 * @author moon
 * @date 2023-08-10 9:57
 * @since 1.8
 */
public class MemorySpringBootInfoJavaClassObject extends BaseFileObject {
    private final String className;
    private URL url;

    public MemorySpringBootInfoJavaClassObject(String className, URL url, JavacFileManager javacFileManager) {
        super(javacFileManager);
        this.className = className;
        this.url = url;
    }

    @Override
    public JavaFileObject.Kind getKind() {
        return JavaFileObject.Kind.valueOf("CLASS");
    }

    @Override
    public URI toUri() {
        try {
            return url.toURI();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String getName() {
        return className;
    }

    @Override
    public InputStream openInputStream() {
        try {
            return url.openStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

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

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
        return null;
    }

    @Override
    public Writer openWriter() throws IOException {
        return null;
    }

    @Override
    public long getLastModified() {
        return 0;
    }

    @Override
    public boolean delete() {
        return false;
    }

    public String inferBinaryName() {
        return className;
    }

    @Override
    public String getShortName() {
        return className.substring(className.lastIndexOf("."));
    }

    @Override
    protected String inferBinaryName(Iterable<? extends File> iterable) {
        return className;
    }


    @Override
    public boolean equals(Object o) {
        return false;
    }

    @Override
    public int hashCode() {
        return 0;
    }


    @Override
    public boolean isNameCompatible(String simpleName, JavaFileObject.Kind kind) {
        return false;
    }
}

4.Java 文件类

package com.example.dynamic;

import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import java.io.File;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.Iterator;

/**
 * Java 文件管理器
 *
 * @author moon
 * @date 2023-08-10 9:53
 * @since 1.8
 */
public class SpringJavaFileManager extends JavacFileManager {

    /**
     *
     * @param context
     * @param b
     * @param charset
     */
    public SpringJavaFileManager(Context context, boolean b, Charset charset) {
        super(context, b, charset);
    }

    /**
     * 重写类加载器
     * @param location a location
     * @return
     */
    @Override
    public ClassLoader getClassLoader(Location location) {
        nullCheck(location);
        Iterable var2 = this.getLocation(location);
        if (var2 == null) {
            return null;
        } else {
            ListBuffer var3 = new ListBuffer();
            Iterator var4 = var2.iterator();
            while (var4.hasNext()) {
                File var5 = (File) var4.next();
                try {
                    var3.append(var5.toURI().toURL());
                } catch (MalformedURLException var7) {
                    throw new AssertionError(var7);
                }
            }
            return this.getClassLoader((URL[]) var3.toArray(new URL[var3.size()]));
        }
    }

    /**
     * 获取 LaunchedURLClassLoader 加载器
     *
     * @param var1
     * @return
     */
    @Override
    protected ClassLoader getClassLoader(URL[] var1) {
        ClassLoader var2 = this.getClass().getClassLoader();
        try {
            Class loaderClass = Class.forName("org.springframework.boot.loader.LaunchedURLClassLoader");
            Class[] var4 = new Class[]{URL[].class, ClassLoader.class};
            Constructor var5 = loaderClass.getConstructor(var4);
            return (ClassLoader) var5.newInstance(var1, var2);
        } catch (Throwable var6) {
        }
        return new URLClassLoader(var1, var2);
    }

}

8.配置文件

server:
  port: 8082
my:
  ip: 123.456.789.1

三.测试

启动 Java 服务(Xbootclasspath 引入 tools.jar)

java -Xbootclasspath/a:C:\Progra~1\Java\jdk1.8.0_341\jre\lib\tools.jar -jar dynamic-demo.jar

在这里插入图片描述

1.测试用类

1.测试类原类修改

package com.example.service.impl;

import com.example.config.KafkaConfig;
import com.example.service.TestAbstract;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author moon
 * @date 2023-08-31 11:01
 * @since 1.8
 */
@Component
public class TestAbstractImpl extends TestAbstract {

    @Autowired
    KafkaConfig config;

    @Value("${my.ip}")
    String ip;

    @Override
    public void hand(String str) {
        config.getConfig();
        System.out.println("How are you" + str);
        System.out.println(ip);
    }
}

2.测试

1.原类直接打印

http://127.0.0.1:8082/test/print?content=lisi

在这里插入图片描述

2.原类修改

重载
http://127.0.0.1:8082/reload/re?name=TestAbstractImpl&beanName=testAbstractImpl

在这里插入图片描述
调用测试
http://127.0.0.1:8082/test/print?content=zhangsan

在这里插入图片描述

四.Jar 反编译记录

1.IDEA 安装插件 Java Decompiler

在这里插入图片描述

2.找到插件包(可以将该Jar包取到其他位置使用):C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.2\plugins\java-decompiler\lib

创建一个 SRC 目录
反编译命令
%JAVA_HOME_19%\java -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true tools.jar src

在这里插入图片描述

结果是一个 tools.jar 文件,将其扩展名改为 .zip 并解压就可以看到实际已经是 java 文件了

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/969789.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数学建模:模糊综合评价分析

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 数学建模&#xff1a;模糊综合评价分析 文章目录 数学建模&#xff1a;模糊综合评价分析综合评价分析常用评价方法一级模糊综合评价综合代码 多级模糊综合评价总结 综合评价分析 构成综合评价类问题的五个…

Emmet 使用笔记小结

Emmet 使用笔记小结 最近在跟视频走 CSS 的教程&#xff0c;然后要写很多的 HTML 结构&#xff0c;就想着总结一下 Emmet 的语法。 Emmet 是一个工具可以用来加速 HTML 和 CSS 的开发过程&#xff0c;不过 emmet 只支持 HTML & XML 文件结构&#xff0c;所以我个人觉得对…

【JavaSE】面试01

文章目录 1. JDK、JRE、JVM之间的关系2. 补充3. 面试题&#xff1a;重载和重写的区别&#xff1f;4. super和this5. &#xff08;重点&#xff01;&#xff01;&#xff09;若父类和子类均有静态代码块、实例代码块以及无参构造方法&#xff0c;则继承关系上的执行顺序&#xf…

如何解决分库分表主键问题?

分析&回答 从问题角度出发&#xff1a;我们需要一个全局唯一的 id 来支持&#xff0c;排序问题等。这都是你实际生产环境中必须考虑的问题。可以先看下我们之前的文章分布式系统唯一ID如何生成&#xff1f; 雪花算法和雪花算法的变种是大家常用的 喵呜面试助手&#xff1…

Python字节码文件

迷途小书童的 Note 读完需要 5分钟 速读仅需 2 分钟 大家好&#xff0c;我是迷途小书童&#xff01; 今天给大家介绍一个神奇的文件 -- pyc 文件&#xff0c;它能加速 Python 程序的执行速度&#xff0c;同时也能起到保护源码的作用。 1 什么是 pyc 文件? pyc 文件是经过编译的…

PYTHON-“人生重开模拟器“

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是Aileen★。希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f194;本文由 Aileen_0v0★ 原创 CSDN首发&#x1f412; 如需转载还…

SAP-PP:基础概念笔记-5(物料主数据的MRP1~4视图)

文章目录 前言一、MRP1视图Base Unit of Measure&#xff08;UoM&#xff09;MRP 组采购组ABC 指示器Plant-Specific Material Status 特定的工厂物料状态MRP 类型 MRP TypeMRP 类型 MRP TypeMaster Production Scheduling(MPS) 主生产计划基于消耗的计划(CBP)再订货点Reorder-…

【业务功能篇91】微服务-springcloud-多线程-线程池执行顺序

一、线程的实现方式 1. 线程的实现方式 1.1 继承Thread class ThreadDemo01 extends Thread{Overridepublic void run() {System.out.println("当前线程:" Thread.currentThread().getName());} }1.2 实现Runnable接口 class ThreadDemo02 implements Runnable{…

介绍几个搜索引擎

Google&#xff1a;全球最大的搜索引擎&#xff0c;提供全面的搜索服务&#xff0c;包括网页、图片、视频、新闻、地图等。 Baidu&#xff1a;中国最大的搜索引擎&#xff0c;提供类似于Google的全面搜索服务&#xff0c;同时也有网盘、知道等功能。 Bing&#xff1a;微软公司…

Linux持续学习者的实用命令:sed

引言 作为一名Linux持续学习者&#xff0c;我们经常需要对文本内容进行处理或修改&#xff0c;这时候sed命令就能派上用场了。sed是一个强大的流式文本编辑器&#xff0c;它可以在读取文本时进行修改并输出&#xff0c;支持各种复杂的字符串替换、内容删除、行插入等操作。在本…

Linux的目录结构特点

Linux的目录结构特点 1、使用树形目录结构来组织和管理文件。 2、整个系统只有一个根目录&#xff08;树根&#xff09;&#xff0c;Linux的根目录用“/”表示。 3、其他所有分区以及外部设备&#xff08;如硬盘&#xff0c;光驱等&#xff09;都是以根目录为起点&#xff0…

[华为云云服务器评测] Unbutnu添加SSH Key、编译启动Springboot项目

系列文章目录 第一章 [linux实战] 华为云耀云服务器L实例 Java、node环境配置 第二章 [linux实战] Unbutnu添加SSH Key、启动Springboot项目 文章目录 系列文章目录前言一、任务拆解二、配置git,添加SSH Key2.1、登录远程主机2.2、配置git用户名和邮箱2.3、生成SSH key2.4、查…

说说redo log 与 undo log

redo log redo log叫做重做日志.用于解决数据库事物提交 还未刷入磁盘,服务器down机导致的数据丢失的问题。 InnoDB作为MySQL的存储引擎&#xff0c;数据存储在磁盘中&#xff0c;如果每次读写数据都要操作磁盘IO效率会很低&#xff0c;为此InnoDB提供了缓存(Buffer Pool)&am…

无涯教程-JavaScript - STDEVP函数

STDEVP函数替代Excel 2010中的STDEV.P函数。 描述 该函数根据作为参数给出的整个总体计算标准偏差。标准偏差是对值与平均值(平均值)的分散程度的度量。 语法 STDEVP (number1,[number2],...)争论 Argument描述Required/OptionalNumber1The first number argument corresp…

文件包含漏洞及漏洞复现

文件包含漏洞 1. 文件包含概述 程序开发人员通常会把可重复使用函数或语句写到单个文件中&#xff0c;形成“封装”。在使用某个功能的时候&#xff0c;直接调用此文件&#xff0c;无需再次编写&#xff0c;提高代码重用性&#xff0c;减少代码量。这种调用文件的过程通常称为…

机器学习与数据分析

【数据清洗】 异常检测 孤立森林&#xff08;Isolation Forest&#xff09;从原理到实践 效果评估&#xff1a;F-score 【1】 保护隐私的时间序列异常检测架构 概率后缀树 PST – &#xff08;异常检测&#xff09; 【1】 UEBA架构设计之路5&#xff1a; 概率后缀树模型 【…

单独创建一个线程并执行

C并发编程入门 目录 STL 写法 #include <thread> #include <iostream> using namespace std;void thread_fun(void) {cout << "one STL thread!" << endl; }int main(void) {std::thread thread1(thread_fun);thread1.join();return 0; }其…

数字IC验证——PSS可移植测试用例

PSS是Accellera组织定义的测试用例生成规范&#xff0c;其思想是定义一个抽象模型&#xff0c;EDA工具可以从中生成适用于每个设计层次结构和每个验证平台的测试&#xff0c;即PSS定义了统一的测试场景&#xff0c;而场景的使用可以横跨不同验证层次和配置。 这种特性决定了PSS…

docker笔记8:Docker网络

1.是什么 1.1 docker不启动&#xff0c;默认网络情况 ens33 lo virbr0 在CentOS7的安装过程中如果有选择相关虚拟化的的服务安装系统后&#xff0c;启动网卡时会发现有一个以网桥连接的私网地址的virbr0网卡(virbr0网卡&#xff1a;它还有一个固定的默认IP地址192.168.122…

快速掌握STM32工程创建

STM32 工程创建-- 使用Keil uVision5 软件 晓理紫 1 准备库函数库 STM32F10x_StdPeriph_Lib_V3.5.0 VX 搜索“晓丽紫”关注回复STM32F10x即可下载 2、创建一个目录用来存放工程 STM32Study STM32Study/study1 存放本次工程目录 3、打开Keil uVision5 创建工程 4、选择型号(根据…