Jdk8 动态编译 Java 源码为 Class 文件
一.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.