zoukankan      html  css  js  c++  java
  • Java的动态加载及其安全性问题

    1.什么是动态加载

    Class Loaders是动态加载Java类与Resource的一种机制。它支持Laziness,type-safe linkage,user-defined extensibility和multiple communicating namespaces这4种特性。

      Lazy loading:Class只有在需要的时候才加载。这样减少了内存使用量,能提高系统反映速度;

      Type-safe linkage:动态类加载不会破坏JVM的类型安全;

      User-definable class loading policy:开发者可以自定义的类加载器,控制动态类加载过程;

      Multiple namespaces:JVM允许使用不同的类加载器加载相同的Class名称,但不同内容的类。

    Class Loaders早在JDK1.0时就已存在,最开始的目的是使HotJava浏览器能加载Applet。从那以后,动态类加载机制被广泛应用到其他方面,例如web application server中Servlets的加载。class loader在JDK 1.0,1.1版本存在的缺陷,已经在JDK 1.2解决,其缺陷主要是编写不正确的Class Loader会造成类型安全问题。

    2.Class Loader的工作原理

       Class Loader的目的是动态加载Java类和Resource。Java类是平台无关的,标准的,具有规范二进制文件格式的。class文件有编译器生成,可以被任何一中JVM加载。Java类的表现形式不仅只有.class文件,还可以为内存buffer,或是网络数据流。

      JVM执行class文件内的Byte code。但是Byte code不是class文件的全部内容,class文件内还包含符号表,表示类,属性和方法名,以及类内引用到其他类,属性,和方法名。例如下面的类

    class C{

    void f(){

       D d=new D();

    }

    }

    类文件内类C引用D。为了能让JVM知道D类是什么,JVM必须要先load D的class file并创建D class对象。

    JVM使用类加载器加载类文件,并创建Class对象。类加载器都是ClassLoader的子类实例。ClassLoader.loadClass方法通过获得一个类名,返回一个Class对象,表示该类的类型。上面的代码里,假设C被类加载器L加载,则L是C的加载器。JVM将使用L加载所有被C引用到的其他Java类。

    如果D还没有被加载,L将加载D:

    L.loadClass(“D”)

    当D已经被加载,JVM就可以创建D的一个对象实例。

    一个Java应用程序可以使用不同类型的类加载器。例如Web Application Server中,Servlet的加载使用开发商自定义的类加载器, java.lang.String在使用JVM系统加载器,Bootstrap Class Loader,开发商定义的其他类则由AppClassLoader加载。在JVM里由类名和类加载器区别不同的Java类型。因此,JVM允许我们使用不同的加载器加载相同namespace的java类,而实际上这些相同namespace的java类可以是完全不同的类。这种机制可以保证JDK自带的java.lang.String是唯一的。

    每个ClassLoader加载Class的过程是:
    1.检测此Class是否载入过(即在cache中是否有此Class),如果有到8,如果没有到2
    2.如果parent classloader不存在(没有parent,那parent一定是bootstrap classloader了),到4
    3.请求parent classloader载入,如果成功到8,不成功到5
    4.请求jvm从bootstrap classloader中载入,如果成功到8
    5.寻找Class文件(从与此classloader相关的类路径中寻找)。如果找不到则到7.
    6.从文件中载入Class,到8.
    7.抛出ClassNotFoundException.
    8.返回Class.

    3.JVM原生类加载器原理

    当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构:

    bootstrap classloader-> extension classloader-> system classloader

    bootstrap classloader - 引导(也称为原始)类加载器,它负责加载Java的核心类。在Sun的JVM中,在执行java的命令中使用-Xbootclasspath选项或使用-D选项指定sun.boot.class.path系统属性值可以指定附加的类。这个加载器的是非常特殊的,它实际上不是java.lang.ClassLoader的子类,而是由JVM自身实现的。大家可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库:
        URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (int i = 0; i < urls.length; i++) {
          System.out.println(urls.toExternalForm());
        }

    extension classloader - 扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的类包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的JAR类包对所有的JVM和system classloader都是可见的。

    在这个实例上调用方法getParent()总是返回空值null,因为引导加载器bootstrap classloader不是一个真正的ClassLoader实例。所以当大家执行以下代码时:
        System.out.println(System.getProperty("java.ext.dirs"));
        ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
        System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());

    extension classloader是system classloader的parent,而bootstrap classloader是extension classloader的parent,但它不是一个实际的classloader,所以为null。

    system classloader - 系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径。总能通过静态方法ClassLoader.getSystemClassLoader()找到该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。执行以下代码即可获得:
        System.out.println(System.getProperty("java.class.path"));
    输出结果则为用户在系统属性里面设置的CLASSPATH。

    4.安全性问题

    例如所加载jar文件,与父class loader存在同namespace 的class,但是java version不一致,这时候,就容易导致类安全性问题.

    可以通过class loader的加载顺序,进行jar加载,改变”父优先”的法则

    public ClassLoader getDSClassLoader(String moudleName) {
            if (DSClassLoader == null) {
                try {
                    DSClassLoader = new MCFClassLoader(
                            new URL[] {
                                    new URL("......xxx.jar"),
                                   new URL("......yyy.jar")},
                            ConnectorConfigurationParserServiceImpl.class
                                    .getClassLoader());
                } catch (MalformedURLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return DSClassLoader;
        }
    public ClassLoader getDSClassLoader(String moudleName) {
            if (DSClassLoader == null) {
                try {
                    DSClassLoader = new MCFClassLoader(
                            new URL[] {
                                    new URL("......xxx.jar"),
                                   new URL("......yyy.jar")},
                            ConnectorConfigurationParserServiceImpl.class
                                    .getClassLoader());
                } catch (MalformedURLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return DSClassLoader;
        }
  • 相关阅读:
    UML
    Jenkins(Jenkins的安装配置)
    SpringCloud:Config配置中心
    IDEA将项目上传到GitHub
    JS—高阶函数
    项目中git操作
    互联网概述
    ESLint的extends
    vue3 + TS + eslint 实现代码格式化以及代码规范提示
    vue3基础使用
  • 原文地址:https://www.cnblogs.com/zhulongchao/p/4564694.html
Copyright © 2011-2022 走看看