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 ;@Overridepublic 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 {@AutowiredKafkaConfig config;@Value("${my.ip}")String ip;@Overridepublic 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 {@AutowiredKafkaConfig config;@Overridepublic 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*/@Overridepublic 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<>()) ;//取 JavaFilefilterClass(packageName,className,jarFile.getUrl(),entry.getName(),javaFiles);}}//遍历取内部 Jar 包List<JarEntry> entries = jarFile.stream().filter(jarEntry -> {return jarEntry.getName().endsWith(".jar");}).collect(Collectors.toList());// Jar Filefor (JarEntry entry : entries) {//取内部文件tempJarFile = jarFile.getNestedJarFile(jarFile.getEntry(entry.getName()));//跳过工具包 Jarif (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<>()) ;//取 JavaFilefilterClass(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 {//取 JavaFilejavaFiles.add(new MemorySpringBootInfoJavaClassObject(className, new URL(url, entryName), javaFileManager));//缓存 Package / JavaFileCLASS_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);}@Overridepublic 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;}}@Overridepublic String inferBinaryName(Location location, JavaFileObject file) {if (file instanceof MemoryInputJavaClassObject) {return ((MemoryInputJavaClassObject) file).inferBinaryName();}return super.inferBinaryName(location, file);}@Overridepublic 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) {@Overridepublic 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;}@Overridepublic 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;}@Overridepublic OutputStream openOutputStream() {return new FilterOutputStream(new ByteArrayOutputStream()) {@Overridepublic 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*/@Overridepublic void flush() throws IOException {}/*** 关闭* @throws IOException*/@Overridepublic 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;}@Overridepublic JavaFileObject.Kind getKind() {return JavaFileObject.Kind.valueOf("CLASS");}@Overridepublic URI toUri() {try {return url.toURI();} catch (URISyntaxException e) {e.printStackTrace();}return null;}@Overridepublic String getName() {return className;}@Overridepublic InputStream openInputStream() {try {return url.openStream();} catch (IOException e) {e.printStackTrace();}return null;}@Overridepublic OutputStream openOutputStream() throws IOException {return null;}@Overridepublic CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {return null;}@Overridepublic Writer openWriter() throws IOException {return null;}@Overridepublic long getLastModified() {return 0;}@Overridepublic boolean delete() {return false;}public String inferBinaryName() {return className;}@Overridepublic String getShortName() {return className.substring(className.lastIndexOf("."));}@Overrideprotected String inferBinaryName(Iterable<? extends File> iterable) {return className;}@Overridepublic boolean equals(Object o) {return false;}@Overridepublic int hashCode() {return 0;}@Overridepublic 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*/@Overridepublic 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*/@Overrideprotected 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 {@AutowiredKafkaConfig config;@Value("${my.ip}")String ip;@Overridepublic 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 文件了