当前位置: 首页 > news >正文

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

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

在这里插入图片描述

相关文章:

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

Jdk8 动态编译 Java 源码为 Class 文件 一.JDK版本二.工程介绍1.依赖2.启动类3.配置类&#xff08;用于测试依赖注入&#xff09;4.工具类1.Java 源码文件读取类2.SpringBoot 容器实例管理类 5.测试类1.抽象类2.接口类3.默认抽象实现4.默认接口实现 6.接口类1.测试接口2.类重载…...

Shell自动化日志维护脚本

简介&#xff1a; 系统日志对于了解操作系统的运行状况、故障排除和性能分析至关重要。然而&#xff0c;长期积累的日志文件可能变得庞大&#xff0c;影响系统性能。在这篇文章中&#xff0c;我们将介绍一个自动化的解决方案&#xff0c;使用 Bash 脚本来监控和维护系统日志文件…...

设计模式入门笔记

1 设计模式简介 在IT这个行业&#xff0c;技术日新月异&#xff0c;可能你今年刚弄懂一个编程框架&#xff0c;明年它就不流行了。 然而即使在易变的IT世界也有很多几乎不变的知识&#xff0c;他们晦涩而重要&#xff0c;默默的将程序员划分为卓越与平庸两类。比如说&#xff…...

存储成本降低85%,携程历史库场景的降本实践

携程&#xff0c;一家中国领先的在线票务服务公司&#xff0c;从 1999 年创立至今&#xff0c;数据库系统历经三次替换。在移动互联网时代&#xff0c;面对云计算卷积而来的海量数据&#xff0c;携程通过新的数据库方案实现存储成本降低 85% 左右&#xff0c;性能提升数倍。本文…...

如何精确掌握函数防抖和函数节流的使用?

前序 函数防抖&#xff08;Debouncing&#xff09;和函数节流&#xff08;Throttling&#xff09;都是用于控制函数执行频率的技术&#xff0c;通常在处理高频率触发的事件&#xff08;如窗口滚动、鼠标移动、输入框输入等&#xff09;时非常有用 一、核心概念 函数防抖 函…...

【Linux系列】离线安装openjdk17的rpm包

首发博客地址 首发博客地址[1] 系列文章地址[2] 视频地址[3] 准备 RPM 包 请从官网下载&#xff1a;https://www.oracle.com/java/technologies/downloads/#java17[4] 如需不限速下载&#xff0c;请关注【程序员朱永胜】并回复 1020 获取。 安装 yum localinstall jdk-17_linux…...

Python 没有 pip 包问题解决

最近需要搞一个干净的Python,从官网上直接下载解压可用的绿色版&#xff0c;发现无法正常使用PiP 一 官网下载Python https://www.python.org/downloads/ 选择 embeddable package,这种是免安装的包&#xff0c;解压后可以直接使用。 二 配置环境变量 添加环境变量&#xff1a…...

并发-Java中的锁(二)--- 重入锁ReentrantLock,公平锁,非公平锁笔记

重入锁ReentrantLock 支持重进入的锁&#xff0c;表示该锁能够支持一个线程对资源的重复加锁该锁支持获取锁时的公平和非公平的选择 如果在绝对时间上&#xff0c;先对锁进行获取的请求一定先被满足&#xff0c;那么锁是公平的&#xff0c;获取锁是顺序的。 实现重进入 线程再…...

LeetCode每日一题:1921. 消灭怪物的最大数量(2023.9.3 C++)

目录 1921. 消灭怪物的最大数量 题目描述&#xff1a; 实现代码与解析&#xff1a; 贪心 原理思路&#xff1a; 1921. 消灭怪物的最大数量 题目描述&#xff1a; 你正在玩一款电子游戏&#xff0c;在游戏中你需要保护城市免受怪物侵袭。给你一个 下标从 0 开始 且长度为 …...

SpringBoot连接MySQL数据库,使用Mybatis框架(入门)

1. 说明 SpringBoot项目&#xff0c;连接MySQL数据库&#xff0c;使用Mybatis框架。 本篇文章作为 SpringBoot 使用 Mybatis 的入门。 2. 依赖 2.1. MySQL驱动依赖 MySQL驱动&#xff0c;使用SpringBoot版本对应的默认版本&#xff0c;不需要手动指定版本。 比如&#xf…...

滑动窗口实例6(找到字符串中所有字母异位词)

题目&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 示例 1: 输入: s "cbaebabac…...

武林新秀(一)`git init` 初始化一个新的Git仓库

文章目录 命令的概述和用途命令的用法命令行选项和参数的详细说明命令的示例命令的注意事项或提示 命令的概述和用途 git init 是 Git 版本控制系统中用于初始化一个新的 Git 仓库或重新初始化一个现有的仓库的命令。“init” 是 “initialize”&#xff08;初始化&#xff09…...

gRPC之Interceptor

1、gRPC Interceptor 在应用开发过程中会有这样的需求&#xff0c;就是在请求执行前后做一些通用的处理逻辑&#xff0c;比如记录日志、tracing、身份 认证等&#xff0c;在web框架中一般是使用middleware来实现的&#xff0c;gRPC 在客户端和服务端都支持了拦截器功能&#…...

计算机竞赛 基于机器视觉的二维码识别检测 - opencv 二维码 识别检测 机器视觉

文章目录 0 简介1 二维码检测2 算法实现流程3 特征提取4 特征分类5 后处理6 代码实现5 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于机器学习的二维码识别检测 - opencv 二维码 识别检测 机器视觉 该项目较为新颖&#xff0c;适合作为竞赛课…...

ELK安装、部署、调试 (七)kibana的安装与配置

1.介绍 Kibana 是一个基于浏览器的开源可视化工具&#xff0c;主要用于分析大量日志&#xff0c;以折线图、条形图、饼图、热图、区域图、坐标图、仪表、目标、时间等形式。预测或查看输入源的错误或其他重大事件趋势的变化。Kibana 与 Elasticsearch 和 Logstash 同步工作&am…...

【Npm】的安装和使用教程

前端工具及插件库 专栏收录该内容 24 篇文章1 订阅 订阅专栏 npm 一、安装配置 二、初始化配置文件 package.json package.lock.json 二、下载模块 2.1、下载指令 2.2、清理缓存 2.3、模块信息 2.4、npm i 与 npm ci 区别 三、其他指令 第三方模块是别人写好的一些文件&#xf…...

22.3D等距社交媒体菜单的悬停特效

效果 源码 <!doctype html> <html><head><meta charset="utf-8"><title>CSS Isometric Social Media Menu</title><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.…...

音视频开发常用工具

文章目录 前言一、VLC 播放器1、简介2、下载3、VLC media player4、VLC 打开网络串流5、VLC 作为流媒体服务器①、搭建 RTSP 流媒体服务器②、新建播放器 二、MediaInfo1、简介2、下载3、MediaInfo①、主界面②、主要功能特点③、使用方法④、Mediainfo 相关参数和含义简介 三、…...

【leetcode 力扣刷题】字符串匹配之经典的KMP!!!

字符串子串匹配相关 28. 找出字符串中第一个匹配项的下标暴力求解KMP 459. 重复的子字符串暴力求解在SS中找S 以下是能用KMP求解的算法题&#xff0c;KMP是用于字符串匹配的经典算法【至今没学懂………啊啊啊】 28. 找出字符串中第一个匹配项的下标 题目链接&#xff1a;28. 找…...

C#的反射机制

介绍 当谈到C#的反射机制时&#xff0c;它提供了一种动态地在运行时获取和操作类型信息的能力。通过反射&#xff0c;可以在编译时未知的情况下&#xff0c;使用类型信息来创建对象、调用方法、访问属性和字段等。下面是一些反射机制的重要概念和用法&#xff1a; Type 类型&a…...

浅谈城市轨道交通视频监控与AI视频智能分析解决方案

一、背景分析 地铁作为重要的公共场所交通枢纽&#xff0c;流动性非常高、人员大量聚集&#xff0c;轨道交通需要利用视频监控系统来实现全程、全方位的安全防范&#xff0c;这也是保证地铁行车组织和安全的重要手段。调度员和车站值班员通过系统监管列车运行、客流情况、变电…...

【LeetCode每日一题合集】2023.8.14-2023.8.20(⭐切披萨3n块披萨)

文章目录 617. 合并二叉树833. 字符串中的查找与替换&#xff08;模拟&#xff09;2682. 找出转圈游戏输家&#xff08;模拟&#xff09;1444. 切披萨的方案数&#xff08;⭐⭐⭐⭐⭐&#xff09;解法——从递归到递推到优化&#xff08;二维前缀和记忆化搜索&#xff09; 1388…...

通过ref 操作dom , 点击按钮后跳转到页面指定图片位置

滚动图片到视图 定义了一个名为 scrollToIndex 的函数&#xff0c;它接受一个参数 index。当按钮被点击时&#xff0c;这个函数会被调用&#xff0c;并根据传入的 index 值来滚动到对应的图片。 以 alt 来标记图片位置 alt“Tom” import { useRef } from "react";c…...

QT 设置应用程序图标

1.下载xx.ico图标&#xff1a;ico网址 2.在线PNG转换ICO&#xff1a;png在线转换ico 3.添加图标资源 1&#xff09;新建文件路径 2&#xff09;添加图片资源 3&#xff09;在 .pro文件里面添加图片 4&#xff09;将xx.ico放到工程目录&#xff0c;编译完可以看到xx.exe的图标…...

牛客网刷题

牛客网刷题-C&C 2023年9月3日15:58:392023年9月3日16:37:01 2023年9月3日15:58:39 2023年9月3日16:37:01 整型常量和实型常量的区别...

ES6核心语法

主要记录学习ES6的语法 1、let和const 同es5中的var来声明变量。三者的区别分别是&#xff1a; var声明的变量存在变量提升&#xff0c;先声明未赋值&#xff0c;值为undefined。且变量声明可在函数块内使用。变量声明之后可以重复声明let声明的变量无变量提升。作用域是块级…...

python 之import与from import 导入库的解析与差异

文章目录 1. **使用import导入整个模块**&#xff1a;2. **使用from import导入特定内容**&#xff1a;注意事项别名的使用 在Python中&#xff0c;import和from import是用于导入模块中内容的两种不同方式。下面详细介绍它们的用法和差异&#xff1a; 1. 使用import导入整个模…...

python实现MQTT协议(发布者,订阅者,topic)

python实现MQTT协议 一、简介 1.1 概述 本文章针对物联网MQTT协议完成python实现 1.2 环境 Apache-apollo创建brokerPython实现发布者和订阅者 1.3 内容 MQTT协议架构说明 &#xff1a; 利用仿真服务体会 MQTT协议 针对MQTT协议进行测试 任务1&#xff1a;MQTT协议应…...

2023年09月03日-----16:58

协同过滤推荐和矩阵分解本质上有什么不同?协同过滤推荐和矩阵分解是两种推荐系统方法,它们在某些方面有相似之处,但也有一些本质不同之处。 基本原理: 协同过滤推荐:协同过滤是一种基于用户行为数据的推荐方法,它依赖于用户-物品交互数据,如用户的评分或点击历史。协同过…...

HTTP状态码504(Gateway Timeout)报错原因分析和解决办法

文章目录 504报错原因分析一、用户角度1. 代理服务器问题2. 网络问题 二、网站管理员角度1. 服务器负载过重2. 网关配置问题3. 目标服务器响应慢4. IIS/nginx/apache服务关闭5. 维护或故障6. 数据库的慢处理也会导致504 用户角度可以采取哪些措施解决504错误1. 刷新页面2. 检查…...