一个打破通常理解的问题

最近,看到了一个问题,如下描述:
使用Tomcat的情况下,如果有10个web应用程序都是采用Spring来进行组织和管理的话,可以将Spring放在Common或者Share目录下这些程序共享。Spring要对程序类进行管理,自然要访问到用户的程序的类。而用户的程序放在了/WebApp/WEB-INF目录中。那么被Common类加载器或者Share类加载器加载的Spring如何访问并不在其加载范围内的用户程序呢?(PS:来自深入理解Java虚拟机)大家都知道,Java程序任何的事物皆为对象,类也是一个对象,以往的理解是,在同一个程序中,类对象的实例一定是一样的,是相等的。但是,这句话仅仅是在同一个类加载器的前提下,也即是说,如果不同类加载器加载的类所生成的对象是无法比较的,因为并不在一个类加载的范围内。刚才描述的问题是就相当于这样的情况,如果我们想使用其他类加载器的对象,这个时候,应该怎么办,如果操作其他类加载器加载的类对象?解释这个问题,我们需要了解双亲委派模型。在这个模型中,含有启动类加载器、扩展类加载器、应用程序类加载器以及用户自定义类加载器。然而,如果我们想在一个Java服务上部署不同的应用,且应用之间相互隔离的,这个时候,就要破坏双亲委派模型,典型的代表是Tomcat的类加载模型以及OSGi,这些都是可以破坏双亲委派的。在Tomcat中,不同的类加载器他们都会有不同的作用,那Spring是如何帮助我们解决使用其超过范围的类呢?在Spring的源码中,在

org.springframework.web.context.ContextLoader#initWebApplicationContext 这个方法中,有这样的一句话:

一个打破通常理解的问题
一个打破通常理解的问题

也就是说Spring的解决方法是采用线程上下文类加载器,下面解释一下线程上下文类加载器。Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。

这些 SPI 的接口由 Java 核心库来提供,而这些 SPI 的实现代码则是作为 Java 应用所依赖的 jar 包被包含进类路径(CLASSPATH)里。SPI接口中的代码经常需要加载具体的实现类。那么问题来了,SPI的接口是Java核心库的一部分,是由启动类加载器来加载的;SPI的实现类是由系统类加载器来加载的。启动类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能委派给系统类加载器,因为它是系统类加载器的祖先类加载器。线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。

而线程上下文类加载器破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。

线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。

所以,回到之前的问题,如何使用管理不在自己管理范围的类,采用的是线程上下文类加载器,这样的话,可以打破传统的类加载机制,让程序可以进行逆向使用类加载器。

来源:两个菜鸟程序猿,本文观点不代表自营销立场,网址:https://www.zyxiao.com/p/136548

发表评论

登录后才能评论
服务中心
服务中心
联系客服
联系客服
侵权联系 投诉举报
返回顶部
河南,挺住!郑州,挺住!一起为他们加油!!