1.写一个自己的ClassLoader,继承URLClassLoader
package com.tomcat7.test;
import java.net.URL;
import java.net.URLClassLoader;
public class MyClassLoader extends URLClassLoader{
public MyClassLoader(URL[] urls) {
super(urls);
}
public Class<?> findClass(final String name)
throws ClassNotFoundException {
return super.findClass(name);
}
}
这里说下为什么要继承,而不直接使用URLClassLoader
,是因为findClass
是protected
的,详情可查看URLClassLoader
源码
2.编写测试类,里面没有直接引入其它package
import java.lang.reflect.Method;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
public class ClassLoader {
public static final String JAR_PATH = "E:\\maven\\repository\\commons-lang\\commons-lang\\2.6\\commons-lang-2.6.jar";
public static void main(String[] args) {
try {
URL[] urls = { (new File(JAR_PATH).toURI().toURL()) };
MyClassLoader loader = new MyClassLoader(urls);
Class c = loader.findClass("org.apache.commons.lang.StringUtils");
Method method = c.getMethod("startsWith", String.class,String.class);
Object ret = method.invoke(c, "abcd","ad");
System.out.println("ret:" + ret);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.运行测试类,输出:
ret:false
可以看出已经成功执行
1.运用这种方法,有时候我们可以动态的加入一些Jar,如:动态添加一些插件什么的。但这样效率如何还有待验证。
2.也可以直接像tomcat一样运用,对需要使用的Jar,不直接通过classpath引入,而用这种方式。tomcat7中的StandardClassLoader
,是标记为@Deprecated
的,而且里面也没有添加其它什么东西,和URLClassLoader
一样
通过进一步跟踪,发现URLClassLoader
继承的ClassLoader
,里面有很多关于加载的,目前看了一下,直接返回Class
的有
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
Class c = null;
String source = defineClassSourceLocation(protectionDomain);
try {
c = defineClass1(name, b, off, len, protectionDomain, source);
} catch (ClassFormatError cfe) {
c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
source);
}
postDefineClass(c, protectionDomain);
return c;
}
findClass
没有直接在ClssLoader
中实现,在子类实现,应该是预留让开发人员可以定制,像这里的URLClassLoader
里面就自己实现了。在调用loadClass
也有可能调用到findClass
。
问题:
1.这三个有什么区别?看了一下API说defineClass
可以以符合规范的Class
字节码生成实例,如API中所说的
网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。示例实现如下:
class NetworkClassLoader extends ClassLoader { String host; int port; public Class findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); } private byte[] loadClassData(String name) { // load the class data from the connection . . . } }
貌似可以用来实现远程调用。。。。
2.loadClass
好像功能最全一点,加载的优先级?对于上面的测试方法,应该可以直接调用loadClass
。而不用再去自己写一个MyClassLoader
重写findClass
3.findClass
应该是预留给开发人员来扩展,用这个应该可以做一些其它什么事?