Java类加载器ClassLoader的使用详解

目录
  • BootstrapClassLoader
  • ExtClassLoader
  • AppClassLoader
  • 类加载器的具体实现在哪里
  • 类加载器的初始化时机
  • 如何进行的类加载
  • Loader.getResource(resourceName)
  • 调用获取资源方法的一些常见问题

BootstrapClassLoader

加载范围(根据系统参数):

System.getProperty("sun.boot.class.path");

负责加载核心类库,以我的本地的环境来展示获取的内容:

D:\development\jdk\jdk8\jre\lib\resources.jar;
D:\development\jdk\jdk8\jre\lib\rt.jar;
D:\development\jdk\jdk8\jre\lib\sunrsasign.jar;
D:\development\jdk\jdk8\jre\lib\jsse.jar;
D:\development\jdk\jdk8\jre\lib\jce.jar;
D:\development\jdk\jdk8\jre\lib\charsets.jar;
D:\development\jdk\jdk8\jre\lib\jfr.jar;
D:\development\jdk\jdk8\jre\classes;
C:\Users\18140\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\222.3345.118\plugins\java\lib\rt\debugger-agent.jar

修改加载范围:

  • -Xbootclasspath: 重新设定核心类库的搜索路径,不建议使用。
  • -Xbootclasspath/a: 在已有的-XBootclasspath后面追加搜索路径。较常用。
  • -Xbootclasspath/p: 在已有的-XBootclasspath前面追加搜索路径。不常用,因为可能会引起依赖丢失。

例(:号赋参数值):

java -Xbootclasspath/a:D:\XXXFolder; ProgramName

ExtClassLoader

加载范围(根据系统参数):

System.getProperty("java.ext.dirs");

负责加载扩展类库,以我的本地的环境来展示获取的内容:

D:\development\jdk\jdk8\jre\lib\ext;
C:\Windows\Sun\Java\lib\ext

修改加载范围,例(=号赋参数值):

java -Djava.ext.dirs=D:\XXXFolder; ProgramName

AppClassLoader

加载范围(根据系统参数):

System.getProperty("java.class.path");

负责加载应用程序的类与类库,其实就是classpath下的内容。以我的本地的环境来展示获取的内容:

.......
D:\development\jdk\jdk8\jre\lib\rt.jar;
D:\development\idea\workspace\personal\resources-operation\target\classes;
D:\development\maven\apache-maven-resp\org\springframework\boot\spring-boot-starter-web\2.3.7.RELEASE\spring-boot-starter-web-2.3.7.RELEASE.jar;
.......

修改加载范围,例(空格后赋参数值):

java -classpath D:\XXXFolder; ProgramName
// 简写
java -cp D:\XXXFolder; ProgramName

类加载器的具体实现在哪里

Ext、AppClassLoader类加载器在sun.misc.Launcher中,通过内部静态类的形式进行了实现。

由于BootstrapClassLoader是非java语言实现的,故没有对应的内部静态类,但存有一个BootClassPathHolder静态内部类在Launcher中。

public class Launcher {
  static class AppClassLoader extends URLClassLoader {}
  static class ExtClassLoader extends URLClassLoader {}
  private static class BootClassPathHolder {}
}

类加载器的初始化时机

public class Launcher {
  public Launcher() {
    ExtClassLoader var1;
    try {
     // 1. 创建ExtClassLoader
     var1 = Launcher.ExtClassLoader.getExtClassLoader();// 68
    } catch (IOException var10) {// 69
     throw new InternalError("Could not create extension class loader", var10);// 70
    }
    try {
     // 2. 创建AppClassLoader,并将ExtClassLoader作为ParentClassLoader
     this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);// 76
    } catch (IOException var9) {// 77
     throw new InternalError("Could not create application class loader", var9);// 78
    }
    // 3. 将AppClassLoader挂载到当前线程
    Thread.currentThread().setContextClassLoader(this.loader);// 83
    // ......
  }
}

如何进行的类加载

不同的类加载器会根据不同的系统参数,获取不同的一组扫描路径。并根据扫描路径(Path)的类型(文件夹、Jar包),

维护一组Path与Loader实例在类加载器的父类URLClassLoader的ucp(URLClassPath)字段中。

当我们通过类加载器的loadClass方法加载类时,真正依赖的便是loader。

package sun.misc;
public class URLClassPath {
  private static class Loader implements Closeable {}
  static class JarLoader extends Loader {}
  private static class FileLoader extends Loader {}
}

那么,当我们通过lodeClass时,

public static void main(String[] args) throws ClassNotFoundException {
    // 1. 重新设定ClassPath
    System.setProperty("java.class.path", "D:\\development\\idea\\workspace\\personal\\resources-operation\\target\\classes");
    // 2. 重新初始化Launcher
    Launcher launcher = new Launcher();
    // 3. 获取AppClassLoader
    ClassLoader classLoader = launcher.getClassLoader();
    // 4. loadClass
    Class<?> aClass = classLoader.loadClass(ResourcesOperationApplication.class.getName());
    System.out.println(aClass.getName());
}

是如何落实到loader的呢?以AppClassLoader为例:

static class AppClassLoader extends URLClassLoader {
  final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
  // 获取AppClassLoader实例
  public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
   // 1. 读取classpath系统参数
   final String var1 = System.getProperty("java.class.path");
   // 2. 转为File实例
   final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
   return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction<AppClassLoader>() {
    public AppClassLoader run() {
     // 3. File转为URL
     URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
     // 3. 创建实例
     return new AppClassLoader(var1x, var0);
    }
   });
  }
  AppClassLoader(URL[] var1, ClassLoader var2) {
   // 4. 初始化父类,并传递了一组URL实例
   super(var1, var2, Launcher.factory);
   this.ucp.initLookupCache(this);
  }
 // ......
}
public class URLClassLoader extends SecureClassLoader implements Closeable {
  /* The search path for classes and resources */
  private final URLClassPath ucp;
  /* The context to be used when loading classes and resources */
  private final AccessControlContext acc;
  public URLClassLoader(URL[] urls, ClassLoader parent,
          URLStreamHandlerFactory factory) {
   super(parent);
   SecurityManager security = System.getSecurityManager();
   if (security != null) {
    security.checkCreateClassLoader();
   }
   acc = AccessController.getContext();
   // 5. 将URL实例封装到URLClassPath中
   ucp = new URLClassPath(urls, factory, acc);
  }
}
public class URLClassPath {
  private ArrayList<URL> path;
  Stack<URL> urls;
  ArrayList<Loader> loaders;
  HashMap<String, Loader> lmap;
  private URLStreamHandler jarHandler;
  private boolean closed;
  public URLClassPath(URL[] var1, URLStreamHandlerFactory var2, AccessControlContext var3) {
    this.path = new ArrayList();// 101
    this.urls = new Stack();// 104
    this.loaders = new ArrayList();// 107
    this.lmap = new HashMap();// 110
    this.closed = false;// 116
    // 6. 将URL封装到内部的path中
    for(int var4 = 0; var4 < var1.length; ++var4) {// 138
     this.path.add(var1[var4]);// 139
    }
    this.push(var1);// 141
    if (var2 != null) {// 142
     this.jarHandler = var2.createURLStreamHandler("jar");// 143
    }
    if (DISABLE_ACC_CHECKING) {// 145
     this.acc = null;// 146
    } else {
     this.acc = var3;// 148
    }
  }
}

但是并没有发现关于将URL转为loader的代码,那么loader是何时创建的?以上面的测试代码为切入口:

Class<?> aClass = classLoader.loadClass(ResourcesOperationApplication.class.getName());

直接定位到核心方法,前继的非核心方法就跳过了

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) {
                long t0 = System.nanoTime();
                // 父系委托机制
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }
                if (c == null) {
                    // ...
                    // 查找类资源
                    c = findClass(name);
                    // ...
                }
                // ...
            }
            return c;
        }
    }
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
        final Class<?> result;
        try {
            result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        // 1. 将类的限定名转为 '/' 分隔的形式,例:life.cqq.xxx.ClassName -> life/cqq/xxx/ClassName.class
                        String path = name.replace('.', '/').concat(".class");
                        // 2. 通过URLClassPath获取类资源
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                // 最终根据类名 & 已加载的类资源创建Class对象
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            } catch (ClassFormatError e2) {
                                if (res.getDataError() != null) {
                                    e2.addSuppressed(res.getDataError());
                                }
                                throw e2;
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }
}
public class URLClassPath {
    public Resource getResource(String var1, boolean var2) {
        // ......
        Loader var3;
        // 3. getNextLoader:获取可以处理类资源的loader
        for (int var5 = 0; (var3 = this.getNextLoader(var4, var5)) != null; ++var5) {// 249
            // 5. 通过获取到的loader获取类资源
            Resource var6 = var3.getResource(var1, var2);// 250
            if (var6 != null) {// 251
                return var6;// 252
            }
        }
        return null;// 255
    }
    // getNextLoader最终会调用到该方法
    private Loader getLoader(final URL var1) throws IOException {
        try {
            return (Loader) AccessController.doPrivileged(new PrivilegedExceptionAction<Loader>() {// 565
                public Loader run() throws IOException {
                    String var1x = var1.getFile();// 568
                    // 4. 最终的判断:根据URL创建不同的loader并返回
                    // url.endsWith("/") && "file".equals(var1.getProtocol() ? FileLoader : JarLoader
                    if (var1x != null && var1x.endsWith("/")) {// 569
                        return (Loader) ("file".equals(var1.getProtocol()) ? new FileLoader(var1) : new Loader(var1));// 570
                    } else {
                        return new JarLoader(var1, URLClassPath.this.jarHandler, URLClassPath.this.lmap,
                            URLClassPath.this.acc);// 576
                    }
                }
            }, this.acc);
        } catch (PrivilegedActionException var3) {// 580
            throw (IOException) var3.getException();// 581
        }
    }
}

可以发现,loader其实是懒加载的。仅有当已有loader无法加载时,会从path列表中读取仍未被创建loader的path并创建loader。

总结一下:

类加载器会根据系统参数创建对应的适配类型的Loader(FileLoader | JarLoader),且Loader是懒加载的。并维护在父类URLClassLoader的ucp(URLClassPath)属性中的loaders列表中。

loadClass时,最终会执行getResource方法,迭代Loader实例集合,尝试获取资源实例。最终根据类名 & 已加载的类资源在defineClass中创建Class对象。

Loader.getResource(resourceName)

这个方法有必要说明一下,为后继获取资源文件问题做下铺垫。方法是用法就是获取资源,在loadClass方法时获取类文件,类文件抽象来说也是一种资源,只是loadClass方法中多了一些关于类的处理。

当我们获取的非类文件资源,而是其他资源,如不同类型的配置文件,那么这些资源在哪里,如何加载,是需要说清的两个问题。

  • where问题的答案其实就是当前类加载器能够搜索的范围是什么,即设定的系统参数。
  • 如何加载说的也是前面提到的loader,不同的loader(file、jar)处理不同的内容。比如FileLoader就会判断资源是否在目录下,那么JarLoader便是判断资源是否在Jar包中。
public class URLClassPath {
    public Resource getResource(String var1, boolean var2) {
        // ......
        Loader var3;
        for (int var5 = 0; (var3 = this.getNextLoader(var4, var5)) != null; ++var5) {// 249
            // 0. 通过获取到的loader获取类资源
            Resource var6 = var3.getResource(var1, var2);// 250
            if (var6 != null) {// 251
                return var6;// 252
            }
        }
        return null;// 255
    }
    // 1. 文件加载器
    private static class FileLoader extends Loader {
        private File dir;
        // ...
        Resource getResource(final String var1, boolean var2) {
            try {
                URL var4 = new URL(this.getBaseURL(), ".");// 1370
                final URL var3 = new URL(this.getBaseURL(), ParseUtil.encodePath(var1, false));// 1371=
                if (!var3.getFile().startsWith(var4.getFile())) {// 1373
                    return null;// 1375
                } else {
                    if (var2) {// 1378
                        URLClassPath.check(var3);// 1379
                    }
                    // 1) 根据loader对应的文件目录,拼接资源路径参数构建File实例
                    final File var5;
                    if (var1.indexOf("..") != -1) {// 1382
                        var5 = (new File(this.dir, var1.replace('/', File.separatorChar))).getCanonicalFile();// 1383 1384
                        if (!var5.getPath().startsWith(this.dir.getPath())) {// 1385
                            return null;// 1387
                        }
                    } else {
                        var5 = new File(this.dir, var1.replace('/', File.separatorChar));// 1390
                    }
                    // 2) 若File实例映射的文件存在,则返回Resource实例,否则返回空
                    return var5.exists() ? new Resource() {// 1393
                        // ...
                    } : null;// 1407
                }
            } catch (Exception var6) {// 1404
                return null;// 1405
            }
        }
    }
    // 2. Jar加载器
    static class JarLoader extends Loader {
        Resource getResource(String var1, boolean var2) {
            if (this.metaIndex != null && !this.metaIndex.mayContain(var1)) {// 1059 1060
                return null;// 1061
            } else {
                try {
                    this.ensureOpen();// 1066
                } catch (IOException var5) {// 1067
                    throw new InternalError(var5);// 1068
                }
                // 1) 若loader对应Jar包中,存有资源名称的节点,则返回资源节点内容
                JarEntry var3 = this.jar.getJarEntry(var1);// 1070
                if (var3 != null) {// 1071
                    return this.checkResource(var1, var2, var3);// 1072
                } else if (this.index == null) {// 1074
                    return null;// 1075
                } else {
                    HashSet var4 = new HashSet();// 1077
                    return this.getResource(var1, var2, var4);// 1078
                }
            }
        }
    }
}

对于应用层的开发者而言,需要通过ClassLoader.gerResource(resourceName)进行调用。

public abstract class ClassLoader {
    // 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;
    }
}
public class URLClassLoader extends SecureClassLoader implements Closeable {
    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() {
                    // 2. 继续通过类加载器在父类中维护的URLClassPath获取资源
                    return ucp.findResource(name, true);
                }
            }, acc);
        return url != null ? ucp.checkURL(url) : null;
    }
}
public class URLClassPath {
    public URL findResource(String var1, boolean var2) {
        int[] var4 = this.getLookupCache(var1);// 224
        // 3. 本质还是迭代loader集合进行资源获取
        Loader var3;
        for(int var5 = 0; (var3 = this.getNextLoader(var4, var5)) != null; ++var5) {// 225
            URL var6 = var3.findResource(var1, var2);// 226
            if (var6 != null) {// 227
                return var6;// 228
            }
        }
        return null;// 231
    }
}

调用获取资源方法的一些常见问题

Class.getResource & ClassLoader.getResource 的差异

通过ClassLoader获取资源已经在上面说过了,主要在于通过Class对象调用getResource方法有什么特别之处?

public final class Class<T> implements java.io.Serializable,
    GenericDeclaration,
    Type,
    AnnotatedElement {
    // 本质依旧是调用ClassLoader.getResource,唯一的不同在于对于资源名称进行了一次特殊处理。
    // 做了哪些处理正如官方注释所描述:
    // 1. 如果是绝对路径(以 '/' 开头),剔除 '/' 符号后返回
    // 2. 如果非绝对路径,则拼接'.'号转为'/'后的包名作为前缀后返回
    // 其实理解下来就是:以当前类文件所在的包为基准,进行相对、绝对路径的拼接。绝对路径则不采用包名作为前缀,相对路径则以类所在的包名作为前缀。
    public java.net.URL getResource(String name) {
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl == null) {
            return ClassLoader.getSystemResource(name);
        }
        return cl.getResource(name);
    }
    /**
     * Add a package name prefix if the name is not absolute Remove leading "/"
     * if name is absolute
     */
    private String resolveName(String name) {
        if (name == null) {
            return name;
        }
        if (!name.startsWith("/")) {
            Class<?> c = this;
            while (c.isArray()) {
                c = c.getComponentType();
            }
            String baseName = c.getName();
            int index = baseName.lastIndexOf('.');
            if (index != -1) {
                name = baseName.substring(0, index).replace('.', '/')
                    +"/"+name;
            }
        } else {
            name = name.substring(1);
        }
        return name;
    }
}

加载不同文件目录 或 Jar包中含有的同名资源

比如classpath中包含的不同目录、Jar包下都有一个同名文件config.properties,那么到底该加载哪一个呢?

根据之前的源码分析,可以推断出目录或jar对应的loader,哪个最先执行,则返回该loader对应的目录或者jar下的config.properties。

如果我想要获取所有的同名文件呢?

Enumeration<URL> resources = classLoader.getResources("config.properties");
while (resources.hasMoreElements()) {
    URL url = resources.nextElement();
    // do something
}

到此这篇关于Java类加载器ClassLoader的使用详解的文章就介绍到这了,更多相关Java ClassLoader内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中如何自定义一个类加载器

    目录 如何自定义加载器? 示例:读取某文件的下的某class文件 类加载器的使用及自定义类加载器 如何自定义加载器? 1.创建一个自定义加载器类 继承 ClassLoader 类 2.重写 findClass 方法. 主要是实现从那个路径读取 jar包或者.class文件,将读取到的文件用字节数组来存储,然后可以使用父类的 defineClass 来转换成字节码. 如果想破坏双亲委派的话,就重写 loadClass 方法, 否则不用重写 注意: 1.ClassLoader提供的 protecte

  • java自定义类加载器如何实现类隔离

    目录 自定义类加载器 准备 通过URLClassLoader来实现[推荐] 通过继承ClassLoader实现 网上java自定义类加载器很多容易找到,但是都是加载的单个类,如果被加载的类,有引用了其他类怎么办呢?接下来看一下如何来处理这种情况 有时候一个项目中可能会引用不同版本的第三方依赖,比如笔者在升级hbase系统时,代理层就同时用到了1.X和2.X版本的hbase-client的jar包. 当时是使用的阿里的SOFAArk来实现的. 它的本质就是是哟个类加载来实现的,接下来就通过一个小例

  • Java类加载器与双亲委派机制和线程上下文类加载器专项解读分析

    目录 一.类加载器 1.启动类加载器 2.拓展类加载器 3.应用类加载器 4.类的命名空间 二.双亲委派机制 1.类加载机制流程 2.类加载器加载顺序 3.双亲委派机制流程 4.源码分析 5.双亲委派机制优缺点 三.线程上下文类加载器 1.线程上下文类加载器(Context Classloader) 2.ServiceLoader 四.自定义类加载器 一.类加载器 类加载器就是根据类的二进制名(binary name)读取java编译器编译好的字节码文件(.class文件),并且转化生成一个ja

  • Java类加载器ClassLoader源码层面分析讲解

    目录 Launcher 源码 AppClassLoader 源码 ExtClassLoader 源码 ClassLoader 源码 总结 最终总结一下 Launcher 源码 sun.misc.Launcher类是java 虚拟机的入口,在启动 java应用 的时候会首先创建Launcher.在初始化Launcher对象的时候会创建一个ExtClassLoader拓展程序加载器 和 AppClassLoader应用程序类加载器(这俩鬼东西好像只是加载类的路径不一样而已),然后由这俩类加载器去加载

  • Java中线程上下文类加载器超详细讲解使用

    目录 一.什么是线程上下文类加载器 1.1.重要性 1.2.使用场景 二.ServiceLoader简单介绍 三.案例 3.1.使用ServiceLoader加载mysql驱动 3.2.Class.forName加载Mysql驱动 3.2.1.com.mysql.jdbc.Driver 3.2.2.java.sql.DriverManager初始化 3.2.3.调用DriverManager的registerDriver方法 3.2.4.执行DriverManager.getConnection

  • Java通过自定义类加载器实现类隔离

    目录 前言 类隔离是什么 使用场景 解决方案 重写findClass 重写loadClass 总结 前言 由于微服务的快速迭代.持续集成等特性,越来越多的团队更倾向于它.但是也体现出了一些问题,比如在基础设施建设过程中,需要把通用功能下沉,把现有大而全的基础设施按领域拆分,考虑需要兼容现有生产服务,会产生不同的依赖版本,有时不注意就可以引发问题.比如本文遇到的依赖包版本冲突问题,以及如何利用类隔离技术解决的分析. 类隔离是什么 类隔离是一种通过类加载器实现加载所需类的实现方式,使得不同版本类间隔

  • Java类加载器ClassLoader的使用详解

    目录 BootstrapClassLoader ExtClassLoader AppClassLoader 类加载器的具体实现在哪里 类加载器的初始化时机 如何进行的类加载 Loader.getResource(resourceName) 调用获取资源方法的一些常见问题 BootstrapClassLoader 加载范围(根据系统参数): System.getProperty("sun.boot.class.path"); 负责加载核心类库,以我的本地的环境来展示获取的内容: D:\d

  • JVM类加载器之ClassLoader的使用详解

    目录 类加载器 概述 加载器的种类 验证不同加载器 核心方法 JVM类加载机制的三种方式 全盘负责 父类委托.双亲委派 缓存机制 打破双亲委派 重写loadclass方法 自定义类加载器 准备字节码文件 创建自定义类加载器 执行测试 注意事项 类加载器 概述 类加载器负责读取Java字节代码,并转换成java.lang.Class类的一个实例的代码模块. 类加载器除了用于加载类外,还可用于确定类在Java虚拟机中的唯一性. 任意一个类,都由加载它的类加载器和这个类本身一同确定其在 Java 虚拟

  • Java类加载器ClassLoader用法解析

    这篇文章主要介绍了Java类加载器ClassLoader用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 正文 当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.连接.初始化3个步骤来对该类进行初始化.如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化. 一.类加载过程 1.加载 加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说

  • classloader类加载器_基于java类的加载方式详解

    基础概念 Classloader 类加载器,用来加载 Java 类到 Java 虚拟机中.与普通程序不同的是.Java程序(class文件)并不是本地的可执行程序.当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader. JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,BootstrapClassLoader是用本地代码实现

  • 详解Java类加载器与双亲委派机制

    目录 引子 了解.class文件 类加载的过程 类加载器 与 双亲委派机制 ClassLoader 自定义类加载器 编写一个自定义的类加载器 为什么我们这边要打破双亲委派机制 自定义类加载器时,如何打破双亲委派机制 SPI机制 与 线程上下文类加载器 JDBC Tomcat SpringBoot Starter 尾语 引子 大家想必都有过平时开发springboot 项目的时候稍微改动一点代码,就得重启,就很烦 网上一般介绍 2种方式 spring-boot-devtools,或者通过JRebe

  • java中类加载与双亲委派机制详解

    目录 类加载是什么 类加载器 双亲委派机制 BootStrapClassLoader ExtClassLoader AppClassLoader 为什么使用双亲委派机制 全盘负责委托机制 自定义类加载器 打破双亲委派机制 类加载是什么 把磁盘中的java文件加载到内存中的过程叫做类加载 当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把主类加载到JVM. 有如下 User 类 package dc.dccmmtop; public Class User { publi

  • jvm之java类加载机制和类加载器(ClassLoader)的用法

    当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.连接.初始化3个步骤来对该类进行初始化.如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化. 一.类加载过程 1.加载 加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象. 类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础

  • Java SpringMVC拦截器与异常处理机制详解分析

    目录 拦截器(interceptor)的作用 拦截器快速入门 案例:用户登录权限控制 拦截器方法说明 SpringMVC异常处理 异常处理的思路 异常处理两种方式 拦截器(interceptor)的作用 Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. 将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain).在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用.拦截器也是AOP思

  • Java中JDBC的使用教程详解

    目录 概念 快速入门 步骤 代码实现 详解各个对象 DriverManager:驱动管理对象 Connection:数据库连接对象 Statement:执行sql的对象 ResultSet:结果集对象,封装查询结果 PreparedStatement:执行sql的对象 抽取JDBC工具类 : JDBCUtils 分析 代码实现 练习 JDBC控制事务 事务 操作 使用Connection对象来管理事务 代码 概念 Java DataBase Connectivity  Java 数据库连接, J

随机推荐