前言
在开发一个基础工具包给业务组的小伙伴们使用的时候,发现一个小问题,就是在反射的时候在自己电脑上运行的正常,但是打成jar包后,就class not fuond,有点奇怪。 如果能借助Spring这个都不是事,关键是不能用。
场景复现
下面的目的是,找到这个指定包下的所有类,对我指定了注解的类进行加载,其实就是一个可以灵活装配、拆卸的策略模式。这样业务的类可以通过指定注解来选择是否成为业务的一部分。 下面这段代码在IDE中运行正常。
代码语言:javascript复制
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;
/**
* 类加载工厂
*
* @author liukai
* @since 2015/9/9.
*/
public class GeneratorFactory {
public static Map<String, TransactionCreator> creators = new HashMap<>();
static {
// 基于 IDE 运行
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
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,实际可以通过获取运行路径变量替换写死的路径。
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;
/**
* 类加载工厂
*
* @author liukai
* @since 2015/9/9.
*/
public class GeneratorFactory {
public static Map<String, TransactionCreator> creators = new HashMap<>();
static {
// 基于 jar 包反射
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();
//去掉后缀.class
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应该得提供。