简单了解Tomcat与OSGi的类加载器架构
前言:
本次博客主要是对Tomcat与OSGi的类加载器架构,所以就需要对tomcat、OSGi以及类加载机制有所了解
类加载可以在http://www.cnblogs.com/ghoster/p/7594224.html中简单了解
一、Tomcat:正统的类加载架构
1.主流的Java Web服务器,如Tomcat、Jetty、WebLogic、WebSphere等都实现了自己定义的类加载器(一般都不止一个)。因为一个功能健全的web服务器要解决一下几个问题:
1)部署在一个服务器上的两个web应用程序所使用的Java类库可以实现互相隔离。这是最基本的需求,两个不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类库可以互相独立使用
2)部署在同一个服务器上的两个Web应用程序所使用的Java类库可以互相共享。这个需求也非常很常见,如,用户可能有10各使用Spring组织的应用程序部署在同一台服务器上,如果把10份Spring分别存放在各个应用程序的隔离目录中,将会是很大的资源浪费-这主要不是浪费磁盘空间的问题,而是指类库在使用时都要被加载到服务器内存,如果类库不能共享,虚拟机的方法区就会很容易出现过度膨胀的风险
3)服务器需要尽可能地保证自身的安全不受部署的Web应用程序影响。目前,有许多主流的Java Web服务器自身也是使用Java语言来实现的。因此,服务器本身也有类库依赖的问题,一般来说,基于安全考虑,服务器所使用的类库应该与应用程序的类库相互独立
4)支持jsp应用的Web服务器,大多数都需要支持HotSwap功能。我们知道,jsp文件最终要编译成Java Class才能由虚拟机执行,但jsp文件由于其纯文本存储的特性,运行时修改的概率远远大于第三方类库或程序自身的Class文件。而且ASP、PHP和JSP这些网页应用也把修改后无需重启作为一个很大的“优势”来看待,因此,“主流”的Web服务器都会支持JSP的热替换,当然也有“非主流”的,如运行在生产模式下的WebLogic服务器默认就不会处理JSP文件的变化
由于存在上述问题,在部署Web应用时,单独的一个ClassPath就无法满足需求了,所以各种Web服务器都“不约而同”地提供了好几个ClassPath路径供用户存放第三方类库,这些路径一般都以lib或classes命名,被放置到不同路径中的类库,具备不同的访问范围和服务对象,通常,每一个目录都会有一个相应的自定义类加载器去加载放置在里面的Java类库。
2.Tomcat对用户类库与类加载器的规划
在其目录结构下有三组目录(“/common/*”、“/server/*”、“/shared/*”)可以存放Java类库,另外还可以加上Web应用程序本身的目录“/WEB-INF/*”,一共4组,把Java类库放置在这些目录中的含义分别如下:
1)放置在/commom目录中:类库可被Tomcat和所有的Web应用程序共同使用
2)放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见
3)放置在/shared目录中:类库可被所有的Web应用程序所共同使用,但对Tomcat自己不可见
4)放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见
为了支持这套目录结构,并对目录里面的类库进行加载和隔离,Tomcat自定义了多个类加载器,这些类加载器按照经典的双亲委派模型来实现,所下图:
最上面的三个类加载器是JDK默认提供的类加载器,这三个加载器的的作用之前也说过,这里不再赘述了,可以在http://www.cnblogs.com/ghoster/p/7594224.html简单了解。而CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebAppClassLoader则是Tomcat自己定义的类加载器,他们分别加载/common/*、/server/*、/shared/*和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个jsp文件对应一个Jsp类加载器
从上图的委派关系可以看出,CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则与对方相互隔离。WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的哪一个Class,它出现的目的就是为了被丢弃:当服务器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过在建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能
二、OSGi:灵活的类加载器架构
既然说到OSGI,就要来解释一下OSGi是什么,以及它的作用
OSGi(Open Service Gateway Initiative):是OSGi联盟指定的一个基于Java语言的动态模块化规范,这个规范最初是由Sun、IBM、爱立信等公司联合发起,目的是使服务提供商通过住宅网管为各种家用智能设备提供各种服务,后来这个规范在Java的其他技术领域也有不错的发展,现在已经成为Java世界中的“事实上”的模块化标准,并且已经有了Equinox、Felix等成熟的实现。OSGi在Java程序员中最著名的应用案例就是Eclipse IDE
OSGi中的每一个模块(称为Bundle)与普通的Java类库区别并不大,两者一般都以JAR格式进行封装,并且内部存储的都是Java Package和Class。但是一个Bundle可以声明它所依赖的Java Package(通过Import-Package描述),也可以声明他允许导出发布的Java Package(通过Export-Package描述)。在OSGi里面,Bundle之间的依赖关系从传统的上层模块依赖底层模块转变为平级模块之间的依赖(至少外观上如此),而且类库的可见性能得到精确的控制,一个模块里只有被Export过的Package才可能由外界访问,其他的Package和Class将会隐藏起来。除了更精确的模块划分和可见性控制外,引入OSGi的另外一个重要理由是,基于OSGi的程序很可能可以实现模块级的热插拔功能,当程序升级更新或调试除错时,可以只停用、重新安装然后启动程序的其中一部分,这对企业级程序开发来说是一个非常有诱惑性的特性
OSGi之所以能有上述“诱人”的特点,要归功于它灵活的类加载器架构。OSGi的Bundle类加载器之间只有规则,没有固定的委派关系。例如,某个Bundle声明了一个它依赖的Package,如果有其他的Bundle声明发布了这个Package,那么所有对这个Package的类加载动作都会为派给发布他的Bundle类加载器去完成。不涉及某个具体的Package时,各个Bundle加载器是平级关系,只有具体使用某个Package和Class的时候,才会根据Package导入导出定义来构造Bundle间的委派和依赖
另外,一个Bundle类加载器为其他Bundle提供服务时,会根据Export-Package列表严格控制访问范围。如果一个类存在于Bundle的类库中但是没有被Export,那么这个Bundle的类加载器能找到这个类,但不会提供给其他Bundle使用,而且OSGi平台也不会把其他Bundle的类加载请求分配给这个Bundle来处理
一个例子:假设存在BundleA、BundleB、BundleC三个模块,并且这三个Bundle定义的依赖关系如下:
BundleA:声明发布了packageA,依赖了java.*的包
BundleB:声明依赖了packageA和packageC,同时也依赖了Java.*的包
BundleC:声明发布了packageC,依赖了packageA
那么,这三个Bundle之间的类加载器及父类加载器之间的关系如下图:
由于没有涉及到具体的OSGi实现,所以上图中的类加载器没有指明具体的加载器实现,只是一个体现了加载器之间关系的概念模型,并且只是体现了OSGi中最简单的加载器委派关系。一般来说,在OSGi中,加载一个类可能发生的查找行为和委派关系会比上图中显示的复杂,类加载时的查找规则如下:
1)以java.*开头的类,委派给父类加载器加载
2)否则,委派列表名单内的类,委派给父类加载器加载
3)否则,Import列表中的类,委派给Export这个类的Bundle的类加载器加载
4)否则,查找当前Bundle的ClassPath,使用自己的类加载器加载
5)否则,查找是否在自己的Fragment Bundle中,如果是,则委派给Fragment bundle的类加载器加载
6)否则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载
7)否则,查找失败
从之前的图可以看出,在OSGi里面,加载器的关系不再是双亲委派模型的树形架构,而是已经进一步发展成了一种更复杂的、运行时才能确定的网状结构。
简单了解Tomcat与OSGi的类加载器架构的更多相关文章
- Java类加载机制与Tomcat类加载器架构
Java类加载机制 类加载器 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这 ...
- Tomcat与spring的类加载器案例
Tomcat与spring的类加载器案例接下来将介绍<深入理解java虚拟机>一书中的案例,并解答它所提出的问题.(部分类容来自于书中原文) Tomcat中的类加载器在Tomcat目录结构 ...
- tomcat 7 中的类加载器学习
tomcat 7自带很多junit测试用例,可以帮助我们窥探源码的秘密.以下使用来测试类加载器的一个测试用例.类加载器也是对象,他们用来将类从类从.class文件加载到虚拟机,这些已经讲了很多,深入j ...
- [读书笔记]OSGI-灵活的类加载器架构
以下内容来自周志明的<深入理解Java虚拟机>. 学习JEE规范,去看JBoss源码:学习类加载器,就去看OSGI源码. OSGI,即Open Service Gateway Initia ...
- 深入理解JVM,类加载器
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流(即字节码)”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称 ...
- 深入理解JVM虚拟机7:JNDI,OSGI,Tomcat类加载器实现
打破双亲委派模型 JNDI JNDI 的理解 JNDI是 Java 命名与文件夹接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之中的一 ...
- Tomcat 类加载器打破双亲委派模型
我们分为4个部分来探讨: 1. 什么是类加载机制? 2. 什么是双亲委任模型? 3. 如何破坏双亲委任模型? 4. Tomcat 的类加载器是怎么设计的? 我想,在研究tomcat 类加载之前,我们复 ...
- java类加载器-Tomcat类加载器
在上文中,已经介绍了系统类加载器以及类加载器的相关机制,还自定制类加载器的方式.接下来就以tomcat6为例看看tomat是如何使用自定制类加载器的.(本介绍是基于tomcat6.0.41,不同版本可 ...
- tomcat: 类加载器
一.tomcat是个web容器,要解决以下问题 1. 一个web容器可能要部署两个或者多个应用程序,不同的应用程序,可能会依赖同一个第三方类库的不同版本,因此要保证每一个应用程序的类库都是独立.相互隔 ...
随机推荐
- mysql笔记-索引
什么是聚簇索引 聚簇索引:索引的叶节点就是数据节点(索引值).而非聚簇索引的叶节点仍然是索引节点(告诉你怎么在表中查找这一记录),只不过有一个指针指向对应的数据块. Innodb和MyIsam区别 转 ...
- 译自如何将Spring Cloud应用程序从Spring Boot 1.2迁移到1.3
前言 笔者第三个Spring Cloud(版本为Spring Boot 1.2)类项目升级最新版本时遇到不少问题,本文内容是作者翻译Spring Cloud官网一位国外友人文章产生. 原文地址: Mi ...
- async & await 异步编程小示例,一看就懂
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Spring注解 @Configuration
Spring注解 @Configuration 一.@Configuration的作用 二.@Configuration的Spring容器启动方式 三.不加@Configuration的@Bean的解 ...
- 跨站点请求伪造(CSRF)
一.前言 跨站点请求伪造(Cross-SiteRequest Forgeries, CSRF),是指攻击者通过设置好的陷阱,强制对已完成认证的用户进行非预期的个人信息或设定信息等某些状态更新,属于被动 ...
- zoj 2724 Windows Message Queue(使用priority_queue容器模拟消息队列)
题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2724 题目描述: Message queue is the b ...
- vue实用组件——表格
之前用了一下vue-bootstrap,感觉里面的表格组件特别好用,但是如果仅仅为了使用表格就引入bootstrap,似乎有点不划算.所以自己就试着实现了一下bootstrap里面表格的部分功能,目前 ...
- 使用WireShark分析使用RedisTemplate取不到值的问题
现象:使用Java Redis客户端将数据存放到Redis后,使用redisTemplate却不出来原因:Java Redis客户端在将数据存放在Redis时,会对Key,Value,Field进行编 ...
- 并发编程—— FutureTask 源码分析
1. 前言 当我们在 Java 中使用异步编程的时候,大部分时候,我们都会使用 Future,并且使用线程池的 submit 方法提交一个 Callable 对象.然后调用 Future 的 get ...
- V8源码边缘试探-黑魔法指针偏移
这博客是越来越难写了,参考资料少,难度又高,看到什么写什么吧! 众多周知,在JavaScript中有几个基本类型,包括字符串.数字.布尔.null.undefined.Symbol,其中大部分都可以在 ...