前言
在开发一个基础工具包给业务组的小伙伴们使用的时候,发现一个小问题,就是在反射的时候在自己电脑上运行的正常,但是打成jar包后,就class not fuond,有点奇怪。
如果能借助Spring这个都不是事,关键是不能用。
场景复现
下面的目的是,找到这个指定包下的所有类,对我指定了注解的类进行加载,其实就是一个可以灵活装配、拆卸的策略模式。这样业务的类可以通过指定注解来选择是否成为业务的一部分。
下面这段代码在IDE中运行正常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.JarURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.jar.JarEntry; import java.util.jar.JarFile;
public class GeneratorFactory {
public static Map<String, TransactionCreator> creators = new HashMap<>();
static { reflectByClass(); }
public static void reflectByClass() { try { ArrayList<Class> creatorsClass = new ArrayList<>(); ArrayList<File> classFiles = new ArrayList<>(); Class<?> interfaceClass = Class.forName("com.liukai.test.generate.TransactionCreator"); String packageName = interfaceClass.getPackage().getName(); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); String path = packageName.replace(".", "/"); System.out.println("path: " + path); Enumeration<URL> resources = contextClassLoader.getResources(path); System.out.println("Enumeration<URL> resources: " + resources); while (resources.hasMoreElements()) { URL url = resources.nextElement(); System.out.println("url.getFile(): " + url.getFile()); classFiles.add(new File(url.getFile())); } for (File file : classFiles) { creatorsClass.addAll(findClass(file, packageName)); }
for (Class clazz : creatorsClass) { System.out.println("Class clazz : creatorsClass"); Creator declaredAnnotation = (Creator) clazz.getAnnotation(Creator.class); if (declaredAnnotation != null) { System.out.println("creators: add" + declaredAnnotation.type()); creators.put(declaredAnnotation.type(), (TransactionCreator) clazz.newInstance()); } } } catch (ClassNotFoundException | IOException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); } }
public static TransactionCreator getGenerator(String type) { System.out.println("creators: " + creators.size()); return creators.get(type); }
private static ArrayList<Class> findClass(File file, String packagename) { ArrayList<Class> list = new ArrayList<>(); if (!file.exists()) { return list; } File[] files = file.listFiles(); for (File file2 : files) { if (file2.isDirectory()) { assert !file2.getName().contains("."); ArrayList<Class> arrayList = findClass(file2, packagename + "." + file2.getName()); list.addAll(arrayList); } else if (file2.getName().endsWith(".class")) { try { list.add(Class.forName(packagename + '.' + file2.getName().substring(0, file2.getName().length() - 6))); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } return list; }
}
|
解决方式:
由于打成jar包后,类路径多了一层jar
,所以加载时,要考虑到jar包路径
,以下这个demo,实际可以通过获取运行路径变量替换写死的路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.JarURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.jar.JarEntry; import java.util.jar.JarFile;
public class GeneratorFactory {
public static Map<String, TransactionCreator> creators = new HashMap<>();
static { reflectByJar(); }
public static void reflectByJar() { String path = "/Users/liukai/workspaces/java/test/build/libs/"; String fileName = "test.jar"; try { JarFile jarFile = new JarFile(path + fileName); Enumeration<JarEntry> e = jarFile.entries(); JarEntry entry; while (e.hasMoreElements()) { entry = (JarEntry) e.nextElement(); if (entry.getName().indexOf("META-INF") < 0 && entry.getName().indexOf(".class") >= 0) { String classFullName = entry.getName(); String className = classFullName.substring(0, classFullName.length() - 6).replace("/", "."); Class<?> clazz; try { try { if (!className.contains("com.liukai.test")) { continue; } System.out.println("Class clazz : creatorsClass:" + className); clazz = Class.forName(className); Creator declaredAnnotation = (Creator) clazz.getAnnotation(Creator.class); if (declaredAnnotation != null) { System.out.println("creators: add" + declaredAnnotation.type()); creators.put(declaredAnnotation.type(), (TransactionCreator) clazz.newInstance()); } } catch (ClassNotFoundException e1) { e1.printStackTrace(); }
} catch (Exception e1) {
}
} } } catch (IOException e) { e.printStackTrace(); } }
|
总结
感觉还是不智能,应该有一个通用的方法,无论我是什么场景,只需要一套代码就解决,而不是用户来抽像,这个JDK应该得提供。