首页 > 编程知识 正文

什么情况违反双亲委派机制,双亲委派机制优缺点

时间:2023-05-03 22:59:20 阅读:15544 作者:4447

“类加载体系”和ClassLoader父母委托机制。 编译java程序的. java文件时,将生成. class文件。 class文件由称为类加载器的ClassLoader加载,ClassLoader在加载过程中使用“父母委托机制”加载. class文件。 先看看图吧。

一边看图一边从上往下介绍:

BootStrapClassLoader :启动时启动jvm创建的类加载器。 加载$JAVA_HOME/jre/lib下的类库或使用-Xbootclasspath参数指定。 由于引导类加载器包含虚拟机的本地实现详细信息,因此开发人员无法直接从引用中操作,因为无法直接获取对引导类加载器的引用。

在extclassloader:sun.misc.launcher中定义为内部类ext class loader的扩展类加载器。 (即sun.misc.launcher $ ext class loader ) )。

AppClassLoader :在同一sun.misc.Launcher中定义为内部类AppClassLoader的APP应用程序类加载器(AppClassLoader位于java环境变量CLASSPATH指定的路径下) 在CLASSPATH中指定的路径可以从system.getproperty ' Java.class.path '获取。 当然,该变量也可以复盖,并且可以使用参数-cp,例如java -cp路径(可以指定要执行的class目录)。

自定义类加载器:自定义类加载器。 这个ClassLoader是指我们定制的ClassLoader。 例如,tomcat的标准类加载器就是这样。 当然,大多数情况下,app类加载器就足够了。

类加载器初始化源代码

在下面粘贴有关类加载的jdk源代码。 在上述四种加载器中,CustomClassLoader是用户自定义的,BootStrapClassLoader是由jvm创建的,因此未列出。 本节介绍了启动app类加载器和ext类加载器的过程。 如上所述,AppClassLoader和ExtClassLoader在sun.misc.Launcher中定义,但我的sun.misc.launcheer如果想查看sun.*包的源代码,请单击

1公共开发人员((2extclassloaderextclassloader; 3 try {4} ext class loader=ext class loader.getext class loader (5)6catch (ioexceptionioexception )7thrownewinternalerror 8 }9try { 10 loader=app class loader.getappclassloader (ext class loader ); 11 ) 12 catch (ioexceptionioexception1) 13 throw new internal error (' couldnotcreateapplicationclassloader ' ); 14 ) 15thread.currentthread ().setcontextclassloader ); 16 strings=system.getproperty (Java.security.manager ); 17if(s!=null (18 securitymanagersecuritymanager=null; 19if ('.equals (|' default '.equals ) ) 20安全管理器=newsecuritymanager ); 21电子邮件

22 try { 23安全管理器=(安全管理器) loader.loadclass(s ).newInstance ); 25厘米(illegalaccessexceptionillegalaccessexception ) ({ } 26厘米(instatiationexceptioninstantiationexception ) } ) 27 catch (classnotfoundexceptionclassnotfoundexception (28 catch ) classcastexceptionclasscastexception ) 29if (安全选项)

(securitymanager);31 else

32    throw new InternalError((new StringBuilder()).append("Could not create SecurityManager: ").append(s).toString());33 }34 }

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

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

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

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

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

再贴下ExtClassLoader源码:

1 static class ExtClassLoader extendsURLClassLoader {2 privateFile[] dirs;3

4 public static ExtClassLoader getExtClassLoader() throwsIOException {5 //用调getExtDirs()方法取获配置的扩展类路径

6 final File[] dirs =getExtDirs();7 try{8 //应用getExtDirs()方法返回的路径生成一个新的ClassLoader实例

9 return (ExtClassLoader) AccessController.doPrivileged(newPrivilegedExceptionAction() {10 public Object run() throwsIOException {11 int len =dirs.length;12 for (int i = 0; i < len; i++) {13 MetaIndex.registerDirectory(dirs[i]);14 }15 return newExtClassLoader(dirs);16 }17 });18 } catch(java.security.PrivilegedActionException e) {19 throw(IOException) e.getException();20 }21 }22

23

24 //再看这个方法

25 private staticFile[] getExtDirs() {26 //取获配置的扩展类路径

27 String s = System.getProperty("java.ext.dirs");28 File[] dirs;29 if (s != null) {30 StringTokenizer st = newStringTokenizer(s, File.pathSeparator);31 int count =st.countTokens();32 dirs = newFile[count];33 for (int i = 0; i < count; i++) {34 dirs[i] = newFile(st.nextToken());35 }36 } else{37 dirs = new File[0];38 }39 returndirs;40 }41

42 //其他码代略

43 ...44 }

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

下面看看AppClassLoader源码:

1 public static ClassLoader getAppClassLoader(ClassLoader classloader) throwsIOException{2 String s = System.getProperty("java.class.path");3 File afile[] = s != null ? Launcher.getClassPath(s) : new File[0];4 return (AppClassLoader)AccessController.doPrivileged(newPrivilegedAction(s, afile, classloader) {5 publicObject run() {6 URL aurl[] = s != null ? Launcher.pathToURLs(path) : new URL[0];7 return newAppClassLoader(aurl, extcl);8 }9

10 finalString val$s;11 finalFile val$path[];12 finalClassLoader val$extcl;13

14 {15 s =s1;16 path =afile;17 extcl =classloader;18 super();19 }20 });21 }

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

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

1 public static voidmain(String... args) {2 ClassLoader loader = Test.class.getClassLoader();3 System.err.println(loader);4 while (loader != null) {5 loader =loader.getParent();6 System.err.println(loader);7 }8 }

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

ClassLoader双亲委派机制源码

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

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

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

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

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

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

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

1 protected synchronized Class> loadClass(String name, boolean resolve) throwsClassNotFoundException {2 //First, check if the class has already been loaded

3 Class c =findLoadedClass(name);4 if (c == null) {5 try{6 if (parent != null) {7 c = parent.loadClass(name, false);8 } else{9 c =findBootstrapClassOrNull(name);10 }11 } catch(ClassNotFoundException e) {12 //ClassNotFoundException thrown if class not found13 //from the non-null parent class loader

14 }15 if (c == null) {16 //If still not found, then invoke findClass in order17 //to find the class.

18 c =findClass(name);19 }20 }21 if(resolve) {22 resolveClass(c);23 }24 returnc;25 }

代码很明朗:首先找缓存(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类,例如下面这样:

1 packagejava.lang;2

3 /**

4 * hack5 */

6 public classInteger {7 public Integer(intvalue) {8 System.exit(0);9 }10 }

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

1 public static voidmain(String... args) {2 Integer i = new Integer(1);3 System.err.println(i);4 }

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

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。