3、双亲委派模型详解

3、双亲委派模型详解

2023年6月26日发(作者:)

3、双亲委派模型详解3. 双亲委派模型  双亲委派模式要求除了顶层的启动类加载器外,其余的类加载器都应当有⾃⼰的⽗类加载器,请注意双亲委派模式中的⽗⼦关系并⾮通常所说的类继承关系,⽽是采⽤组合关系来复⽤⽗类加载器的相关代码,类加载器间的关系如下:image3.1 ⼯作流程  ⼯作原理的是,如果⼀个类加载器收到了类加载请求,它并不会⾃⼰先去加载,⽽是把这个请求委托给⽗类的加载器去执⾏,如果⽗类加载器还存在其⽗类加载器,则进⼀步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果⽗类加载器可以完成类加载任务,就成功返回,倘若⽗类加载器⽆法完成此加载任务,⼦加载器才会尝试⾃⼰去加载,这就是双亲委派模式image3.2 应该叫做“⽗委派模型”⽽不是“双亲委派模型”  “双亲委派”有很强的误导性,这是个翻译问题,实际上在oracle官⽅⽂档上是这样描述的:The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a “parent” class loader. When loading a class, a class loa  java平台通过委派模型去加载类。每个类加载器都有⼀个⽗加载器。当需要加载类时,会优先委派当前所在的类的加载器的⽗加载器去加载这个类。如果⽗加载器⽆法加载到这个类时,再尝试在当前所在的类的加载器中加载这个类。  所以,java的类加载机制应该叫做“⽗委派模型”,⽽不是“双亲委派机制”,“双亲委派机制”这个名字太具有误导性了。3.3 双亲委派优势采⽤双亲委派模式的是好处是Java类随着它的类加载器⼀起具备了⼀种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当⽗亲已经加载了该类时,就没有必要⼦ClassLoader再加载⼀次。其次是考虑到安全因素,java核⼼api中定义类型不会被随意替换,假设通过⽹络传递⼀个名为r的类,通过双亲委托模式传递到启动类加载器,⽽启动类加载器在核⼼Java API发现这个名字的类,发现该类已被加载,并不会重新加载⽹络传递的过来的r,⽽直接返回已加载过的,这样便可以防⽌核⼼API库被随意篡改。可能你会想,如果我们在classpath路径下⾃定义⼀个名为Interge类(该类是胡编的)呢?该类并不存在中,经过双亲委托模式,传递到启动类加载器中,由于⽗类加载器路径下并没有该类,所以不会加载,将反向委托给⼦类加载器加载,最终会通过系统类加载器加载该类。但是这样做是不允许,因为是核⼼API包,需要访问权限,强制加载将会报出如下异常tyException: Prohibited package name: 3.4 双亲委派什么时候被破坏通过预加载的⽅式;通过textClassLoader();3.4.1 预加载这⾥通过⼀个简单的例⼦,就拿sql连接来说:(1)Manager:包中的类,通过Bootstrap加载器加载。(2)DriverTest:开发⼈员⾃定义的实现了接⼝的类型,通过App加载器加载。  开发⼈员通过erDriver⽅法把⾃⼰实现的获取连接的Driver实现类加载并注册到DriverManager中。然后nection⽅法会定义⼀个 DriverTest 类,实现⾥⾯的接⼝public class DriverTest implements Driver { static { try { erDriver(new DriverTest()); n("who load DriverTest: " + ssLoader()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } @Override public Connection connect(String url, Properties info) throws SQLException { return new Connection() { //此处省略⼀堆代码...... } }

//启动代码 public static void main(String[] args) { try { //由AppClassLoader加载DriverTest类 e("Test"); n("who load DriverManager: "+ssLoader()); //通过中的DriverManager去获取链接,DriverManager由BootstrapClassLoader加载 Connection connection = nection("jdbc://"); } catch (Exception e) { tackTrace(); } }}此时运⾏main⽅法打印:who load DriverTest: er$AppClassLoader@18b4aac2who load DriverManager: nullProcess finished with exit code 0---------------------————————————————  DriverManager是由Bootstrap加载器的,因⽽获取不了Bootstrap加载器,所以为null。从⽗委派模型的机制上看,因为是由Bootstrap加载器加载的,所以⾥⾯  那么nection是怎么调⽤DriverTest(App加载器)的getConnection⽅法呢?因为⽗委派模型的限制,DriverManager不可能⾃⼰去加载DriverTest,DriverTest的加载实际上是由AppClassLoader完成的,DriverTest⾥⾯会往DriverManager中注册⼀个驱动。public class DriverTest implements { static { try { //在这⾥注册 erDriver(new DriverTest()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }}  对于DriverManager⽽⾔,他不关注driver的加载,他只需要遍历“registeredDrivers”,然后检查驱动类是否能被“调⽤类的类加载器”识别,如果可以识别,则调⽤t⽅法(即DriverTest中的实现) public class DriverManager{ private static Connection getConnection( String url, ties info, Class caller) throws SQLException { //省略⼀堆代码 for(DriverInfo aDriver : registeredDrivers) { //在这⾥做安全校验if(isDriverAllowed(, callerCL)) { try { println(" trying " + ss().getName()); //在这⾥调⽤DriverTest的connect⽅法 Connection con = t(url, info); if (con != null) { // Success! println("getConnection returning " + ss().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } //省略⼀堆代码

整体的流程是这样的image3.4.2 textClassLoader()1.通过调⽤textClassLoader()相当于直接拿到类加载器,⾃然不必通过向上递归⾛双亲委派了。2.举个例⼦:中的 yFinder 中的 newInstance⽅法:(1)在newInstance中会⽤到 getProviderClass ⽅法(2)在getProviderClass中会⽤到 textClassLoader⽅法(3)在textClassLoader中会⽤到tThread().getContextClassLoader()拿到线程上下⽂类加载器 /** * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. *

* 定义的JNDI接⼝ * @param type Base class / Service interface of the factory to instantiate * JNDI的实现类名 * @param className Name of the concrete class corresponding to the service provider * 加载器:如果为null,则通过线程的上下⽂加载器进⾏加载 * @param cl ClassLoader used to load the factory class. If null * current Thread's context classLoader is used to load the factory class. * 如果为true,则使⽤bootstrap加载器。 * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. */ static T newInstance(Class type, String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) throws FactoryConfigurationError { //省略⼀堆代码 try { //在这个⽅法⾥⾯,可以通过线程上下⽂加载器进⾏加载className对应的类 Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); //省略⼀堆代码 } } static private Class getProviderClass(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException { try { if (cl == null) { if (useBSClsLoader) { return e(className, false, ssLoader()); } else { //在这⾥,会获得线程的上下⽂加载器去加载类 //其中 ss是 cl = textClassLoader(); if (cl == null) { throw new ClassNotFoundException(); } else { return e(className, false, cl); } } } //省略⼀堆代码 }class SecuritySupport { ClassLoader getContextClassLoader() throws SecurityException{ return (ClassLoader) ileged(new PrivilegedAction() { public Object run() { ClassLoader cl = null; //try { //获得线程的上下⽂加载器 cl = tThread().getContextClassLoader(); //} catch (SecurityException ex) { } if (cl == null) cl = temClassLoader(); return cl; } }); }3.5 tomcat的类加载机制  不只是Driver驱动的实现是这样,在tomcat、spring等等的容器框架也是通过⼀些⼿段去绕过“⽗委派机制”。例如下图中的tomat类加载器的结构:image从图中的委派关系中可以看出:CommonClassLoader能加载的类都可以被Catalina - - - ClassLoader和SharedClassLoader使⽤,从⽽实现了公有类库的共⽤。CatalinaClassLoader和Shared ClassLoader⾃⼰能加载的类则与对⽅相互隔离。WebAppClassLoader可以使⽤SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。JasperLoader的加载范围仅仅是这个JSP⽂件所编译出来的那⼀个.Class⽂件,它出现的⽬的就是为了被丢弃:当Web容器检测到JSP⽂件被修改时,会替换掉⽬前的JasperLoader的实例,并通过再建⽴⼀个新的Jsp类加载器来实现JSP⽂件的HotSwap功能。tommccat 违背了⽗委派模型吗?tomcat 违背了⽗委派模型。因为双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当由⾃⼰的⽗类加载器加载。⽽tomcat 不是这样实现,tomcat 为了实现隔离性,没有遵守这个约定,每个webappClassLoader加载⾃⼰的⽬录下的class⽂件,不会传递给⽗类加载器。

发布者:admin,转转请注明出处:http://www.yc00.com/web/1687751995a39305.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信