您的位置:首页 > 其它

classloader之getresource,findClass深度分析

2016-05-03 21:56 281 查看
openjdk7:http://download.java.net/openjdk/jdk7

当我们在用classload去加载类的时候,classload去哪里加载呢?

肯定首先是有父加载器去加载,当父加载不到的时候,才有当前加载器去加载。

其实我们常用的就是bootclassload,extclassload和appclassload,其中extclassload和appclassload都是继承urlclassload的,鼻祖就是抽象的classload类,classload类只是定义了类加载的顺序,具体实现大部分还是在urlclassload里面的,每个urlclassload内部都会维护一组URL,当加载类和资源的时候,其实就是从这些url里面加载的。



本文先简单的分析下,详细的下文再分析。

回到主题,本文要讲的是classload.getResource()方法。

1、 URL ClassLoader.getResource(String name) 返回第一个url

先看下鼻祖ClassLoad的

/**
* Finds the resource with the given name.  <strong>A resource is some data
* (images, audio, text, etc) that can be accessed by class code in a way
* that is independent of the location of the code</strong>.
*
* <p> The name of a resource is a '<tt>/</tt>'-separated path name that
* identifies the resource.
*
* @param  name
*         The resource name
*
* @return  A <tt>URL</tt> object for reading the resource, or
*          <tt>null</tt> if the resource could not be found or the invoker
*          doesn't have adequate  privileges to get the resource.
*
* @since  1.1
*/
public URL getResource(String name) {
URL url;
if (parent != null) {
url = parent.getResource(name);
} else {
url = getBootstrapResource(name);
}
if (url == null) {
url = findResource(name);
}
return url;
}

protected URL findResource(String name) {
return null;
}


getResource 方法返回的优先是父加载器加载的资源,并且只返回第一个匹配的资源


getResource 方法表示一种可以被类访问的资源


findResource 方法是protected类型的,表示只有子类才可以调用。


资源以 /  分割

2、ClassLoader.Enumeration<URL> getResources(String name) 返回所以得url

public Enumeration<URL> getResources(String name) throws IOException {
Enumeration[] tmp = new Enumeration[2];
if (parent != null) {
tmp[0] = parent.getResources(name);
} else {
tmp[0] = getBootstrapResources(name);
}
tmp[1] = findResources(name);

return new CompoundEnumeration<>(tmp);
}

protected Enumeration<URL> findResources(String name) throws IOException {
return java.util.Collections.emptyEnumeration();
}


findResource 方法是protected类型的,表示只有子类才可以调用。

3、URLClassLoader.findResources

URLClassLoader继承了ClassLoader,只是实现了里面的findResource两个方法。每个URLClassLoader 内部维护了一组url,初始化URLClassLoader和通过addUrl(url)可以添加url,然后findResource只是从这些url里面查找

private final URLClassPath ucp;


public URL findResource(final String name) {
/*
* The same restriction to finding classes applies to resources
*/
URL url = AccessController.doPrivileged(
new PrivilegedAction<URL>() {
public URL run() {
return ucp.findResource(name, true);
}
}, acc);

return url != null ? ucp.checkURL(url) : null;
}


public Enumeration<URL> findResources(final String name)
throws IOException
{
final Enumeration<URL> e = ucp.findResources(name, true);

return new Enumeration<URL>() {
private URL url = null;

private boolean next() {
if (url != null) {
return true;
}
do {
URL u = AccessController.doPrivileged(
new PrivilegedAction<URL>() {
public URL run() {
if (!e.hasMoreElements())
return null;
return e.nextElement();
}
}, acc);
if (u == null)
break;
url = ucp.checkURL(u);
} while (url == null);
return url != null;
}

public URL nextElement() {
if (!next()) {
throw new NoSuchElementException();
}
URL u = url;
url = null;
return u;
}

public boolean hasMoreElements() {
return next();
}
};
}

在URLClassLoader里面,我们可以调用getResource和findResource四个方法。

其中getResource优先返回父加载器里面的资源

findResource直接而且只是从当前类加载器的url里面去查询资源(不请求父加载器)


getResource和findResource返回的Enumeration<URL>方法,顺序和传进去的url顺序有关(逐次访问)

4、实战

package cn.myroute;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class MyClassLoader extends URLClassLoader{

public MyClassLoader(URL[] urls) {
super(urls);
// TODO Auto-generated constructor stub
}

public void addDir(String dir) throws MalformedURLException{
dir = "file:"+dir;
addURL(new URL(dir));
}

public void addJar(String jar) throws MalformedURLException{
addURL(new URL("file:"+jar));
}

}


package cn.myroute;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;

import org.junit.Test;

public class CommonTest {

@Test
public void test1(){
try {
ClassLoader cl = getClass().getClassLoader();
print(cl);
MyClassLoader myLoader = new MyClassLoader(new URL[0]);
myLoader.addDir("E:\\java\\youku_workspace\\JavaNavi-2.0.4\\target\\classes\\");
myLoader.addDir("E:\\java\\youku_workspace\\JavaNavi-2.0.4\\target\\classes2\\");
myLoader.addJar("e:/java/chill-java-0.3.5.jar");
print(myLoader);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public void print(ClassLoader cl) throws IOException{
System.out.println("loader:"+cl+"---------------------------------");
String name = "server.conf";
URL url = cl.getResource(name);
System.out.println(url);
System.out.println();
Enumeration<URL> en = cl.getResources(name);
while(en.hasMoreElements()){
System.out.println(en.nextElement());
}
System.out.println("get resource end");
System.out.println();

if(cl instanceof URLClassLoader){
System.out.println("begin urlclassloader");
URLClassLoader uLoader = (URLClassLoader) cl;
url = uLoader.findResource(name);
System.out.println(url);
System.out.println();
en = uLoader.findResources(name);
while(en.hasMoreElements()){
System.out.println(en.nextElement());
}
System.out.println("findResources end");
System.out.println();
}
System.out.println("----over---------");
}
}

loader:sun.misc.Launcher$AppClassLoader@106d69c---------------------------------
file:/E:/java/my_workspace/myclassload/bin/server.conf

file:/E:/java/my_workspace/myclassload/bin/server.conf
get resource end

begin urlclassloader
file:/E:/java/my_workspace/myclassload/bin/server.conf

file:/E:/java/my_workspace/myclassload/bin/server.conf
findResources end

----over---------
loader:cn.myroute.MyClassLoader@5f4c2e---------------------------------
file:/E:/java/my_workspace/myclassload/bin/server.conf

file:/E:/java/my_workspace/myclassload/bin/server.conf
file:E:/java/youku_workspace/JavaNavi-2.0.4/target/classes/server.conf
file:E:/java/youku_workspace/JavaNavi-2.0.4/target/classes2/server.conf
jar:file:e:/java/chill-java-0.3.5.jar!/server.conf
get resource end

begin urlclassloader
file:E:/java/youku_workspace/JavaNavi-2.0.4/target/classes/server.conf

file:E:/java/youku_workspace/JavaNavi-2.0.4/target/classes/server.conf
file:E:/java/youku_workspace/JavaNavi-2.0.4/target/classes2/server.conf
jar:file
bc15
:e:/java/chill-java-0.3.5.jar!/server.conf
findResources end

----over---------


5、findClass方法

findClass方法定义在鼻祖ClassLoader中,但只是个空方法。但是表示的意思就是,当前加载器加载类的时候,去哪里找到相应的类。具体实现在URLClassLoader里面。

先看下鼻祖ClassLoader的实现

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);
}


再看下URLClassLoader的实现

protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Class>() {
public Class run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
throw new ClassNotFoundException(name);
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
}

可以看到加载类时,是把类当成其中的一种资源去查找的。

String path = name.replace('.', '/').concat(".class");

 Resource res = ucp.getResource(path, false);


6、深入URLClassPath

URLClassLoader里面定义了个URLClassPath的变量,加入的url都会到这个类里面来。


URLClassPath里面会把每个url解析成一个load,然后查找资源的时候,就去每个load里面查找。


load类有三个实现,分别是load(代表网络的jar包),FileLoader(代表目录,典型的bin目录,切记不是lib目录,里面有好多jiar包),JarLoader(代表本地一个jar包)。

private Loader getLoader(final URL url) throws IOException {
try {
return java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Loader>() {
public Loader run() throws IOException {
String file = url.getFile();
if (file != null && file.endsWith("/")) {
if ("file".equals(url.getProtocol())) {
return new FileLoader(url);
} else {
return new Loader(url);
}
} else {
return new JarLoader(url, jarHandler, lmap);
}
}
});
} catch (java.security.PrivilegedActionException pae) {
throw (IOException)pae.getException();
}
}

Loader getLoader(final URL url)  方法根据传进来的url类型解析成三个对应的load,注意里面的if条件判断






内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  classloader getResource