博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringBoot 动态编译 JAVA class 解决 jar in jar 的依赖问题
阅读量:4071 次
发布时间:2019-05-25

本文共 4785 字,大约阅读时间需要 15 分钟。

使用 动态编译在 运行期根据配置文件生成java代码 并且编译为class 加载到 classloader中 的玩法已经用了一年多了,但是一直有个坑就是 在编译Java class的时候需要 提取依赖jar包到 服务器的某个目录中,然后加上- classPath 参数才可运行成功。

于是我就想在想有没有一种办法可以让程序编译的时候去springboot boot info下面去找依赖。

 

众所周知,在IDE中运行springboot 程序 类加载器是JDK自带的,类加载器已经完成了依赖jar的加载,所以编译是没问题的,但是springboot 用的是springboot自己写的LaunchedURLClassLoader ,我在想是不是换掉相关的classloader 就可以实现了。于是重写了以下代码。

/** * java 文件管理器 主要用来 重新定义class loader */class SpringJavaFileManager extends JavacFileManager {    public SpringJavaFileManager(Context context, boolean b, Charset charset) {        super(context, b, charset);    }    @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()]));        }    }    protected ClassLoader getClassLoader(URL[] var1) {        ClassLoader var2 = this.getClass().getClassLoader();        if (this.classLoaderClass != null) {            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);    }}

这样使用: 

public static SpringJavaFileManager getStandardFileManager(DiagnosticListener
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); } private static Map
compile(String className, String javaStr) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager stdManager = getStandardFileManager(null, null, null);

 发现没啥用,看来只改此处还是不行,于是去国外论坛翻相关知识点。发现一个大神说要从JavaFileManager入手,因为Java编译器是通过JavaFileManager来加载相关依赖类的,于是又开动 重写JavaFileManager,使用到了springboot的 jarFile 来读取嵌套jar。

/** * 内存Java文件管理器 */class MemoryJavaFileManager extends ForwardingJavaFileManager
{ // compiled classes in bytes: final Map
classBytes = new HashMap
(); final Map
> classObjectPackageMap = new HashMap<>(); private JavacFileManager javaFileManager; MemoryJavaFileManager(JavaFileManager fileManager) { super(fileManager); this.javaFileManager = javaFileManager; } public Map
getClassBytes() { return new HashMap
(this.classBytes); } @Override public void flush() throws IOException { } @Override public void close() throws IOException { classBytes.clear(); } public List
getLibJarsOptions(String packgeName) { List
result = new ArrayList<>(); try { String jarBaseFile = MemoryClassLoader.getPath(); JarFile jarFile = new JarFile(new File(jarBaseFile)); List
entries = jarFile.stream().filter(jarEntry -> { return jarEntry.getName().endsWith(".jar"); }).collect(Collectors.toList()); JarFile libTempJarFile = null; List
tempEntries = null; for (JarEntry entry : entries) { tempEntries = new ArrayList<>(); libTempJarFile = jarFile.getNestedJarFile(jarFile.getEntry(entry.getName())); Enumeration
tempEntriesEnum = libTempJarFile.entries(); while(tempEntriesEnum.hasMoreElements()){ JarEntry jarEntry = tempEntriesEnum.nextElement(); String classPath = jarEntry.getName().replace("/", "."); if(!classPath.endsWith(".class") || jarEntry.getName().lastIndexOf("/") == -1){ continue; } else if(classPath.substring(0,jarEntry.getName().lastIndexOf("/")).equals(packgeName)){ tempEntries.add(jarEntry); } } for (JarEntry tempEntry : tempEntries) { result.add(new MemorySpringBootInfoJavaClassObject(tempEntry.getName().replace("/", ".").replace(".class",""), new URL(libTempJarFile.getUrl(),tempEntry.getName()),javaFileManager)); } } } catch (Exception e) { e.printStackTrace(); } return result; }

 基本上通过修改以上2个点就搞定了。 --  代码有点乱,还未整理。

开源项目地址:

类加载器地址:

fhs framework qq群:976278956

转载地址:http://fhwni.baihongyu.com/

你可能感兴趣的文章
事件冒泡阻止event.stopPropagation()
查看>>
Flex4 beta 的 Spark 布局
查看>>
Spark 架构和组件集的简要概述
查看>>
关于flex4中文(zh_CN)本地化应用编译不通过的解决方法
查看>>
摩斯密码表
查看>>
一段摩斯密码里的爱情故事
查看>>
游戏测试的技术难点和测试技术
查看>>
线程简介
查看>>
线程挂起自己,让出CPU
查看>>
线程同步(C# 编程指南)
查看>>
创建高效的线程安全类的步骤
查看>>
Failed to load class "org.slf4j.impl.StaticLoggerB
查看>>
使用 Apache MINA 2 开发网络应用
查看>>
MANIFEST.MF文件的格式
查看>>
NIO入门-了解Buffer
查看>>
database如何管理超过4GB的文件
查看>>
[转载]java.util.concurrent.ConcurrentHashMap 如何在不损失线程安全的同时提供更高的并发性...
查看>>
sun game server (sgs)初探
查看>>
類別 ConcurrentHashMap<K,V>的更新,删除
查看>>
如何使用Flex 4新的CSS语法,兼容halo组件
查看>>