Java_动态编译总结

不多说直接上代码:

动态编译的主类:

package com.lkb.autoCode.util;

import com.lkb.autoCode.constant.AutoCodeConstant;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.tools.*;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * AutoCompiler 动态编译器
 *
 * @author Lilin
 * @date 2016/5/18
 */
public class AutoCompiler {
    private static final Log log = LogFactory.getLog(AutoCompiler.class);

    /**
     * Description: Compile java source file to java class with run method
     *
     * @param fullFileName the java source file name with full path
     * @return true-compile successfully, false - compile unsuccessfully
     */
    public static boolean compileFile(String fullFileName) {
        boolean bRet = false;
        // get compiler
        JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler();
        // compile the java source code by run method
        int iCompileRet = oJavaCompiler.run(null, null, null, fullFileName);
        // set compile result
        if (0 == iCompileRet) {
            bRet = true;
        }
        return bRet;
    }

    /**
     * Description: Compile java source file to java class with getTask
     * method, it can specify the class output path and catch diagnostic
     * information
     *
     * @param fullFileName the java source file name with full path
     * @param outputPath   the output path of java class file
     * @return true-compile successfully, false - compile unsuccessfully
     * @throws IOException
     */
    public static boolean compileFile(String fullFileName, String outputPath) {
        System.out.println("源文件路径:" + fullFileName);
        System.out.println("输入路径:" + outputPath);
        boolean bRet = false;
        // get compiler
        // TODO 当运行环境是JRE是无法直接ToolProvider.getSystemJavaCompiler();这么获取
        JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler();

        // define the diagnostic object, which will be used to save the diagnostic information
        DiagnosticCollector<JavaFileObject> oDiagnosticCollector = new DiagnosticCollector<JavaFileObject>();

        // get StandardJavaFileManager object, and set the diagnostic for the object
        StandardJavaFileManager oStandardJavaFileManager = oJavaCompiler
                .getStandardFileManager(oDiagnosticCollector, null, null);

        // set class output location
        JavaFileManager.Location oLocation = StandardLocation.CLASS_OUTPUT;
        try {
            File outputFile = new File(outputPath);
            if (!outputFile.exists()) {
                outputFile.mkdir();
            }
            List<File> dependencies = new ArrayList<File>();

            // 加载依赖的jar文件
            dependencies.addAll(getJarFiles(AutoCodeConstant.JARS_PATH));
            // 加载依赖的class文件
            dependencies.add(new File(AutoCodeConstant.BASE_PATH));


            oStandardJavaFileManager.setLocation(StandardLocation.CLASS_PATH, dependencies);
            oStandardJavaFileManager.setLocation(oLocation, Arrays
                    .asList(new File[]{outputFile}));

            File sourceFile = new File(fullFileName);
            // get JavaFileObject object, it will specify the java source file.
            Iterable<? extends JavaFileObject> oItJavaFileObject = oStandardJavaFileManager
                    .getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
            // -g 生成所有调试信息
            // -g:none 不生成任何调试信息
            // -g:{lines,vars,source} 只生成某些调试信息
            // -nowarn 不生成任何警告
            // -verbose 输出有关编译器正在执行的操作的消息
            // -deprecation 输出使用已过时的 API 的源位置
            // -classpath <路径> 指定查找用户类文件的位置
            // -cp <路径> 指定查找用户类文件的位置
            // -sourcepath <路径> 指定查找输入源文件的位置
            // -bootclasspath <路径> 覆盖引导类文件的位置
            // -extdirs <目录> 覆盖安装的扩展目录的位置
            // -endorseddirs <目录> 覆盖签名的标准路径的位置
            // -d <目录> 指定存放生成的类文件的位置
            // -encoding <编码> 指定源文件使用的字符编码
            // -source <版本> 提供与指定版本的源兼容性
            // -target <版本> 生成特定 VM 版本的类文件
            // -version 版本信息
            // -help 输出标准选项的提要
            // -X 输出非标准选项的提要
            // -J<标志> 直接将 <标志> 传递给运行时系统

            // 编译选项,将编译产生的类文件放在当前目录下
            //Iterable<String> options = Arrays.asList("-encoding", AutoCodeConstant.CONTENT_ENCODING, "-classpath", getJarFiles(AutoCodeConstant.JARS_PATH));
            Iterable<String> options = Arrays.asList("-encoding", AutoCodeConstant.CONTENT_ENCODING);
            // compile the java source code by using CompilationTask's call method
            bRet = oJavaCompiler.getTask(null, oStandardJavaFileManager,
                    oDiagnosticCollector, options, null, oItJavaFileObject).call();

            //print the Diagnostic's information
            for (Diagnostic oDiagnostic : oDiagnosticCollector
                    .getDiagnostics()) {
//                log.info("Error on line: "
//                        + oDiagnostic.getLineNumber() + "; URI: "
//                        + oDiagnostic.getSource().toString());
                System.out.printf(
                        "Code: %s%n" +
                                "Kind: %s%n" +
                                "Position: %s%n" +
                                "Start Position: %s%n" +
                                "End Position: %s%n" +
                                "Source: %s%n" +
                                "Message: %s%n",
                        oDiagnostic.getCode(), oDiagnostic.getKind(),
                        oDiagnostic.getPosition(), oDiagnostic.getStartPosition(),
                        oDiagnostic.getEndPosition(), oDiagnostic.getSource(),
                        oDiagnostic.getMessage(null));
            }
        } catch (IOException e) {
            log.error("动态编译出现异常", e);
        } finally {
            //close file manager
            if (null != oStandardJavaFileManager) {
                try {
                    oStandardJavaFileManager.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("动态编译关闭文件管理器出现异常", e);
                }
            }
        }
        return bRet;
    }

    /**
     * 查找该目录下的所有的jar文件
     *
     * @param jarPath
     * @throws Exception
     */
    private static List<File> getJarFiles(String jarPath) {
        final List<File> dependencyJars = new ArrayList<File>();
        File sourceFile = new File(jarPath);
        if (sourceFile.exists()) {// 文件或者目录必须存在
            if (sourceFile.isDirectory()) {// 若file对象为目录
                // 得到该目录下以.java结尾的文件或者目录
                File[] childrenFiles = sourceFile.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        if (pathname.isDirectory()) {
                            return true;
                        } else {
                            String name = pathname.getName();
                            if (name.endsWith(".jar") ? true : false) {
                                //jars[0] = jars[0] + pathname.getPath() + ";";
                                dependencyJars.add(pathname);
                                return true;
                            }
                            return false;
                        }
                    }
                });
            }
        }
        return dependencyJars;
    }
}

辅助类:

package com.lkb.autoCode.constant;

/**
 * AutoCodeConstant
 *
 * @author Lilin
 * @date 2016/5/18
 */
public class AutoCodeConstant {
    /**
     * 登录模板名称
     */
    public static String LOGIN_TEMPLATE_FTL = "LoginClientTemplate.ftl";
    /**
     * 项目根路径
     */
    public final static String BASE_PATH = AutoCodeConstant.class.getResource("/").toString().replaceAll("^file:/", "");

    /**
     * Jar包所在目录
     */
    public final static String JARS_PATH = BASE_PATH.replace("classes","lib");
    /**
     * 模板根路径
     */
    public final static String BASE_TEMPLATE_PATH = BASE_PATH + "template";
    /**
     * client目录
     */
    public final static String LOCAL_CLIENT_BASE_PATH = "src/main/java/com/lkb/sb/client/";
    /**
     * client目录
     */
    public final static String CLIENT_BASE_PATH = "com/lkb/sb/client/";
    /**
     * client类名后缀
     */
    public final static String LOGIN_CLIENT_SUFFIX = "LoginClient.java";
    /**
     * 默认编译输出路径
     */
    public final static String DEFULT_OUTPUT_PATH = "/";
    /**
     * 编码格式
     */
    public final static String CONTENT_ENCODING = "UTF-8";


}

开发背景:需求是根据代码模板动态生成java代码,并动态编译

开发过程中遇到的阻塞:模板代码中有依赖别的class文件和jar文件无法加载的问题

解决方法:

// 加载依赖的jar文件
dependencies.addAll(getJarFiles("jar文件的根路径"));
// 加载依赖的class文件
dependencies.add(new File("class文件的根路径"));



    /**
     * 查找该目录下的所有的jar文件
     *
     * @param jarPath
     * @throws Exception
     */
    private static List<File> getJarFiles(String jarPath) {
        final List<File> dependencyJars = new ArrayList<File>();
        File sourceFile = new File(jarPath);
        if (sourceFile.exists()) {// 文件或者目录必须存在
            if (sourceFile.isDirectory()) {// 若file对象为目录
                // 得到该目录下以.java结尾的文件或者目录
                File[] childrenFiles = sourceFile.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        if (pathname.isDirectory()) {
                            return true;
                        } else {
                            String name = pathname.getName();
                            if (name.endsWith(".jar") ? true : false) {
                                //jars[0] = jars[0] + pathname.getPath() + ";";
                                dependencyJars.add(pathname);
                                return true;
                            }
                            return false;
                        }
                    }
                });
            }
        }
        return dependencyJars;
    }
原文地址:https://www.cnblogs.com/gisblogs/p/5512370.html