在Java顶用类装载框架节制类加载
当前位置:以往代写 > JAVA 教程 >在Java顶用类装载框架节制类加载
2019-06-14

在Java顶用类装载框架节制类加载

在Java顶用类装载框架节制类加载

副标题#e#

摘要 通过构建一个可以或许把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的新版本的实例。正如你所见,这大概会带来一些有趣的挑战。

为了确保不变性和安详性,挪用代码必需可以或许指明它想利用的类的正确版本。为此,你可以建设一个类加载器,组件容器模子而且利用一些简朴的类加载技能。


#p#副标题#e#

四、档案与组件

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

· 可以或许指定要实例化一个组件的哪个版本的开拓者

· 装载组件的帮助类的正确版本-按照与该组件在同一个jar文件中发明的信息。

这使得组件的开拓者和消费者可以或许完全节制实际建设和利用每一个组件的相应版本。

在下面的几节中,我将接头一下有关于界说组件和组件定名空间的观念。

五、共享帮助资源

#p#分页标题#e#

最大的问题之一是,当利用尺度类加载器在Java中处理惩罚共享库时,所有的类都被加载到一个定名空间中。这使得在任何给按时刻很难利用沟通库的差异版本。你所需要的是,一个组件可以或许界说它本身的定名空间-该组件及其所有帮助库将会装载到个中的。

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

譬喻,假如我有一个定名为"com.jeffhanson.components.HelloWorld"的类,我想运行它的两个版本,那么办理方案是,利用一个类装载器建设HelloWorld类的一个版本的一个实例,而利用另一个类装载器建设另一个版本的HelloWorld类。图1展示了这一观念。

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

图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>

#p#副标题#e#

你可以把上面例子中的元素与下面例子中的元素举办较量。独一的改变是组件档案元素的值。这个组件元素值界说了包括每一个版本组件的档案的名字。

<?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);)
}

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

图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展示了在组件容器框架中的类之间的干系。

七、装载特定类版本

此刻,你可以利用该容器和受限制的类装载器来从指定档案中装载包括版本信息的类的组件。

#p#分页标题#e#

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

对付每一个实例化的组件工具的挪用将发生每个组件的期望版内情应的功效。

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

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

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

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

八、小结

总之,你已经从本文中看到了奈何构建一个类装载组件容器框架。这样以来,便利了在一个自包括上下文中界说、版本化和建设Java组件。以这种方法来操作Java的类装载本领可以把类装载约束到指定位置,从而让你同时装载差异版本的类-在同一个运行JVM中建设的和利用的类。

    关键字:

在线提交作业