我尝试使用客户类加载器实现此功能:我在alternatives.jar文件中有一些类文件,它们提供的实现与正常实现不同。也就是说,这个jar中的每个类都有另一个版本,在其他jar文件中 - 也会加载到类路径中。
我知道使用仪器API实现相同目的会更好。但现在我担心的是我需要理解为什么我失败了。
所以这是我的方法: 1.定义一个AlternativeClassLoader.java,在这个文件中,我重写了findClass方法。因此,如果可以从alternatives.jar中找到类名,则使用alternatives.jar中的版本。 2.在构造函数中,我调用了super(null),因此所有这些类加载工作都将由我的类加载器执行,而不是系统的。 3.这(似乎是真的)也要求我加载其他类(如果它们不是系统一)。所以我必须解析classpath,找到类路径指示的所有类。
我的问题是,我可以加载我的替代类,一切似乎都很好......但是,我使用的是slf4j,它会出现以下错误:
Failed to auto configure default logger context
Reported exception:
ch.qos.logback.core.joran.spi.JoranException: Parser configuration error occurred
Failed to instantiate [ch.qos.logback.classic.LoggerContext]
报告的例外:
java.lang.ExceptionInInitializerError
at java.util.ResourceBundle.getLoader(ResourceBundle.java:431)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:841)
我怀疑这是由我糟糕的类加载器实现引起的。有人会帮帮我吗?非常感谢!
这是我的类加载器:
public class AlternativeClassLoader extends ClassLoader {
private static final String ALTERNATIVE_JAR_PROPERTY = "alternativejar";
private static final Logger logger = LoggerFactory
.getLogger(AlternativeClassLoader.class);
private Map<String, Class<?>> clzCache = new HashMap<String, Class<?>>();
private Map<String, String> others = new HashMap<String, String>();
private Set<String> alternativesRegistry;
private JarFile altjar;
public AlternativeClassLoader(ClassLoader parent) {
/*
* pass null so I can incept all class loading except system's. By doing
* this you'll need to override findClass
*/
super(null);
registerAlternatives();
registerOthers();
}
/**
* This method will parse classpath and get all non-system class name, and
* build classname - jar_file_path/file_system_path mappings
*/
private void registerOthers() {
String[] paths = System.getProperty("java.class.path").split(":");
URL[] urls = new URL[paths.length];
for (String path : paths) {
if (path.endsWith("*.jar")) {
registerClass(path, others);
} else {
File f = new File(path);
if (!f.isDirectory())
continue;
File[] classFiles = f.listFiles(new FileFilter() {
@Override
public boolean accept(File arg0) {
if (arg0.getName().endsWith(".class")) {
return true;
} else {
return false;
}
}
});
for (File file : classFiles) {
String fileName = file.getName();
String className = fileName.substring(0,
fileName.lastIndexOf("."));
others.put(className, file.getPath());
}
}
}
showRegistry(
"Me will also be responsible for loading the following classes:",
others);
}
private void registerClass(String path, Map<String, String> registry) {
try {
JarInputStream jis = new JarInputStream(new FileInputStream(path));
for (JarEntry entry = jis.getNextJarEntry(); entry != null; entry = jis
.getNextJarEntry()) {
if (entry.getName().endsWith(".class") && !entry.isDirectory()) {
StringBuilder className = new StringBuilder();
for (String part : entry.getName().split("/")) {
if (className.length() != 0)
className.append(".");
className.append(part);
if (part.endsWith(".class"))
className.setLength(className.length()
- ".class".length());
}
registry.put(className.toString(), path);
}
}
} catch (Exception e) {
e.printStackTrace(System.out);
logger.error(
"Failed when read/parse jar {}. Your class file may not been replaced by alternative implementation",
path, e);
}
}
/**
* Try to find alternative class implementation from jar file specified by
* ALTERNATIVE_JAR_PROPERTY. If it's not specified, then use same jar file
* where this classloader is loaded.
*/
private void registerAlternatives() {
String jarFilePath = System.getProperty(ALTERNATIVE_JAR_PROPERTY);
if (jarFilePath == null || jarFilePath.isEmpty()) {
URL url = getClass().getProtectionDomain().getCodeSource()
.getLocation();
System.out.println(url + ":" + url.toString());
jarFilePath = url.getPath();
}
try {
altjar = new JarFile(jarFilePath);
} catch (IOException e) {
logger.error("cannot read jar {}", jarFilePath);
return;
}
Map<String, String> registry = new HashMap<String, String>();
registerClass(jarFilePath, registry);
alternativesRegistry = registry.keySet();
showRegistry("===Found the following alternative class:===", registry);
}
private void showRegistry(String string, Map<String, String> registry) {
System.out.println(string);
for (String clzName : registry.keySet()) {
System.out.printf("Class:%30s ->%s\n", clzName,
registry.get(clzName));
}
}
private Class<?> myLoadClass(String name) throws IOException,
ClassFormatError {
logger.debug("myload class {}", name);
System.out.printf("myload class %s\n", name);
if (alternativesRegistry.contains(name) && altjar != null) {
JarEntry entry = altjar.getJarEntry(name + ".class");
InputStream is = altjar.getInputStream(entry);
return readClassData(name, is);
}
String path = others.get(name);
if (path == null || path.isEmpty()) {
return null;
}
if (path.endsWith(".jar")) {
JarFile jar = new JarFile(path);
JarEntry entry = jar.getJarEntry(name + ".class");
InputStream is = jar.getInputStream(entry);
return readClassData(name, is);
} else {// it's a folder, need to read clz from .class file
System.out.printf("file path for %s is %s\n", name, path);
InputStream is = new FileInputStream(new File(path));
return readClassData(name, is);
}
}
private Class<?> readClassData(String name, InputStream is)
throws IOException, ClassFormatError {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
int len = is.read(buffer);
while (len > 0) {
out.write(buffer, 0, len);
len = is.read(buffer);
}
Class<?> clz = defineClass(name, out.toByteArray(), 0, out.size());
if (clz != null) {
System.out.printf("loaded %s by me\n", name);
clzCache.put(name, clz);
}
return clz;
}
protected Class<?> findCachedClass(String name)
throws ClassNotFoundException {
Class<?> clz = clzCache.get(name);
if (clz == null) {
clz = findLoadedClass(name);
}
return clz;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findClass: " + name);
Class<?> cls = findCachedClass(name);
if (cls == null) {
try {
cls = myLoadClass(name);
} catch (ClassFormatError | IOException e) {
logger.error("failed to load class {}", name, e);
System.out.printf("failed to load class %s\n", name);
e.printStackTrace();
}
}
return cls;
}
}
我试图覆盖findResource(),但它从未被调用。
这就是我使用类加载器的方法:
java -Djava.system.class.loader=AlternativeClassLoader -classpath=.:./alternatives.jar:./slf4j-xxx.jar Test
答案 0 :(得分:0)
所以以下是源代码:
public class AlternativeClassLoader extends ClassLoader {
private static final String ALTERNATIVE_JAR_PROPERTY = "alternativejar";
private Map<String, Class<?>> clzCache = new HashMap<String, Class<?>>();
public AlternativeClassLoader(ClassLoader parent) {
super(parent);
loadAlternativeClasses();
}
private void loadAlternativeClasses() {
String jarFilePath = System.getProperty(ALTERNATIVE_JAR_PROPERTY);
if (jarFilePath == null || jarFilePath.isEmpty()){
URL url = getClass().getProtectionDomain().getCodeSource().getLocation();
System.out.println(url + ":" + url.toString());
jarFilePath = url.getPath();
}
JarInputStream jis;
try {
jis = new JarInputStream(new FileInputStream(jarFilePath));
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null){
String className = entry.getName();
className = className.substring(0, className.length() - ".class".length());
System.out.printf("loading class from %s: %s\n", jarFilePath, className);
readClassData(className, jis);
}
} catch (IOException e) {
e.printStackTrace();
}
} private Class<?> readClassData(String name, InputStream is) throws IOException,
ClassFormatError {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
int len = is.read(buffer);
while (len > 0) {
out.write(buffer, 0, len);
len = is.read(buffer);
}
Class<?> clz = defineClass(name, out.toByteArray(), 0, out.size());
if (clz != null) {
System.out.printf("loaded %s by myself\n", name);
clzCache.put(name, clz);
}
return clz;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findClass: " + name);
Class<?> cls = clzCache.get(name);
if (cls == null)
cls = super.findClass(name);
return cls;
}
}