java是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是:

  • 类加载体系

  • .class文件检验器

  • 内置于Java虚拟机(及语言)的安全特性

  • 安全管理器及Java API

本篇博客主要介绍“类加载体系”的基本原理;如需了解其它几类安全机制可以通过上面的博客链接进入查看。

简介

“类加载体系”及ClassLoader双亲委派机制。java程序中的 .java文件编译完会生成 .class文件,而 .class文件就是通过被称为类加载器的ClassLoader加载的,而ClassLoder在加载过程中会使用“双亲委派机制”来加载 .class文件,先上图:

看着图从上往下介绍:

  1. BootStrapClassLoader:启动类加载器,该ClassLoader是jvm在启动时创建的,用于加载 $JAVA_HOME/jre/lib下面的类库(或者通过参数-Xbootclasspath指定)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不能直接通过引用进行操作。

  2. ExtClassLoader:扩展类加载器,该ClassLoader是在sun.misc.Launcher里作为一个内部类ExtClassLoader定义的(即 sun.misc.Launcher$ExtClassLoader),ExtClassLoader会加载 $JAVA_HOME/jre/lib/ext下的类库(或者通过参数-Djava.ext.dirs指定)。

  3. AppClassLoader:应用程序类加载器,该ClassLoader同样是在sun.misc.Launcher里作为一个内部类AppClassLoader定义的(即 sun.misc.Launcher$AppClassLoader),AppClassLoader会加载java环境变量CLASSPATH所指定的路径下的类库,而CLASSPATH所指定的路径可以通过System.getProperty("java.class.path")获取;当然,该变量也可以覆盖,可以使用参数-cp,例如:java -cp 路径 (可以指定要执行的class目录)。

  4. CustomClassLoader:自定义类加载器,该ClassLoader是指我们自定义的ClassLoader,比如tomcat的StandardClassLoader属于这一类;当然,大部分情况下使用AppClassLoader就足够了。

ClassLoader初始化源码

下面贴下jdk关于类加载的源码,上述四种类加载器中CustomClassLoader是用户自定义的,BootStrapClassLoader是jvm创建的,就不展示了;这里展示下AppClassLoader和ExtClassLoader的启动过程,前面介绍过,AppClassLoader和ExtClassLoader都是在sun.misc.Launcher里定义的,而我的sun.misc.Launcher没有源码,大家将就看看反编译的代码吧。如果想看sun.*包下的类源码,大家可以下载openjdk来查看。

public Launcher(){ExtClassLoader extclassloader;try{extclassloader = ExtClassLoader.getExtClassLoader();}catch(IOException ioexception) {throw new InternalError("Could not create extension class loader");}try{loader = AppClassLoader.getAppClassLoader(extclassloader);}catch(IOException ioexception1){throw new InternalError("Could not create application class loader");}Thread.currentThread().setContextClassLoader(loader);String s = System.getProperty("java.security.manager");if(s != null){SecurityManager securitymanager = null;if("".equals(s) || "default".equals(s))securitymanager = new SecurityManager();elsetry{securitymanager = (SecurityManager)loader.loadClass(s).newInstance();}catch(IllegalAccessException illegalaccessexception) { }catch(InstantiationException instantiationexception) { }catch(ClassNotFoundException classnotfoundexception) { }catch(ClassCastException classcastexception) { }if(securitymanager != null)System.setSecurityManager(securitymanager);elsethrow new InternalError((new StringBuilder()).append("Could not create SecurityManager: ").append(s).toString());}}

可以看到在Launcher构造函数的执行过程如下:

  1. 通过ExtClassLoader.getExtClassLoader()创建了ExtClassLoader;

  2. 通过AppClassLoader.getAppClassLoader(ExtClassLoader)创建了AppClassLoader,并将ExtClassLoader设为AppClassLoader的parent ClassLoader;

  3. 通过Thread.currentThread().setContextClassLoader(loader)把AppClassLoader设为线程的上下文 ClassLoader;

  4. 根据jvm参数-Djava.security.manager创建安全管理器(安全管理器的相关内容会在后续博客安全管理器及Java API中介绍),此时jvm会设置系统属性"java.security.manager"为空字符串""。

再贴下ExtClassLoader源码:

public static ExtClassLoader getExtClassLoader()throws IOException{File afile[] = getExtDirs();return (ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction(afile) {public Object run() throws IOException{int i = dirs.length;for(int j = 0; j < i; j++)MetaIndex.registerDirectory(dirs[j]);return new ExtClassLoader(dirs);}final File val$dirs[];{dirs = afile;super();}});PrivilegedActionException privilegedactionexception;privilegedactionexception;throw (IOException)privilegedactionexception.getException();}private static File[] getExtDirs(){String s = System.getProperty("java.ext.dirs");File afile[];if(s != null){StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);int i = stringtokenizer.countTokens();afile = new File[i];for(int j = 0; j < i; j++)afile[j] = new File(stringtokenizer.nextToken());} else{afile = new File[0];}return afile;}

反编译的源码,大家将就看下;这里大家关注下getExtDirs()这个方法,它会获取属性"java.ext.dirs"所对应的值,然后通过系统分隔符分割,然后加载分割后的字符串对应的目录作为ClassLoader的类加载库。

下面看看AppClassLoader源码:

public static ClassLoader getAppClassLoader(ClassLoader classloader) throws IOException{String s = System.getProperty("java.class.path");File afile[] = s != null ? Launcher.getClassPath(s) : new File[0];return (AppClassLoader)AccessController.doPrivileged(new PrivilegedAction(s, afile, classloader) {public Object run() {URL aurl[] = s != null ? Launcher.pathToURLs(path) : new URL[0];return new AppClassLoader(aurl, extcl);}final String val$s;final File val$path[];final ClassLoader val$extcl;{s = s1;path = afile;extcl = classloader;super();}});}

首先获取"java.class.path"对应的属性,并转换为URL[]并设置为ClassLoader的类加载库,注意这里的方法入参classloader就是ExtClassLoader,在创AppClassLoader会传入ExtClassLoader作为parent ClassLoader。

上面就是ClassLoader的启动和初始化过程,后面会把loader作为应用程序的默认ClassLoader使用,看下面的测试用例:

package com.jvm.study.classload;public class Test {public static void main(String... args) {ClassLoader loader = Test.class.getClassLoader();System.err.println("loader:"+loader);while (loader != null) {loader = loader.getParent();System.err.println("in while:"+loader);}}
}

可以看到ClassLoader的层次结构,输出结果为:

ClassLoader双亲委派机制源码

前面谈到了ClassLoader的几类加载器,而ClassLoader使用双亲委派机制来加载class文件的。

ClassLoader的双亲委派机制是这样的(这里先忽略掉自定义类加载器CustomClassLoader):

  1. 当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

  2. 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。

  3. 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;

  4. 若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。

下面贴下ClassLoader的loadClass(String name, boolean resolve)源码:

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) {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.c = findClass(name);}}if (resolve) {resolveClass(c);}return c;}

代码很明朗:首先找缓存(findLoadedClass),没有的话就判断有没有parent,有的话就用parent来递归的loadClass,然而ExtClassLoader并没有设置parent,则会通过findBootstrapClassOrNull来加载class,而findBootstrapClassOrNull则会通过JNI方法”private native Class findBootstrapClass(String name)“来使用BootStrapClassLoader来加载class。

然后如果parent未找到class,则会调用findClass来加载class,findClass是一个protected的空方法,可以覆盖它以便自定义class加载过程。

另外,虽然ClassLoader加载类是使用loadClass方法,但是鼓励用 ClassLoader 的子类重写 findClass(String),而不是重写loadClass,这样就不会覆盖了类加载默认的双亲委派机制。

双亲委派机制为什么安全

前面谈到双亲委派机制是为了安全而设计的,但是为什么就安全了呢?举个例子,ClassLoader加载的class文件来源很多,比如编译器编译生成的class、或者网络下载的字节码。而一些来源的class文件是不可靠的,比如我可以自定义一个java.lang.Integer类来覆盖jdk中默认的Integer类,例如下面这样:

package java.lang;/*** hack*/
public class Integer {public Integer(int value) {System.exit(0);}
}

初始化这个Integer的构造器是会退出JVM,破坏应用程序的正常进行,如果使用双亲委派机制的话该Integer类永远不会被调用,以为委托BootStrapClassLoader加载后会加载JDK中的Integer类而不会加载自定义的这个,可以看下下面这测试个用例:

        public static void main(String... args) {Integer i = new Integer(1);System.err.println(i);}

执行时JVM并未在new Integer(1)时退出,说明未使用自定义的Integer,于是就保证了安全性。

转载于:https://www.cnblogs.com/duanxz/p/6108343.html

java安全沙箱(一)之ClassLoader双亲委派机制相关推荐

  1. java类加载-ClassLoader双亲委派机制

    "类加载体系"及ClassLoader双亲委派机制.java程序中的 .java文件编译完会生成 .class文件,而 .class文件就是通过被称为类加载器的ClassLoade ...

  2. 违反ClassLoader双亲委派机制三部曲第二部——Tomcat类加载机制

    转载自 违反ClassLoader双亲委派机制三部曲第二部--Tomcat类加载机制 前言: 本文是基于 ClassLoader双亲委派机制源码分析 了解过正统JDK类加载机制及其实现原理的基础上,进 ...

  3. 双亲委派机制_史上三次破坏ClassLoader双亲委派机制

    人既会喜逢令人羡慕的幸运,也会遭遇始料不及的失败与磨难.艰难时,不丧失美好的希望:而在成功时,不忘记感恩之心.谦虚之心:人只要有这份心态,任何时候都可以重振旗鼓. 人的一生始于心,终于心. 本篇是继上 ...

  4. ClassLoader 双亲委派机制

    看了挺多双亲委派机制的博客,但是看完就容易忘掉,所以自己调试一下类加载的过程,并记录调试过程和结果. 1.定位类加载的方法,并打断点记录观察加载类SampleTomcatApplication.cla ...

  5. 【04-JVM面试专题-什么是双亲委派机制(父类委托机制)?如何打破双亲委派机制?双亲委派机制的优缺点?什么是沙箱安全机制呢?】

    什么是双亲委派机制?如何打破双亲委派机制? JVM的双亲委派机制知道吗?怎么打破它呢?你看看自己掌握的怎么样呢? 什么是双亲委派机制?(父类委托机制) 检查某个类是否已经加载 自底向上,从Custom ...

  6. Java的双亲委派机制

    Java的双亲委派机制是java中类加载过程采用的机制,所以首先要理解java的类加载过程. 类加载过程:程序经过javac.exe命令以后,会生成一个或多个class字节码文件,接着使用java.e ...

  7. JVM 虚拟机类加载过程和双亲委派机制

    Java 编译后的字节码 Class 文件加载到虚拟机后才能运行和使用. 一.类加载过程 包括三个步骤, Loading 加载,Linking 链接,Initializing 初始化:第二步又可以细分 ...

  8. 如何打破双亲委派机制

    双亲委派机制 第一次知道何为打破双亲委派机制是通过阅读周志明的<深入理解Java虚拟机>,我们知道双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器 ...

  9. JVM(1)之JVM的组成详解(字符串常量池+双亲委派机制+JIT即时编译......)

    以下总结自:<深入理解java虚拟机> + 宋红康老师视频 字节码文件介绍:深入理解JVM之Java字节码(.class)文件详解_Windy_729的博客-CSDN博客_字节码文件 JV ...

最新文章

  1. html怎么设置取当前日期格式,js获取当前日期,格式为YYYY-MM-DD
  2. github中SSH的Key
  3. solrCloud源码分析之CloudSolrClient
  4. ES分组聚合:计算每个tag下的商品数量且某个filed包含指定关键字,分组,平均,每个tags下的平均价格,排序,指定范围区间
  5. c语言指针大全,C语言指针
  6. 每天一道LeetCode-----一个整数序列,每个元素出现两次,只有一个(两个)出现一次,找到这个(这两个)元素
  7. 太神奇了!使用C#实现自动核验健康码:(2)OCR识别
  8. 基本查询(Query查询中文)
  9. shell获取执行脚本路径
  10. 时间转化_2个关键因素决定福鼎白茶的后期转化
  11. 前端开发下划线怎么设置_怎么使用Word快速制作填空题下划线? 只须一个快捷键, 教师必备...
  12. 《推荐系统笔记(一)》线性回归方法的数学推导
  13. struts2 中 constant标签
  14. 怎样用springboot开发cs_springboot 系列 springboot 初探
  15. 2021-08-25剑指 Offer 13. 机器人的运动范围
  16. 数据库左连接、右连接
  17. 闭包及作用域销毁练习
  18. python基础刻意练习--Task 6 字典与集合
  19. DeepCTR-Torch 如何使用【案例(Criteo、Movielens)演示、特征(SparseFeat、DenseFeat、VarLenSparseFeat)参数含义】
  20. 银行从业如何备考,有什么刷题的APP?

热门文章

  1. 记一次阿里云的面试,止步第三轮,可惜!
  2. 看漫画就能学SQL,简直太cool了
  3. 2019年我建议你做好三件事情
  4. 弹幕,你知道是怎样练成的?
  5. 码农口述:AI创业两年,积蓄花光,重回职场敲代码
  6. redis: string类型设置过期时间后 再进行set操作,会清除过期时间
  7. 4.HTTPS与HTTP的区别
  8. 【Linux - mysql】怎么修改数据库编码
  9. Android --- adapter.notifyDataSetChanged() 不起作用怎么办
  10. android 获取权限管理,Android常用权限获取和设置