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 文件了