關於web工程實現動態編譯代碼,出現找不到第三方jar包的解決方案。

出現的問題:java

我用spring boot搭建一個web環境,想實現動態加載一個外部的java文件,編譯並運行獲得結果。web

大概代碼以下:spring

    /**
     * 
     * <p>Title: compile</p>
     * <p>Description: compile </p>
     * @param javaName javaName the name of your public class,eg: <code>TestClass.java</code>
     * @param javaSrc  source code string
     * @return return the Map, the KEY means ClassName, the VALUE means bytecode.
     */
    public static Map<String, byte[]> compile(String javaName, String javaSrc) {
        // 獲取jvm的jre
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
        try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
            JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc);
            
            // 用來獲取編譯錯誤時的錯誤信息
            DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
            
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, collector, null, null,
                    Arrays.asList(javaFileObject));
            if (task.call()) {
                return manager.getClassBytes();
            } else {
                // 當編譯存在錯誤的時候,打印錯誤日誌
                collector.getDiagnostics().forEach(item -> log.error(item.toString()));
            }
            // 關閉StandardJavaFileManager
            stdManager.close();
            
        } catch (IOException e) {
            log.error("出現異常 : " , e);
        }
        return null;
    }

可是,我外部的java文件是含有第三方jar包的(這些jar包,個人spring boot工程所有含有)。tomcat

在eclipse下運行時,一切順利,但當我將spring boot打包成jar包,使用java -jar 的方式運行的時候,這段代碼在編譯java文件時出錯,提示app

error: package org.jsoup does not exist
import org.jsoup.Jsoup;

解決方法:eclipse

1. 不能使用jar包部署(樓主找了好久都沒找到jar包這麼解決,因此放棄了,有知道的大佬幫忙補充一下。)jvm

    本例子使用war包的方式部署到tomcat上。編輯器

2. 修改以上代碼,編譯時加入編譯屬性:ide

/**
	 * 
	 * <p>Title: compile</p>
	 * <p>Description: compile </p>
	 * @param javaName javaName the name of your public class,eg: <code>TestClass.java</code>
	 * @param javaSrc  source code string
	 * @return return the Map, the KEY means ClassName, the VALUE means bytecode.
	 */
	public static Map<String, byte[]> compile(String javaName, String javaSrc) {
		// 獲取jvm的jre
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
		try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
			JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc);
			
			// 用來獲取編譯錯誤時的錯誤信息
			DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
			/** START 如下代碼在打包成web程序時必須開啓,在編輯器裏面時請屏蔽 */
			StringBuilder cp = new StringBuilder();
            URLClassLoader urlClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
            for (URL url : urlClassLoader.getURLs()) {
                cp.append(url.getFile()).append(File.pathSeparator);
            }
            List<String> options = new ArrayList<String>();
			options.add("-classpath");
			options.add(cp.toString());
			/**  END  以上代碼在打包成web程序時必須開啓,在編輯器裏面時請屏蔽 */
			// 此處的options 只有打包成web服務時,才須要,在編輯器中運行時,直接填null便可。
			JavaCompiler.CompilationTask task = compiler.getTask(null, manager, collector, options, null,
					Arrays.asList(javaFileObject));
			if (task.call()) {
				return manager.getClassBytes();
			} else {
				// 當編譯存在錯誤的時候,打印錯誤日誌
				collector.getDiagnostics().forEach(item -> log.error(item.toString()));
			}
			// 關閉StandardJavaFileManager
			stdManager.close();
			
		} catch (IOException e) {
			log.error("出現異常 : " , e);
		}
		return null;
	}

3. 請注意,在編輯器中運行時,加入 options  會致使運行失敗,因此在編輯器中請屏蔽掉,發佈時再打開。ui

相關文章
相關標籤/搜索