zzzkc 发表于 2021-12-2 17:02

项目打包jar并动态编译外部Java文件

本帖最后由 zzzkc 于 2021-12-2 17:06 编辑

感谢 https://blog.csdn.net/shuaizai88/article/details/105016029 博主解决了动态编译Java代码过程中出现找不到依赖包的问题。其核心代码:// 可执行jar的路径如:C:/Users/User/Desktop/xx/xx/project.jar
String jarBaseFile = MemoryClassLoader.getPath();
JarFile jarFile = new JarFile(new File(jarBaseFile));
// 获取项目中所依赖的jar包
List<JarEntry> entries = jarFile.stream().filter(jarEntry -> {
    return jarEntry.getName().endsWith(".jar");
}).collect(Collectors.toList());
JarFile libTempJarFile = null;
List<JavaFileObject> onePackgeJavaFiles = null;
String packgeName = null;
// 遍历每一个引用的jar包
for (JarEntry entry : entries) {
    libTempJarFile = jarFile.getNestedJarFile(jarFile.getEntry(entry.getName()));
    if (libTempJarFile.getName().contains("tools.jar")) {
      continue;
    }
    Enumeration<JarEntry> tempEntriesEnum = libTempJarFile.entries();
    // 遍历每一个jar包中的class并缓存
    while (tempEntriesEnum.hasMoreElements()) {
      JarEntry jarEntry = tempEntriesEnum.nextElement();
      String classPath = jarEntry.getName().replace("/", ".");
      if (classPath.endsWith(".class") && jarEntry.getName().lastIndexOf("/") != -1) {
            packgeName = classPath.substring(0, jarEntry.getName().lastIndexOf("/"));
            onePackgeJavaFiles = CLASS_OBJECT_PACKAGE_MAP.containsKey(packgeName) ? CLASS_OBJECT_PACKAGE_MAP.get(packgeName) : new ArrayList<>();
            onePackgeJavaFiles.add(new MemorySpringBootInfoJavaClassObject(jarEntry.getName().replace("/", ".").replace(".class", ""), new URL(libTempJarFile.getUrl(), jarEntry.getName()), javaFileManager));
            CLASS_OBJECT_PACKAGE_MAP.put(packgeName, onePackgeJavaFiles);
      }
    }
}不过有个问题,就是找不到项目自己编写的类,比如代码引用了项目的一个工具类,这时编译外部的Java文件就会报找不到这个工具类的错误。因此需要在上面基础做修改:String jarBaseFile = MemoryClassLoader.getPath();
JarFile jarFile = new JarFile(new File(jarBaseFile));
// 解决找不到service、bean、util等自己写的class
insCacheClass(jarFile, true);
// 获取所引入的jar包
List<JarEntry> entries = jarFile.stream().filter(jarEntry -> jarEntry.getName().endsWith(".jar")).collect(Collectors.toList());
JarFile libTempJarFile;
// 遍历每一个引用的jar包
for (JarEntry entry : entries) {
    libTempJarFile = jarFile.getNestedJarFile(jarFile.getEntry(entry.getName()));
    if (libTempJarFile.getName().contains("tools.jar")) {
      continue;
    }
    insCacheClass(libTempJarFile, false);
}


/**
* 从jar内遍历class并缓存
* @Param jarFile jar文件
* @param isProjectJar 是否本项目jar
*/
private void insCacheClass(JarFile jarFile, Boolean isProjectJar) {
    Enumeration<JarEntry> tempEntriesEnum = jarFile.entries();
    String packageName;
    List<JavaFileObject> onePackageJavaFiles;
    // 相关的class文件进行缓存
    try {
      while (tempEntriesEnum.hasMoreElements()) {
            JarEntry jarEntry = tempEntriesEnum.nextElement();
            String classPath = jarEntry.getName().replace("/", ".");
            if (classPath.endsWith(".class") && jarEntry.getName().lastIndexOf("/") != -1) {
                packageName = classPath.substring(0, jarEntry.getName().lastIndexOf("/"));
                // jar项目
                if (isProjectJar) {
                  if (packageName.contains("BOOT-INF.classes.")) {
                        packageName = packageName.replace("BOOT-INF.classes.", "");
                  }
                }
                onePackageJavaFiles = CLASS_OBJECT_PACKAGE_MAP.containsKey(packageName) ? CLASS_OBJECT_PACKAGE_MAP.get(packageName) : new ArrayList<>();
                onePackageJavaFiles.add(new MemoryJavaClassObject(jarEntry.getName().replace("/", ".").replace(".class", ""),
                                                                  new URL(jarFile.getUrl(), jarEntry.getName()), javaFileManager));
                CLASS_OBJECT_PACKAGE_MAP.put(packageName, onePackageJavaFiles);
            }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
}这是基于打成jar包的形式,如果是war包用tomcat运行的话,应该也是获取依赖包的路径再进行遍历缓存。修改后的项目地址: https://gitee.com/zzzkc/jar-project外部Java文件的packageName对应 PathUtils.java 中的PACKAGE_NAME;private static final String ROOT_PATH = "C:/Users/User/Desktop/";
private static final String PACKAGE_NAME = "deploy.clz";测试:在桌面创建deploy/clz文件夹,将项目中的OnService文件复制到deploy/clz中并修改文件package deploy.clz测试数据:SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`(
`id` int(20) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`age` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, '张强', '43');
INSERT INTO `sys_user` VALUES (2, '李锦', '21');

SET FOREIGN_KEY_CHECKS = 1;最后打包jar,运行并访问:127.0.0.1:9001/deploy/OnService/getList 或者 http://127.0.0.1:9001/deploy/OnService/getOne?id=1   

wencun 发表于 2021-12-4 11:40

收藏了多谢大佬
页: [1]
查看完整版本: 项目打包jar并动态编译外部Java文件