第9章 类加载及执行子系统的案例与实战
目录
9.1 概述
略。
9.2 案例分析
9.2.1 tomcat:正统的类加载器架构
主流的java web服务器如tomcat、weblogic等,都实现了自己定义的类加载器。
一个功能健全的web服务器,要解决以下几个问题:
- 部署在同一个服务器上的两个web应用程序所使用的java类库相互隔离。因为两个不同的应用程序可能依赖不同版本的类库。
- 部署在同一个服务器上的两个web应用程序所使用的java类库可以共享。如果不能共享,方法区可能出现过度膨胀风险。
- 服务器要尽可能保证自身不受部署的web应用程序影响。因为服务器也有类库依赖的问题,需要与应用程序使用的类库隔离。
- 支持JSP生成类的热替换。
为了解决上述问题,主流的web服务器都提供了好几个ClassPath路径供用户存放第三方类库,比如classes、lib命名的文件夹。被放置到不同路径中的类库,对应有不同的类加载器。
以下以tomcat为例进行说明。
Tomcat中有三组目录/common/*,/server/*,/shared/*可以存放java类库。再加上web应用程序自身的目录/WEB-INF/*,一共四组。
- 放在/common/*中:可被tomcat和所有web应用程序共享。
- 放在/server/*中:只可被tomcat使用。
- 放在/ shared/*中:只可被所有web应用程序使用。
- 放在/WEB-INF/*中:只可被当前web应用程序使用。
为了支持这套目录结构,tomcat实现了多个自定义类加载器,如图。
//缺图。
补充说明:tomcat6.x中,为了简化大多数部署场景做了一项改进。只有指定了/tomcat/conf/Catalina.properties中的server.loader和share.loader才会真正建立CatalinaClassLoader和SharedClassLoader。默认情况下是不指定的,所以tomcat6.x将/common/*,/server/*,/shared/*合并成了/lib/*。如果需要回到上面的设定,则需要手动配置刚刚提到的两个参数。
9.2.2 OSGi:灵活的类加载器架构
OSGi中的每个bundle和java类库类似,都是以jar格式封装,且内部存储java package和class。
它的优点是:热插拔,只需要停用、重新安装或者启动应用程序中一部分即可。
在OSGi中,bundle之间的依赖关系从传统的上下层转变为同级模块之间的依赖。一个bundle可以声明它依赖的java package,也可以声明允许发布的java package。一个模块中只有被发布过的package才能被外部访问。
比如,一个bundle A声明了它依赖的java package,而bundle B声明发布了这个java package,那么所有对这个java package的加载操作都会被委派给bundle B来完成。
但是OSGi也带来了额外的复杂度,带来了线程死锁和内存泄漏的风险等。
9.2.3 字节码生成技术和动态代理的实现
除了javac和字节码类库CGLib等之外,使用字节码生成的例子还有很多,比如编译时植入的AOP框架,动态代理技术,反射等。这里使用相对简单的动态代理来看看字节码生成技术是如何影响程序的。
动态代理的动态,是指在原始类和接口还未知时,就确定了代理类的代理行为。
可以使用java.lang.reflect.Proxy或者java.lang.reflect.InvocationHandler。也可以查看Spring的bean增强。
//缺代码示例。
9.2.4 Retroranslator:跨越JDK版本
略。
9.3 实战:自己动手实现远程执行功能
略。