在Java中用类装载框架控制类加载

sofa-ark:SOFAArk是一个轻量级的,基于Java类加载器隔离框架 SOFAArk Project SOFAArk 是一款基于 Java 实现的轻量级隔离容器,由蚂蚁金服公司开源贡献;主要提供隔离和应用(模块)动态部署能力;基于 技术,可以将多个应用(模块)打包成一个自包含可运行的 Fat Jar,应用既可以是简单的单模块 Java 应用也可以是 Spring Boot/SOFABoot 应用;进入快速开始并获取更多详细信息; 背景 日常使用 Java 开发,常常会遇到包依赖冲突的问题,尤其当工程应用变得臃肿庞大,包冲突的问题也会变得更加棘手,导致各种各样的报错,例如LinkageError, NoSuchMethodError等;实际开发中,可以采用多种方法来解决包冲突问题,比较常见的是似 SpringBoot 的做法,统一管理应用所有依赖包的版本,保证这些三方包不存在依赖冲突;这种做法只能有效避免包冲突的问题,不能根本上解决包冲突的问题;如果某 立即下载

 http://yesky.nen.com.cn/dev/210/2476210.shtml

2006-07-07 08:00 作者: 朱先忠编译 出处: 天极开发 责任编辑:方舟

摘要 通过构建一个能够把Java类装载隔离到一个指定的jar文件中的类装载组件容器框架,你可以确保运行时刻会装载你期望的组件版本。

  Java的类装载框架强有力且具有灵活性。它允许应用程序存取类库而不必链接到静态的"include"文件。代之的是,它能够从指定位置装载包含库类和资源的档案文件,例如由CLASSPATH环境变量所定义的目录和网络位置。由系统来动态地解析对类和资源的运行时刻参考,从而简化了更新和版本发行。然而,每一个库都有其自己的依赖性集合-并且由开发者和发布人员来保证他们的应用程序适当地参考正确的版本。遗憾的是,默认的类装载系统和特定依赖性的结合可能并且确实会导致错误、系统崩溃甚至于更糟糕的情况发生。

  本文中,我将向你建议一个实现类装载的容器框架,从而解决这些问题。

  一、 Java Classpath

  Java根据环境属性/变量CLASSPATH来指定运行时刻用来查找类和其它资源的路径。你可以通过设置CLASSPATH环境变量或使用Java命令行选项--classpath来定义CLASSPATH属性。

  典型地,一个Java运行时刻以下面顺序查找和加载类:

  1. 在bootstrap类列表中的类-这些是体现Java平台的类,例如在rt.jar中的类。

  2. 出现在扩展类列表中的类-这些类使用扩展机制框架来扩展Java平台,使用位于运行时刻环境的/lib/ext目录下的档案文件(.jar,.zip,等等。)。

  3. 用户类-这些类不使用-classpath命令行选项或CLASSPATH环境变量标识的扩展机制架构。

  二、 档案与Classpath

  一个档案.jar或.zip文件可以包括一个manifest文件-它们包含能够用于提供档案信息,设置档案属性,等等的入口。这个manifest文件还可以通过包括一个名为Class-Path的入口(它包含一个档案和目录列表)来扩展classpath。JDK 1.3中引入了Class-Path manifest入口用于指定可选的据需要可以加载的jar文件和目录。下面是一个Class-Path入口的例子:

Class-Path: mystuff/utils.jar
mystuff/logging.jar mylib/

  Java提供了一种可扩展模型用于指定装载类的位置和文件列表。然而,由此也引发了一些问题,例如,一个不同版本的库可能存在于classpath中-这超出一个执行类所期望的结果。

  三、 Classpath版本冲突

  在Java中,一个类的运行时刻 标识 是由通过其完全限定名字来定义的(在类名之前的包名,有时被作为FQN),所有这些都添加到装载类的相关装载器的ID。这样以来,由多个类加载器加载的一个类的每一个实例都将被当作是Java运行时刻的一个单独的实体。这意味着,运行时刻能够在任何时间装载同一个类的多个版本。这是一种非常有力和相当灵活的特征;然而,如果一位开发人员不认真地使用的话,某些副作用可能会令他疑惑不解。

  可以设想,你在开发一个企业应用程序-它使用类似语义从多种源存取数据,例如一个文件系统和一个数据库。许多这种类型的系统都暴露一个数据存取层-通过抽象类似数据源的数据存取对象(DAO)。现在,设想你装载一个新版本的一个数据库DAO,使用一种略微不同的API来满足一个DAO 客户 端的新特征的要求-但是你仍然需要旧式的DAO以便适合于其它还没有为这种新的API准备好的客户端。在典型的运行时刻环境下,这种新的DAO将简单地替换旧的版本并且所有的新实例都将从新版本中创建。然而,如果在不停止运行时刻环境的前提下发生更新,那么任何已经存在的旧DAO的实例将与该新DAO的任何实例一起驻留于内存中-当创建这些新实例时。这已经足已令人疑惑了。更为糟糕的是,一位DAO客户期望创建一个旧版本的DAO的实例,但是实际上得到一个具有已改变的API的新版本的实例。正如你所见,这可能会带来一些有趣的挑战。

  为了确保稳定性和安全性,调用代码必须能够指明它想使用的类的正确版本。为此,你可以创建一个类加载器,组件容器模型并且使用一些简单的类加载技术。

  四、 档案与组件

  因为档案文件(jar文件,zip文件,等等)与Java类加载机制和发布工具之间具有相当松的耦合性,所以它们是一种用作自定义组件容器的自然的候选。一个Java组件在一个档案文件中的打包与发布的成功依赖于:

  · 能够指定要实例化一个组件的哪个版本的开发者

  · 装载组件的辅助类的正确版本-根据与该组件在同一个jar文件中发现的信息。

  这使得组件的开发者和消费者能够完全控制实际创建和使用每一个组件的相应版本。

  在下面的几节中,我将讨论一下有关于定义组件和组件 命名 空间的概念。

  五、 共享辅助资源

  最大的问题之一是,当使用标准类加载器在Java中处理共享库时,所有的类都被加载到一个命名空间中。这使得在任何给定时刻很难使用相同库的不同版本。你所需要的是,一个组件能够定义它自己的命名空间-该组件及其所有辅助库将会装载到其中的。

  因为在Java中,一个类的运行时刻标识是使用类的完全限定名和其加载器的ID来定义的,所以一个命名空间已经相应于每一个类加载器存在。因此,你可以使用类加载器来构建一个组件容器,由它来定义一个组件及其依赖对象的一个命名空间。

  例如,如果我有一个命名为"com.jeffhanson.components.HelloWorld"的类,我想运行它的两个版本,那么解决方案是,使用一个类装载器创建HelloWorld类的一个版本的一个实例,而使用另一个类装载器创建另一个版本的HelloWorld类。图1展示了这一概念。

点击放大此图片
图1.使用多个类装载器:由于Java命名惯例特征的影响,使用不同的类装载器将定义不同的命名空间。


  正如我将在本文中所要展示的,使用两个不同的类装载器来实例化一个类的技术实际上创建了一个虚拟的命名空间。然而,我实际上刚好创建了同一个版本的类的多个实例。

  为了便于加载和实例化同一个类的多个版本,我将 展示 (在下面的几节中)一个组件-容器框架-它基于类装载器命名空间机制以允许装载同一个类的不同版本。

六、 利用Classloader命名空间

  你可以把组件容器框架实现为一个容器实体-负责加载在jar或zip档案中定义的组件以及该组件需要的辅助类。这个框架的创建目标是:

  1. 允许开发者指定实例化一个组件的哪个版本。

  2. 基于与组件在同一个jar文件中找到的信息为每个组件装载正确的辅助类。

  3. 跨组件共享辅助类和档案。

  你将需要一个配置文件来定义组件及其相应的辅助文件,正如下列示例所展示的:

<?xml version="1.0"?>
 <component name="com.jeffhanson.components.HelloWorld">
  <component-archive>
   HelloWorldComponentV1.jar
  </component-archive>
  <ancillary-resources>
  <ancillary-resource>
   log4j-1.2.12.jar
  </ancillary-resource>
  <ancillary-resource>
   concurrent-1.3.4.jar
  </ancillary-resource>
  </ancillary-resources>
</component>


  你可以把上面例子中的元素与下面例子中的元素进行比较。唯一的改变是组件档案元素的值。这个组件元素值定义了包含每一个版本组件的档案的名字。

<?xml version="1.0"?>
<component name=
"com.jeffhanson.components.HelloWorld">
<component-archive>
HelloWorldComponentV2.jar
</component-archive>
<ancillary-resources>
<ancillary-resource>
log4j-1.2.12.jar
</ancillary-resource>
<ancillary-resource>
concurrent-1.3.4.jar
</ancillary-resource>
</ancillary-resources>
</component>


  为了确保框架仅从指定位置加载类,你必须创建一个新的扩展URLClassLoader的ClassLoader。重载loadClass方法以防止到它的调用传播到默认的类装载器的父级-并因此从标准classpath中加载类。这样以来,就可以把类搜索限定到提供给类装载器的URL并且让你把特定jar文件位置提供给装载组件的类装载器。

  下列代码展示了组件的类装载机制:

package com.jeffhanson.components;
import java.net.URL;
import java.net.URLClassLoader;
public class RestrictedURLClassLoader
extends URLClassLoader {
 public RestrictedURLClassLoader( URL[] urls) { super(urls, null);)
}


点击放大此图片
图2.组件容器框架类关系:该图展示了存在于组件容器框架中的类之间的关系。


public Class loadClass(String name)
throws ClassNotFoundException
{
 Class cls = super.loadClass(name);
 if (cls == null)
 {
  throw new ClassNotFoundException("Restricted ClassLoader" + " is unable to find class: " + name);
 }
 return cls;
}
}


  这个受限制的类装载器由组件容器使用来装载组件和任何指定的辅助类。

  该组件容器使用当前线程的上下文类装载器来查找该组件的URL。然后,这个URL被加入到受限制的类装载器并且用于实例化该组件。然后,该组件类被组件容器缓冲以便于后面的调用。列表1展示了该组件容器的代码,图2展示了在组件容器框架中的类之间的关系。

七、 装载特定类版本

  现在,你可以使用该容器和受限制的类装载器来从指定档案中装载包含版本信息的类的组件。

  列表2展示了如何实例化组件容器的实例并且使用配置文件名初始化它们-针对两种版本的HelloWorld组件。然后,每个组件版本被装载和实例化-使用ComponentContainer类的createComponent方法。

  对于每一个实例化的组件对象的调用将产生每个组件的期望版本相应的结果。


图3.组件序列图:该图展示了组件容器框架创建一个组件的序列。

  图3中的序列图展示了框架用于装载和创建一个组件的步骤。

  注意,在实例化一个默认类装载器之前,对于RestrictedURLClassLoader类的调用终止,从而把类搜索限制为提供给RestrictedURLClassLoader实例的URL。

   八、 小结

  总之,你已经从本文中看到了怎样构建一个类装载组件容器框架。这样以来,便利了在一个自包含上下文中定义、版本化和创建Java组件。以这种方式来利用Java的类装载能力可以把类装载约束到指定位置,从而让你同时装载不同版本的类-在同一个运行JVM中创建的和使用的类。

java 利用java反射机制动态加载的简单实现 下面小编就为大家带来一篇java 利用java反射机制动态加载的简单实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧 立即下载

相关推荐

java rmi 安全管理器_Java:无安全管理器:RMI类加载器已禁用

嗨,我有RMI应用程序,现在我尝试从客户端在服务器上调用一些方法。我有以下代码:public static void main(final String[] args) {try {//Setting the security managerSystem.setSecurityManager(new RMISecurityManager());IndicatorsService server = ...

weixin_39696197的博客 252

禁止java new 某个

把构造方法设置成私有的new这个就失效了。 呵呵。 只能通过static 或属性的public才能访问。   再把这个设置成final ,那么它就不能有子。 防止子继承后使用new方法。...

张洪财的专栏 1071

java类装载机制_java类装载机制

张龙老师在圣思源的基础最后部分讲解了一下java类装载机制,当时作为初学者,感觉听起来挺费劲的,现在再看了一下这视频,感觉有点恍然大悟,很多知识点也明白了,现在总结一下,结合别人写的以及自己的理解,也给自己做个笔记!的加载过程java虚拟机与程序的生命周期,在如下几种情况下,java虚拟机将结束生命周期:1、执行了System.exit()方法,我们在Swing中经常使用2、程序正常执行结束3...

weixin_42365303的博客 555

Java类加载机制

按照正常逻辑来说,既然要用到其他中的字段,那么肯定需要加载其他,但是这里我们结果发现,并没有对Test进行加载,那么这是为什么呢?可以看到,在主方法中,我们使用了Test的静态成员变量,并且此静态成员变量是一个final型的,也就是说不可能再发生改变。(因为final不可能发生改变的,编译就会进行优化,直接来个字符串比你去加载在获取快得多不是吗,反正结果都一样),所以说编译之后,实际上跟Test半毛钱关系都没有了。最后就是真正的初始化阶段了,从这里开始,中的Java代码部分,才会开始执行。

yin_ming_hui的博客 453

JVM类装载器详解

JVM类装载器详解

深圳市多克创新科技有限公司 924

java类加载机制

java的加载机制

xiaolong_java的博客 5093

面试题:Java中的类加载

双亲委派 类加载的执行流程

人言束负兑生|完美是完蛋的开始 1267

Java基础 -- 类装载

类装载ClassLoader 工作机制 定义:类装载器就是寻找的字节码文件并构造出,在 JVM 内部表现出来为对象的组件 。在 Java 中,类装载器把一个装入 JVM 中,要经过以下步骤; 装载:查找和导入 Class 文件; 链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的: 校验:检查载入 Class 文件数据的正确性; 准备:给的静态变量分配存储空间; ...

寿春 335

Java类加载过程

转载 的加载 类加载Java程序运行的第一步,研究的加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行。研究类加载机制的第二个目的是让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性。下面的这张图红色圈框表示的加载机制运行时机: 从图中可以看出:编译器(比如eclipse)首先将java文件编译为.class文件,而类加载器又将这些.class文件加载到JVM中。其中类装载器的作用就是的加载。 的加载:将的.class文件中的二进制数据读入到内存中,将其.

cuibw 1822

面试八股之JVM篇2——类加载

类加载器的种、作用、双亲委派模型及采用原因、类装载全过程七大步

做一个善良的人,做好自己的事,如果愿意,再发一份光。 992

七,java类装载

前面我们说沙箱就相当于存取控制器,java载入器对于存取控制器来说是重要的依赖,代码基的权限集合是由类装载器创建的 话题:类加载在安全操作上的用处: ----能为每个建立保护域。如在http://www.hkc.edu.cn/java/class/test.jar URL 上载入字节码,http://www.hkc.edu.cn/java/class/test.jar 和jar

幽径之刃 785

类装载ClassLoader

类装载ClassLoader 类装载器工作机制 类装载器就是寻找的节码文件并构造出在JVM内部表示对象的组件。在Java中,类装载器把一个装入JVM中,要经过以下步骤: [1.]装载:查找和导入Class文件;[2.]链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的:[2.1]校验:检查载入Class文件数据的正确性;[2.2]准备:给的静态变量分配存储空间;[2.3]解析:将...

dooduoaixuexi的博客 921

深入理解——Java类加载机制

我们知道,我们写的java文件是不能直接运行的,我们可以在IDEA中右键文件名点击运行,这中间其实掺杂了一系列的复杂处理过程。这篇文章,我们只讨论我们的代码在运行之前的一个环节,叫做的加载。按照我写文章的常规惯例,先给出这篇文章的大致结构; 首先,认识类加载机制,然后,详细介绍类加载的过程。最后,介绍了类加载器,还有双亲委派原则。 废话不多说,直接来看吧。 一、什么是的加载 在介绍的...

GSON的博客 3253
上一篇: 用p6spy和sqlprofiler来进行jdbc sql日志记录和分析
下一篇: 中日韩大字符集文字编码的比较研究
hennylee
博客等级 码龄24年 6粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值