classloader之getresource,findClass深度分析



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: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条件判断








  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值