• 类加载器


    类加载器

    • 启动类加载器(Bootstrap ClassLoader):此加载器采用C++编写,一般开发中是看不到的。 负责将存放在lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机中。

    • 扩展类加载器
      (Extendsion ClassLoader):用来进行扩展类的加载,一般对应的是jrelibext目录中的类;

    • 应用程序类加载器
      (AppClassLoader):(默认)加载classpath指定的类,是最常使用的是一种加载器。

    双亲委派模型

    除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。

    工作过程:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类的加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

    image

    这种加载模式保证了由上到下的安全性,首先加载类库的类和扩展类,然后才是开发人员写的类。如果你想自己写个String类,都没机会加载的,这样就避免了一些风险。

    双亲委派模型的实现:

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

    破坏双亲委派模型

    1.向前兼容带来的破坏

    为了向前兼容,JDK1.2之后的Java.lang.ClassLoader添加了一个新的protected方法findClass()。在此之前,用户去继承java.lang.ClassLoader的唯一目的就是为了重写loadClass()方法。

    2.自身缺陷导致被破坏

    如果基础类又要调用回用户的代码,该怎么办?

    如JNDI服务,它的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者(SPI)的代码,但是启动类加载器不可能认识这些代码啊。所以Java设计团队引入了线程上下文类加载器。

    Thread.currentThread().setContextClassLoader(currentClassLoader);

    如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。

    JNDI服务使用这个线程上下文类加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作。

    3.动态性带来的破坏

    代码热替换、模块热部署。就像计算机外设一样,接上鼠标、U盘,不用重启机器就能立即使用,需要升级就换个,不用停机也不用重启。

    Java模块化标准–OSGi
    OSGi实现模块化部署的关键则是它自定义的类加载器机制的实现。每一个程序模块(Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起换掉以实现代码的热替换。

    Tomcat类加载器

    在Tomcat目录结构中,有3组目录(”/common/“、”/server/“和”/shared/*”)可以存放
    Java类库,另外还可以加上Web应用程序自身的目录“/WEB-INF/*”,一共4组,把Java类库
    放置在这些目录中的含义分别如下:

    • 放置在/common目录中:类库可被Tomcat和所有的Web应用程序共同使用。
    • 放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见。
    • 放置在/shared目录中:类库可被所有的Web应用程序共同使用,但对Tomcat自己不可
      见。
    • 放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见。

    JSP类加载器 -> WebApp类加载器 -> Shared类加载器 -> Common类加载器

    JasperLoader的加载范围仅仅是这个JSP文件所
    编译出来的那一个Class,它出现的目的就是为了被丢弃:当服务器检测到JSP文件被修改
    时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。

    思考几个问题:
    - web容器是如何加载类的:如Tomcat,Jetty,Resin….
    - JDBC和JNDI连接数据库的区别
    - Class.forName() 和 ClassLoader.loadClass()的区别

    ================================== 赵客缦胡缨,吴钩霜雪明。 银鞍照白马,飒沓如流星。 ==================================
  • 相关阅读:
    poj 2996 模拟
    poj 2965 BFS
    poj 1068 模拟
    poj 3295 前缀表达式求值
    常用的十七大学术搜索引擎
    Why,Unix or Linux ?
    匈牙利命名法
    微调控件(CSpinButtonCtrl)
    美国免费邮箱
    ASP常用的代码
  • 原文地址:https://www.cnblogs.com/lucare/p/9312654.html
Copyright © 2020-2023  润新知