Spring资源加载基础ClassLoader
1 ClassLoader工作机制
1.1 ClassLoader作用
寻找类字节码文件并构造出类在JVM内部表示的组件.负责运行时查找和装入Class字节码文件
1.2 装载步骤
1.2.1 装载
查找装载class字节码文件
1.2.2 链接
执行校验,准备和解析步骤,其中解析步骤时可选的
1.2.2.1 校验
检查装载Class文件的正确性
1.2.2.2 准备
给类的静态变量分配存储空间
1.2.2.3 解析
将符号引用转换为直接引用
1.2.3 初始化
对类的静态变量,方法,代码块执行初始化操作
2 JVM中提供的ClassLoader
2.1 Bootstrap ClassLoader(根加载器)
最顶层的装载器,它不是ClassLoader的子类,采用C++编写,因此在JAVA中不可见。主要负责装载JRE核心类库。可以通过jvm启动参数-Xbootclasspath改变该加载器加载的路径
2.2 Extention ClassLoader(扩展加载器)
主要负责加载JRE扩展目录ext下的包,可以通过-D java.ext.dirs选项指定目录
2.3 App ClassLoader(应用加载器/系统加载器)
负责加载当前工程目录下,classpath下的包或者class文件
其中Extention ClassLoader & AppClassLoader是ClassLoader的子类,根加载器是扩展加载器的父加载器,扩展加载器是应用加载器的父加载器。在默认情况下使用应用加载器
3 JVM加载机制
类加载采用“全盘负责委托机制”。
“全盘负责”:在类加载时指定一个ClassLoader,除非显示声明其他的加载器,否则该类所依赖的类以及引用的类都由该加载器加载。
“委托机制”:类加载时优先委托父加载器寻找并加载目标类,只有在父加载器没有找到的情况下,才从自己的classpath路径下查找并加载目标类。该点主要是出于安全的考虑,在classpath路径下定义JDK中已经存在的类,由于该机制,JDK中的类都由父加载器开始查找并加载.每个加载器都有缓存,在委托时优先查找缓存,如果缓存中存在,那么直接返回,否者才执行查找和加载
4 类实例,类描述对象以及ClassLoader关系
类文件被加载后,在JVM内部对应拥有一个java.lang.Class类描述对象,类的每个实例拥有类描述对象的引用,类描述对象拥有类加载器的引用
5 启动顺序
Bootstrap ClassLoader-->Extention ClassLoader-->AppClass Loader即Bootstrap ClassLoader最先启动,接着是Extenion ClassLoader,最后是AppClass Loader
6 源码分析
6.1 JVM入口应用是sun.misc.Launcher
1.定义Bootstrap ClassLoader加载路径(sun.boot.class.path)
2.创建Extention ClassCloader
3.创建App ClassLoader并设置App ClassLoader的父类:Extention ClassCloader
- public class Launcher {
- //Bootstrap ClassLoader加载路径
- private static String bootClassPath = System.getProperty("sun.boot.class.path");
- //该处定义的App ClassLoader
- private ClassLoader loader;
- public Launcher() {
- // Create the extension class loader
- ClassLoader extcl;
- try {
- //创建Extention ClassLoader
- extcl = ExtClassLoader.getExtClassLoader();
- } catch (IOException e) {......}
- // Now create the class loader to use to launch the application
- try {
- //创建AppClassLoader并传入父加载器,由此可看AppClassLoader父加载器是Extention ClassLoader
- loader = AppClassLoader.getAppClassLoader(extcl);
- } catch (IOException e) {......}
- //设置AppClassLoader为当前线程上下问类加载器
- // Also set the context class loader for the primordial thread.
- Thread.currentThread().setContextClassLoader(loader);
- }
- public ClassLoader getClassLoader() {
- return loader;
- }
- static class ExtClassLoader extends URLClassLoader {}
- static class AppClassLoader extends URLClassLoader {}
- }
该类中定义了Bootstrap ClassLoader加载路径,同时创建了Extention ClassLoader以及AppClassLoader,如下是Bootstrap ClassLoader加载路径信息
- /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar
- /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar
- /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/sunrsasign.jar
- /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar
- /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jce.jar
- /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar
- /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jfr.jar
- /usr/lib/jvm/java-8-openjdk-amd64/jre/classes
从输出路径上看Bootstrap ClassLoader主要加载的是jre/lib目录下的资源.即JVM Runtime核心库
6.2 ExtClassLoader源码
1.根据查找路径获得路径(jav.ext.dirs)下文件
2.将文件路径转换为URL
3.调用父类构造函数,创建ExtClassLoader并设置父加载器
- static class ExtClassLoader extends URLClassLoader {
- public static ExtClassLoader getExtClassLoader() throws IOException
- {
- final File[] dirs = getExtDirs();//获得查找路径下的所有文件
- try {
- return AccessController.doPrivileged(
- new PrivilegedExceptionAction<ExtClassLoader>() {
- public ExtClassLoader run() throws IOException {
- ......
- //创建Extention ClassLoader
- return new ExtClassLoader(dirs);
- }
- });
- } catch (java.security.PrivilegedActionException e) {......}
- }
- public ExtClassLoader(File[] dirs) throws IOException {
- //调用父类URLClassLoader的构造函数并传入父加载器,此时为null
- super(getExtURLs(dirs), null, factory);
- }
- private static File[] getExtDirs() {
- //Extention ClassLoader加载路径
- String s = System.getProperty("java.ext.dirs");
- File[] dirs;
- if (s != null) {
- StringTokenizer st = new StringTokenizer(s, File.pathSeparator);
- int count = st.countTokens();
- dirs = new File[count];
- for (int i = 0; i < count; i++) {
- dirs[i] = new File(st.nextToken());
- }
- } else {
- dirs = new File[0];
- }
- return dirs;
- }
- }
- private static URL[] getExtURLs(File[] dirs) throws IOException {
- Vector<URL> urls = new Vector<URL>();
- for (int i = 0; i < dirs.length; i++) {
- String[] files = dirs[i].list();
- if (files != null) {
- for (int j = 0; j < files.length; j++) {
- if (!files[j].equals("meta-index")) {
- File f = new File(dirs[i], files[j]);
- urls.add(getFileURL(f));
- }
- }
- }
- }
- URL[] ua = new URL[urls.size()];
- urls.copyInto(ua);
- return ua;
- }
- static URL getFileURL(File file) {
- try {
- file = file.getCanonicalFile();
- } catch (IOException e) {}
- try {
- return ParseUtil.fileToEncodedURL(file);
- } catch (MalformedURLException e) {
- // Should never happen since we specify the protocol...
- throw new InternalError(e);
- }
- }
如下是java.ext.dirs的输出信息
- /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext
- /usr/java/packages/lib/ext
6.3 AppClassLoader源码
1.获得java.classs.path路径下的所有文件
2.解析文件路径为URL
3.调用父类构造函数创建AppClassLoader并设置父类
- static class AppClassLoader extends URLClassLoader {
- public static ClassLoader getAppClassLoader(final ClassLoader extcl)
- throws IOException
- {
- //AppClassLoader加载路径
- final String s = System.getProperty("java.class.path");
- final File[] path = (s == null) ? new File[0] : getClassPath(s);
- return AccessController.doPrivileged(
- new PrivilegedAction<AppClassLoader>() {
- public AppClassLoader run() {
- URL[] urls =
- (s == null) ? new URL[0] : pathToURLs(path);
- return new AppClassLoader(urls, extcl);
- }
- });
- }
- AppClassLoader(URL[] urls, ClassLoader parent) {
- super(urls, parent, factory);
- }
- }
- }
- private static URL[] pathToURLs(File[] path) {
- URL[] urls = new URL[path.length];
- for (int i = 0; i < path.length; i++) {
- urls[i] = getFileURL(path[i]);
- }
- // DEBUG
- //for (int i = 0; i < urls.length; i++) {
- // System.out.println("urls[" + i + "] = " + '"' + urls[i] + '"');
- //}
- return urls;
- }
Java.class.path输出信息
/work/new_workspace/aaa/bin
7 ClassLoader继承关系
java.lang.Object
java.lang.ClassLoader
java.security.SecureClassLoader
java.net.URLClassLoader
ExtClassLoader
AppClassLoader
8 获得加载器的父加载器
可以通过getParent()获得当前加载器的父加载器
- //Main为自定义Class
- System.out.println(Main.class.getClassLoader());
- System.out.println(Main.class.getClassLoader().getParent());
- System.out.println(Main.class.getClassLoader().getParent().getParent());
- System.out.println(Boolean.class.getClassLoader());
- sun.misc.Launcher$AppClassLoader@330bedb4
- sun.misc.Launcher$ExtClassLoader@5cad8086
- null
- null
为什么获得ExtClassLoader的父加载器为null?这和每个加载器都有一个父加载器违背.这个属于正常现象.具体原因如下:
1.从ExtClassLoader构造函数super(getExtURLs(dirs), null, factory);可以看出此时传入的就是null
2.Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,不是一个JAVA类,无法在java代码中获取它的引用,凡是sun.boot.class.path路径下的包以及类都是由它加载。JVM初始化sun.misc.Launcher创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。BootstrapClassCloader没有父加载器,但是它却可以作为一个ClassLoader的父加载器。比如ExtClassLoader。这也可以解释之前通过ExtClassLoader的getParent方法获取为Null的现象.我们可以分析getParent()的源码
父加载器可以直接由外界指定,如果外界不指定,那么采用AppClassLoader作为父加载器
//在创建ExtClassLoader时,采用super(getExtURLs(dirs), null, factory).所以获得的是null
- public URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
- super(parent);
- }
- public abstract class ClassLoader {
- //父加载器直接由外部指定
- protected ClassLoader(ClassLoader parent) {
- this(checkCreateClassLoader(), parent);
- }
- }
- //没有指定父加载器的情况下将系统加载器设置为父加载器即AppClassLoader
- protected ClassLoader() {
- this(checkCreateClassLoader(), getSystemClassLoader());
- }
- private ClassLoader(Void unused, ClassLoader parent) {
- this.parent = parent;
- ...
- }
- public final ClassLoader getParent() {
- if (parent == null)
- return null;
- ..........
- return parent;
- }
- public static ClassLoader getSystemClassLoader() {
- initSystemClassLoader();
- ......
- return scl;
- }
- private static synchronized void initSystemClassLoader() {
- if (!sclSet) {
- //通过Launcher获取ClassLoader,其实就是AppClassLoader
- sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
- scl = l.getClassLoader();
- }
9 重要方法
9.1 URL getResource(String name)
查找给定名称的资源。资源的名称是一个/分隔的路径名称标识资源,优先查找父加载器,如果找不到在从Bootstrap ClassLoader路径中查找并加载
- 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;
- }
9.2 Enumeration<URL> getResources(String name)
查找给定名称的所有资源。资源的名称是一个/分隔的路径名称标识资源,优先查找父加载器,如果找不到在从Bootstrap ClassLoader路径中查找并加载,该模式下支持正则匹配
- public Enumeration<URL> getResources(String name) throws IOException {
- @SuppressWarnings("unchecked")
- Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
- if (parent != null) {
- tmp[0] = parent.getResources(name);
- } else {
- tmp[0] = getBootstrapResources(name);
- }
- tmp[1] = findResources(name);
- return new CompoundEnumeration<>(tmp);
- }
9.3 URL getSystemResource(String name)
采用AppClassLoader加载给定名称的资源
- public static URL getSystemResource(String name) {
- ClassLoader system = getSystemClassLoader();
- if (system == null) {
- return getBootstrapResource(name);
- }
- return system.getResource(name);
- }
9.4 Enumeration<URL> getSystemResources(String name)
采用AppClassLoader加载给定名称的所有资源
- public static Enumeration<URL> getSystemResources(String name)
- throws IOException
- {
- ClassLoader system = getSystemClassLoader();
- if (system == null) {
- return getBootstrapResources(name);
- }
- return system.getResources(name);
- }
9.5 URL findResource(String name)
查找具有给定名称的资源。类加载器实现应覆盖此方法以指定在哪里查找资源
- protected URL findResource(String name) {
- return null;
- }
9.6 Enumeration<URL> findResources(String name)
查找具有给定名称的所有资源。类加载器实现应覆盖此方法以指定在哪里查找资源
- protected Enumeration<URL> findResources(String name) throws IOException {
- return java.util.Collections.emptyEnumeration();
- }
9.7 loadClass
loadClass(String name) & loadClass(String name, boolean resolve)name指定了类装载器的名字,必须使用全限定名。resolve告诉装载器是否需要解析该类。在初始化之前,应该考虑进行类解析工作。并不是所有的类都需要解析的。如果JVM只需要知道该类是否存在或者找出该类的超类,那么就不需要解析该类
- public abstract class 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) {
- try {
- if (parent != null) {
- //递归调用,查找父加载器
- c = parent.loadClass(name, false);
- } else {
- //调用Bootstrap Classloader
- c = findBootstrapClassOrNull(name);
- }
- } catch (ClassNotFoundException e) {......}
- if (c == null) {
- // If still not found, then invoke findClass in order
- // to find the class.
- //调用findClass
- c = findClass(name);
- ....
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
- }
- }
- protected final Class<?> findLoadedClass(String name) {
- if (!checkName(name))
- return null;
- return findLoadedClass0(name);
- }
- private native final Class<?> findLoadedClass0(String name);
- private Class<?> findBootstrapClassOrNull(String name)
- {
- if (!checkName(name)) return null;
- return findBootstrapClass(name);
- }
- private native Class<?> findBootstrapClass(String name);
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- throw new ClassNotFoundException(name);
- }
9.8 defineClass(String name,byte[] b, int off,int len)
将类文件字节码数组装换成JVM内部的java.lang.Class对象,字节数组可以从本地系统,远程网络获取
- protected final Class<?> defineClass(String name, byte[] b, int off, int len)
- throws ClassFormatError
- {
- return defineClass(name, b, off, len, null);
- }
- protected final Class<?> defineClass(String name, byte[] b, int off, int len,
- ProtectionDomain protectionDomain)
- throws ClassFormatError
- {
- protectionDomain = preDefineClass(name, protectionDomain);
- String source = defineClassSourceLocation(protectionDomain);
- Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
- postDefineClass(c, protectionDomain);
- return c;
- }
- private native Class<?> defineClass1(String name, byte[] b, int off, int len,
- ProtectionDomain pd, String source);
9.9 findSystemClass(String)
从本地文件系统载入Class文件,如果本地文件系统中不存在该Class文件,抛出ClassNotFoundError。该方法是JVM默认使用的装载器
- protected final Class<?> findSystemClass(String name)
- throws ClassNotFoundException
- {
- ClassLoader system = getSystemClassLoader();
- if (system == null) {
- if (!checkName(name))
- throw new ClassNotFoundException(name);
- Class<?> cls = findBootstrapClass(name);
- if (cls == null) {
- throw new ClassNotFoundException(name);
- }
- return cls;
- }
- return system.loadClass(name);
- }
9.10 findLoadedClass(String name)
该方法查看ClassLoader是否已经加载了某个类,如果载入就返回Class对象,否则返回null,如果强行载入已经载入的类会抛出异常
- protected final Class<?> findLoadedClass(String name) {
- if (!checkName(name))
- return null;
- return findLoadedClass0(name);
- }
- private native final Class<?> findLoadedClass0(String name);
10 如何自定义ClassLoader
1.编写一个类继承ClassLoader
2.重写findClass 方法
3.在findClass中调用defineClass即可
11 Context ClassLoader 线程上下文类加载器
- public class Thread implements Runnable {
- /* The context ClassLoader for this thread */
- private ClassLoader contextClassLoader;
- public void setContextClassLoader(ClassLoader cl) {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(new RuntimePermission("setContextClassLoader"));
- }
- contextClassLoader = cl;
- }
- public ClassLoader getContextClassLoader() {
- if (contextClassLoader == null)
- return null;
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- ClassLoader.checkClassLoaderPermission(contextClassLoader,
- Reflection.getCallerClass());
- }
- return contextClassLoader;
- }
- }
contextClassLoader只是一个成员变量,通过setContextClassLoader()方法设置,通过getContextClassLoader()获得。
每个Thread都有一个相关联的ClassLoader,默认是AppClassLoader。并且子线程默认使用父线程的ClassLoader除非子线程特别设置
12 自定义ClassLoader的作用
常见的用法是将Class文件按照某种加密手段进行加密,然后按照规则编写自定义的ClassLoader进行解密,这样我们就可以在程序中加载特定了类,并且这个类只能被我们自定义的加载器进行加载,提高了程序的安全性。
Spring资源加载基础ClassLoader的更多相关文章
- 06.Spring 资源加载 - ResourceLoader
基本概念 ResourceLoader 接口,在 Spring 中用于加载资源,通过它可以获取一个 Resouce 对象. 内部构造 首先来看它的接口定义: public interface Reso ...
- Spring资源加载器抽象和缺省实现 -- ResourceLoader + DefaultResourceLoader(摘)
概述 对于每一个底层资源,比如文件系统中的一个文件,classpath上的一个文件,或者一个以URL形式表示的网络资源,Spring 统一使用 Resource 接口进行了建模抽象,相应地,对于这些资 ...
- spring资源加载结构解析
1.spring中资源加载使用resources的原因? 在java将不同资源抽象成url,然后通过注册不同的hander来处理不同读取逻辑,一般hander使用协议的前缀来命名,如http,jar, ...
- 05.Spring 资源加载 - Resource
基本概念 Spring 把所有能记录信息的载体,如各种类型的文件.二进制流等都称为资源. 对 Spring 开发者来说,最常用的资源就是 Spring 配置文件(通常是一份 XML 格式的文件). S ...
- spring 资源加载使用说明
Spring 提供了一个强大加载资源的机制,不但能够通过“classpath:”.“file:” 等资源地址前缀识别不同的资源类型,还支持Ant 风格带通配符的资源地址. 首先,我们来了解一下Spri ...
- Spring 资源加载
pom.xml ``` org.springframework spring-core 4.3.14.RELEASE org.springframework spring-beans 4.3.16.R ...
- 【死磕 Spring】----- IOC 之 Spring 统一资源加载策略
原文出自:http://cmsblogs.com 在学 Java SE 的时候我们学习了一个标准类 java.net.URL,该类在 Java SE 中的定位为统一资源定位器(Uniform Reso ...
- spring资源访问接口和资源加载接口
spring 资源访问接口 JDK提供的资源访问类,如java.net.URL.File等,不能很好地满足各种资源的访问需求,比如缺少从类路径或者Web容器的上下文中获取资源的操作类. 鉴于此,spr ...
- 简说Spring中的资源加载
声明: 本文若有 任何纰漏.错误,请不吝指正!谢谢! 问题描述 遇到一个关于资源加载的问题,因此简单的记录一下,对Spring资源加载也做一个记录. 问题起因是使用了@PropertySource来进 ...
随机推荐
- sql server字符串中怎么添加换行?
换行/回车,可以使用CHAR函数处理,比如: 1 insert into tbtest (text) values ('abc' + char(13)+char(10) + 'def') 主要还是要看 ...
- ovs的主要代码函数及大体结构图
作者: 北京-小武 邮箱:night_elf1020@163.com 新浪微博:北京-小武 最终抽时间把openvswitch的2.0代码看的思路汇总了下,因为是曾经好早下载的.看完了也才看到2.1依 ...
- Canvas标签基础
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- js绘制圆形时钟
纯js制作圆形时钟 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- Problem B. Full Binary Tree
题目 链接:http://code.google.com/codejam/contest/2984486/dashboard#s=p1 googlde code jam 2014 Round1A 解题 ...
- visual studio 2010 winform程序不能添加对system.web的引用
visual studio 2010 winform程序不能添加对system.web的引用[转载] 需要引用到System.Web.发现没有“System.Web”.在通过“浏览”方式,找到该DLL ...
- npm包上传下载的命令及例子
npm包上传下载的命令及例子. 新建hello.js 执行:npm init 执行:npm adduser ( username:XXX password:XXX email:XXX ) 上传:npm ...
- zw版【转发·台湾nvp系列Delphi例程】HALCON TestRegionPoint2
zw版[转发·台湾nvp系列Delphi例程]HALCON TestRegionPoint2 procedure TForm1.Button1Click(Sender: TObject);var op ...
- 【译】在Asp.Net中操作PDF - iTextSharp - 利用列进行排版(转)
[译]在Asp.Net中操作PDF - iTextSharp - 利用列进行排版 在使用iTextSharp通过ASP.Net生成PDF的系列文章中,前面的文章已经讲述了iTextSharp所涵盖 ...
- Python: 从字典中提取子集--字典推导
问题: 构造一个字典,它是另外一个字典的子集 answer: 最简单的方式是使用字典推导 eg1: 1. >>>prices = {'ACME': 45.23, 'AAPL': 61 ...