模块化Java:动态模块化 - 编程入门网
模块化Java:动态模块化时间:2010-11-29 infoq 译:宋玮在前一篇文章《模块化Java:静态模块化》中,我们讨论了如何构建Java模 块并将其作为一个单独的JAR进行部署。文中的例子给出了一个client和一个 server bundle(两者在同一个VM中),client通过工厂方法找到server。在该 例子中,工厂实例化了一个已知类,当然也可以使用反射来获取一个服务实现; Spring就是大量运用这种技术把spring对象绑定在一起的。 在我们讨论动态服务之前,有必要回顾一下类路径,因为标准Java代码和模 块化Java代码的区别之一就是依赖在运行时是如何绑定的。在此之后,我们还将 简单讨论一下类的垃圾回收;如果你对此已非常熟悉,则可以跳过这部分内容。 Bundle ClassPath 对于一个普通Java程序,只有一个classpath——启动应用程序所使用的那个 。该路径通常是在命令行中用-classpath选项指定的,或者通 过CLASSPATH 环 境变量来设定。Java类装载器在运行时解析类的时候会扫描此路径,无论这一过 程是静态地(已编译进代码)还是动态地(使用反射及 class.forName())。然 而,在运行时也可以使用多个类加载器;像Jetty和Tomcat这样的Web应用引擎都 是使用多个类加载器,以便支持应用热部署。 在OSGi中,每个bundle都有其自己的类加载器。需要被其他bundle访问的类 则被委派(delegated)给这些其他bundle的类装载器。因此,尽管在传统应用 中,来自logging类库、client和server JAR中的类都是由同一个类加载器加载 的,但在OSGi模块系统中,他们都是由自己的类加载器加载的。 结果是,一个VM中有可能有多个类加载器,其中可能存在名字相同的不同 Class的对象。也就是说,在同一个VM中,一个叫做 com.infoq.example.App的 类,其不同版本可以由com.infoq.example bundle的第1版和第2版同时输出。 Client bundle版本1使用该类的第1版,而client版本2使用该类的第2版。这在 模块化系统中相当普遍;在同一个VM中,有些代码可能需要装载一个类库的老版 本,同时更新点的代码(在另一个bundle中)却需要该类库的新版本。好在OSGi 为你管理起这种依赖传递,确保不再出现不兼容类引发的问题。 类的垃圾回收 每个类都有一个对其类装载器的引用。因此如果想要从不同的bundle访问这 些类,不但要有对该类实例的引用,而且还要有对该类的类装载器的引用。当一 个bundle持有另一个bundle的类时,它也会将该bundle固定在内存中。在前篇文 章的例子中,client被固定到该server上。 在静态世界里,无论你是否把自己的类固定到其他类(或类库)都无所谓; 因为不会有什么变化。可是,在动态世界里,在运行时将类库或工具替换成新版 本就有可能了。这听起来可能有点复杂,但是在可热部署应用的Web应用引擎早 期就出现了(如Tomcat,最早发布于1999年)。每个Web应用程序都绑定到 Servlet API的某个版本上,当其停止时,装载该Web应用的类加载器也就废弃掉 了。当Web应用重新被部署时,又创建了一个新的类加载器,新版类就由其装载 。只要servlet引擎没有保持对老版应用的引用,这些类就像其他Java对象一样 被垃圾回收器回收了。 并不是所有的类库都能意识到Java代码中可能存在类泄漏的问题,就像是内 存泄漏。一个典型的例子就是Log4J的addAppender()调用,一旦其执行了,将会 把你的类绑定在Log4J bundle的生命周期上。即使你的bundle停止了,Log4J仍 将维对appender的引用,并继续发送日志事件(除非该bundle在停止时恰当地调 用了removeAppender()方法)。 查找和绑定 为了成为动态,我们需要有一个能查找服务的机制,而不是持久持有他们( 以免bundle停止)。这是通过使用简单Java接口和POJO来实现的,也就是大家所 熟知的services( |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |