compiler
compiler copied to clipboard
springboot应用打包后, JavaCompiler无法动态加载类
我和那位web下找不到包中类的问题差不多, springboot应用打包后,springboot打的jar包的classpath和jarPath都变了(jar in jar形式), springboot重写了URLClassLoader(LaunchedURLClassLoader), 而JavaCompiler无法引用springboot 打包后jar中的类, 所以我覆写了MemoryJavaFileManager的getClassLoader方法, 返回了自己写的LaunchedURLClassLoader(参照springboot),但就算是编译过了, 在task.call()时,根据没有调用getJavaFileForOutput,所以就没有写入Map<String, byte[]> classBytes,导致无法加载类, 请问有什么解决办法吗? 如果楼主大神或哪位大神有解决办法,请不吝赐教,万分感谢!!!
遇到同样问题,各位大神有解决方法吗?求解答
你能编译过么?待编译的代码中用到其他jar类的,能编译过么?
暂时没解决,我也就没管他了,期待后来人
今天也踩到了这个坑,有什么好的解决办法吗
想了个比较hack的方法,就是在应用启动的时候,先解压下这个spring boot的jar,然后再将依赖加入到classpath里面,这样就能动态编译了
绕了个大弯,不过可以一试 : )
@Evilsylvana 我就是这么干的,可以解决编译问题,但是还有一个坑 Iterable<String> options = Arrays.asList("-classpath", buildClassPath("./BOOT-INF/lib/*")); JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnosticCollector, options, null, Arrays.asList(javaFileObject)); 当classLoader findClass时会报NoClassDefFoundError错误,因为编译时添加的classPath自定义的loader找不到,之后解决后再更新解决方法
@zgzhang 我没出现这个问题,你的classloader不是从当前线程继承的么?试试 new URLClassLoader(new URL[] { classFilePath }, Thread.currentThread() .getContextClassLoader());
@Evilsylvana 我的classLoader是自定义的,为了重写findClass实现从远端加载byteCode
@Evilsylvana 问题已经解决
从一个springBoot已经加载的bean获取它的classLoader launchedURLClassLoader = SpringUtil.getBean("mileageService").getClass().getClassLoader(); EngineClassLoader engineClassLoader = new EngineClassLoader(launchedURLClassLoader);
自定义的classLoader的父亲指定为springBoot 的classLoader 这样就可以加载到对应的class! private class EngineClassLoader extends ClassLoader { public EngineClassLoader(ClassLoader parent) { super(parent); } }
你能编译过么?待编译的代码中用到其他jar类的,能编译过么?
遇到 同样问题,有人解决了吗
@Evilsylvana 问题已经解决
从一个springBoot已经加载的bean获取它的classLoader launchedURLClassLoader = SpringUtil.getBean("mileageService").getClass().getClassLoader(); EngineClassLoader engineClassLoader = new EngineClassLoader(launchedURLClassLoader);
自定义的classLoader的父亲指定为springBoot 的classLoader 这样就可以加载到对应的class! private class EngineClassLoader extends ClassLoader { public EngineClassLoader(ClassLoader parent) { super(parent); } }
老哥,可以贴一下相对完整的解决方式么
已经很清晰了。。,还需要什么呢?
在应用启动的时候,先解压下这个spring boot的jar,然后再将依赖加入到classpath里面
解压jar,再执行?
在应用启动的时候,先解压下这个spring boot的jar,然后再将依赖加入到classpath里面
解压jar,再执行?
解压只是为了把spring-boot打包的fat-jar下lib下的jar包加入classpath,不然没办法自定义编译
在应用启动的时候,先解压下这个spring boot的jar,然后再将依赖加入到classpath里面
解压jar,再执行?
解压只是为了把spring-boot打包的fat-jar下lib下的jar包加入classpath,不然没办法自定义编译
不知道你们解压的意思是不是先通过命令或解压软件解压(我这样做了是能正常找到classpath了);之后搜到了这个方法findJar,把springboot的classloader作为自定义的FileManager的,然后也能找到正确的classpath
我遇到更诡异的问题,MemoryClassLoader 这个类提示 ClassNotFound ...
在应用启动的时候,先解压下这个spring boot的jar,然后再将依赖加入到classpath里面
解压jar,再执行?
解压只是为了把spring-boot打包的fat-jar下lib下的jar包加入classpath,不然没办法自定义编译
不知道你们解压的意思是不是先通过命令或解压软件解压(我这样做了是能正常找到classpath了);之后搜到了这个方法findJar,把springboot的classloader作为自定义的FileManager的,然后也能找到正确的classpath
感谢老哥提供的方法,完美解决了我的问题(同事稍微改了一下源码)
@Evilsylvana 我的classLoader是自定义的,为了重写findClass实现从远端加载byteCode
我也是自定了的Classloader加载特定的class,之前遇到这个问题是因为构造方法中没有调用super(parent);
。当指定了父加载器对象为LaunchedURLClassLoader对象时就可以找到所有的类了。那为什么是这个对象呢?因为是在Springboot2中,就是这个类加载器代替了原来的Application Classloader。
// 取当前类的类加器对象,构造自定义类加载器的时候,传入这个对象作为父类 var launchedURLClassLoader = this.getClass().getClassLoader();
class MonkeyClassLoader extends ClassLoader {
private static final String NAME = "Monkey";
private byte[] classBytes;
public MonkeyClassLoader(ClassLoader parent, byte[] classBytes) {
super(parent);
this.classBytes = classBytes;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (classBytes == null) {
return super.findClass(name);
}
final Class<?> aClass = defineClass(name, classBytes, 0, classBytes.length);
classBytes = null;
return aClass;
}
@Override
public String getName() {
return NAME;
}
}
2016到现在 求助作者更新个新版本吧 支持spingboot2
2016到现在 求助作者更新个新版本吧 支持spingboot2
springboot2.0用这种方式也是支持的, 试过了
经过2天的折腾,我终于解决了这个问题; 编译不成功的原因是在运行环境下找不到依赖类,所以需要添加 -cp货-classpath参数 List<String> options = new ArrayList<>(); options.add("-encoding"); options.add("utf-8"); options.add("-cp"); options.add(buildClassPath()); JavaCompiler.CompilationTask task = compiler.getTask(null, forwardingJavaFileManager, diagnosticCollector, options, null, Collections.singletonList(javaFileObject));
private static String buildClassPath() { ClassLoader loader=Thread.currentThread().getContextClassLoader(); String p= loader.getResource("/application.properties").getPath().replaceAll("^(.WEB-INF)(.)$", "$1")+"/lib/"; File folder=new File(p); File[] fs=folder.listFiles(); StringBuffer sb=new StringBuffer(); for(File f:fs) { sb.append(f.getAbsolutePath()).append(":"); } logger.info("........**........."); logger.info("{}",p=sb.toString().replaceFirst("\:$", "")); logger.info("..................."); return p; }
注意:java编译器依赖的-cp 的jar的路径,一定是一个解压状态的路径,所以需要把项目编译撑.war的包,而且是解压状态的tomcat/webapp下面运行,不要用压缩包的形式.如果非要以war或者jar压缩包运行,则必须要把依赖的jar单独放到一个可以找到的路径下。
感谢您的来件,邮件已收到。