本文共 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 Mapcompile(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/