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()方法。
getResource 方法返回的优先是父加载器加载的资源,并且只返回第一个匹配的资源
getResource 方法表示一种可以被类访问的资源
findResource 方法是protected类型的,表示只有子类才可以调用。
资源以 / 分割
findResource 方法是protected类型的,表示只有子类才可以调用。
在URLClassLoader里面,我们可以调用getResource和findResource四个方法。
其中getResource优先返回父加载器里面的资源
findResource直接而且只是从当前类加载器的url里面去查询资源(不请求父加载器)
getResource和findResource返回的Enumeration<URL>方法,顺序和传进去的url顺序有关(逐次访问)
先看下鼻祖ClassLoader的实现
再看下URLClassLoader的实现
可以看到加载类时,是把类当成其中的一种资源去查找的。
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
URLClassPath里面会把每个url解析成一个load,然后查找资源的时候,就去每个load里面查找。
load类有三个实现,分别是load(代表网络的jar包),FileLoader(代表目录,典型的bin目录,切记不是lib目录,里面有好多jiar包),JarLoader(代表本地一个jar包)。
Loader getLoader(final URL url) 方法根据传进来的url类型解析成三个对应的load,注意里面的if条件判断
当我们在用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条件判断
相关文章推荐
- AS3自写类整理笔记 ClassLoader类第1/2页
- 深入解析Java中的Classloader的运行机制
- java安全沙箱(一)之ClassLoader双亲委派机制
- 加载应用外面的数据库驱动获取连接
- 使用了Spring boot devtools, dozer转换嵌套对象失败解决记录
- 类加载器获取资源文件
- 【转载】<实战> 分析PermGen上存放的被Classloader所加载的类实践
- ClassLoader加载机制
- classLoader 原理
- 动态加载远程Jar的实现方式
- Java类加载器
- Java类加载器-Bootstrap、 ExtClassLoader、 AppClassLoader
- assert 的两种用法
- ioc解析java反摄机制
- 生成对象的方法比较
- 提升JSP页面响应速度的七大秘籍绝招
- class loader 相关笔记
- 8月25日学习杂记
- Java相关技术
- Bouncy Castle 多版本 与 Classloader 的问题