java里如何java类设计工具这个类?

在这一部分的内容中,介绍的是一个相对简单但功能强大的模式:Observer模式。希望通 过这部分地叙述,大家看了之后,能够对设计模式有一个比较全面地,感性的认识。
很多时候,对于一个设计来说(软件上的,建筑上的,或者它他工业上的),经验是至关 重要的。好的经验给我们以指导,并节约我们的时间;坏的经验则给我们以借鉴,可以减少 失败的风险。然而,从知识层面上来讲,经验只是作为一种工作的积累而存在于个人的大脑 中的,很难被传授或者记录。为了解决这样的问题,人们提出了所谓的模式的概念。所谓模 式,是指在一个特定背景下,反复出现的问题解决方案。模式是经验的文档化。
软件模式的概念现在比较的广泛,涉及到分析,设计,体系结构,编码,测试,重构等软 件构造生命期中的各个部分。这儿主要讨论的是设计模式,指的是在软件设计过程中反复出 现的一些问题的解决方法了。不过我们一般在提到设计模式的时候,一般都是指GOF的经典书 《Design Pattern--Elements of Reusable Object-Oriented Software》出现的23个模式, 因而,它是具体的针对于面向对象软件设计过程的。
从全局上看来,模式代表了一种语言,一种被文档化的经验,甚至是一种文化。往往很多 不方便描叙,或者描叙起来很复杂的问题,用模式语言来叙说,会让听者产生心领神会的感 觉。当然,这需要交流双方都能够很好地把握模式语言的含义。然而,这并不是一件容易的 事情。模式在各个人的理解上往往存在差异,这篇文章旨在从一个具体的应用角度:Java类 库,来阐叙设计模式。并结合具体的例子,希望能够加深大家对设计模式的理解。
这儿说的Java类库,其实并没有局限于JDK本身,还包括了一些其他的类库中的例子,比 如JAXP等(当然,下一个版本的JDK中也会包含JAXP了)。其实设计模式的思想现在应用的如 此广泛,无论在什么样的设计中,只要稍微大一点的设计,都可以找到很多很多设计模式的 踪迹,或者说都不可避免的用到设计模式。下面所讲的设计模式,大部分都是GOF的那部经典 中出现过的23个模式,然而,还有一些,比如MVC,并不属于那里。一般的来讲,我们认为 GOF的23个模式是一些中级的模式,在它下面还可以抽象出一些更为一般的低层的模式,在其 上也可以通过组合来得到一些高级的模式。当然,这儿的低中高的区别,如同区别不同的语 言一样,并没有优劣之分,仅仅是在应用层面上的区别。
Observer模式
Observer模式的功用,是希望两个(或多个)对象,我们称之为Subject和Observer,当 一方的状态发生改变的时候,另一方能够得到通知。也就是说,作为Observer的一方,能够 监视到Subject的某个特定的状态变化,并为之做出反应。一个简单的例子就是:当一个用户 视图中的数据被用户改变后,后端的数据库能够得到更新,而当数据库被其他方式更新后, 用户视图中的数据显示也会随之改变。
图一:Obverser模式的类图
在JDK中实际上有一个对Observer模式的简单的实现:就是类java.util.Observerable和 接口java.util.Observer。java.util.Observerable类对应于Subject,而 java.util.Observer就是观察者了。JDK中并没有把这两个部分都设计为接口,而是让类 java.util.Observerable提供了部分的实现,简化了许多编程的工作。当然,这也减少了一 定的灵活性。
下面列出了Observer和Observeral的函数列表,及其简单的功能说明
java.util.Observer:
public void update(Observable obs, Object obj)
java.util.Observer 接口很简单,只定义了这一个方法,狭义的按照Observer模式的说 法,Observer应该在这个方法中调用Subject的getXXX()方法来取得最新的状态,而实际上, 你可以只是在其中对Subject的某些事件进行响应。这便是Java中的代理事件模型的一个雏形 --对事件进行响应。只不过,在Observer模式中将事件特定化为某个状态/数据的改变了。
java.util.Observable
public void addObserver(Observer obs)
向Subject注册一个Observer。也就是把这个Observer对象添加到了一个 java.util.Observable内部的列表中。在JDK中对于这个列表是简单的通过一个 java.util.Vector类来实现的,而实际上,在一些复杂的Observer模式的应用中,需要把这 个部分单另出来形成一个Manager类,来管理Subject和Observer之间的映射。这样,Subject 和Observer进一步的被解藕,程序也会具有更大的灵活性。
public void deleteObserver(Observer obs)
从Subject中删除一个已注册了Observer的引用。
public void deleteObservers()
从Subjec中删除所有注册的Observer的引用。
public int countObservers()
返回注册在Subject中的Observer个数。
protected void setChanged()
设置一个内部的标志以指明这个Ovserver的状态已经发生改变。注意这是一个protected 方法,也就是说只能在Observer类和其子类中被调用,而在其它的类中是看不到这个方法的 。
protected void clearChanged()
清除上叙的内部标志。它在notifyObservers()方法内部被自动的调用,以指明Subject的 状态的改变已经传递到Ovserver中了。
public boolean hasChanged()
确定Subject的状态是否发生了改变。
public void notifyObservers(Object obj)
它首先检查那个内部的标志,以判断状态是否改变,如果是的话,它会调用注册在 Subject中的每个Observer的update()方法。在JDK中这个方法内部是作为synchronized来实 现的,也就是如果发生多个线程同时争用一个java.util.Observerable的notifyObservers() 方法的话,他们必须按调度的等待着顺序执行。在某些特殊的情况下,这会有一些潜在的问 题:可能在等待的过程中,一个刚刚被加入的Observer会被遗漏没有被通知到,而一个刚刚 被删除了的Observer会仍然收到它已经不想要了的通知。
public void notifyObservers()
等价于调用了notifyObservers(null)。
因而在Java中应用Observer就很简单了,需要做的是:让需要被观察的Subject对象继承 java.util.Observerable,让需要观察的对象实现java.util.Observer接口,然后用 java.util.Observerable的addObserver(Observer obj)方法把Observer注册到Subject对象 中。这已经完成了大部分的工作了。然后调用java.util.Observerable的notifyObservers (Object arg)等方法,就可以实现Observer模式的机理。我们来看一个简单使用了这个模式 的例子。这个例子有三个类:FrameSubject,DateSubject,FrameObject和EntryClass, FrameSubject中用户可以设置被观察的值,然后自动的会在FrameObject中显示出来, DateSubject封装被观察的值,并且充当Observer模式中的Subject。
public class FrameSubject extends JFrame. {
  //因为无法使用多重继承,这儿就只能使用对象组合的方式来引入一个
//java.util.Observerable对象了。
  DateSubject subject=new DateSubject();
//这个方法转发添加Observer消息到DateSubject。
  public void registerObserver(java.util.Observer o){
   subject.addObserver(o);
  //数据改变,事件被触发后调用notifyObservers()来通知Observer。
void jButton1_actionPerformed(ActionEvent e) {
   subject.setWidthInfo(Integer.parseInt(jTextField1.getText()));
   subject.setHeightInfo(Integer.parseInt(jTextField2.getText()));
   subject.notifyObservers();
public class DateSubject extends Observable {
   //封装被观察的数据
  private int widthI
  private int heightI
  public int getWidthInfo() {
   return widthI
  public void setWidthInfo(int widthInfo) {
this.widthInfo = widthI
//数据改变后,setChanged()必须被调用,否则notifyObservers()方法会不起作用
this.setChanged();
  public void setHeightInfo(int heightInfo) {
   this.heightInfo = heightI
   this.setChanged();
  public int getHeightInfo() {
   return heightI
public class FrameObserver extends JFrame. implements java.util.Observer {
   //观察的数据
  int widthInfo=0;
  int heightInfo=0;
//在update()方法中实现对数据的更新和其它必要的反应。
  public void update(Observable o, Object arg) {
   DateSubject subject=(DateSubject)
   widthInfo=subject.getWidthInfo();
   heightInfo=subject.getHeightInfo();
   jLabel1.setText("The heightInfo from subject is: ");
   jLabel3.setText(String.valueOf(heightInfo));
   jLabel2.setText("The widthInfo from subject is: ");
   jLabel4.setText(String.valueOf(widthInfo));
public class EntryClass {
   public static void main(String[] args) {
   &&&&&..
   FrameSubject frame. = new FrameSubject();
FrameObserver frame2=new FrameObserver();
//在Subject中注册Observer,将两者联系在一起
frame.registerObserver(frame2);
frame.setVisible(true);
frame2.setVisible(true);
   &&&&&..
我认为在JDK中这个Observer模式的实现,对于一般的Observer模式的应用,已经是非常 的足够了的。但是一方面它用一个类来实现了Subject,另一方面它使用Vector来保存 Subject对于Observer的引用,这虽然简化了编程的过程,但会限制它在一些需要更为灵活, 复杂的设计中的应用,有时候(虽然这种情况不多),我们还不得不重新编写新的Subject对 象和额外的Manager对象来实现更为复杂的Observer模式的应用。
这一部分主要的讨论了模式的概念。随着现代软件工业的不断进步,软件系统的规模的日 益扩大,越来越需要对某些个不断出现的问题进行模式化思维,以成功的经验或者失败的教 训来减少软件开发失败的风险。模式代表了一种文档化的经验,它为某一类的问题提供了最 好(或者说很好)的解决方案,使得即使不是经验丰富的软件工程师,也能够根据模式来构 建相对成功的系统。本节给出的一个Obverser模式的示例,比较好的说明了这一点。 Obverser模式主要解决在对象间的状态映射或者镜像的问题。
声明:该文章系网友上传分享,此内容仅代表网友个人经验或观点,不代表本网站立场和观点;若未进行原创声明,则表明该文章系转载自互联网;若该文章内容涉嫌侵权,请及时向
上一篇:下一篇:
相关经验教程
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.005 收益
的原创经验被浏览,获得 ¥0.001 收益
的原创经验被浏览,获得 ¥0.002 收益
的原创经验被浏览,获得 ¥0.001 收益IBM Bluemix
点击按钮,开始云上的开发!
developerWorks 社区
对于许多关键性业务或者庞大的 Java 系统来说,如果必须暂停系统服务才能进行系统升级,既会大大影响到系统的可用性,同时也增加了系统的管理和维护成本。因此,如果能够方便地在不停止系统业务的情况下进行系统升级,则可以很好地解决上述问题。在本文中,我们将基于实例,对构建在线升级 Java 系统的基础技术和设计原则进行了深入的讲解。相信读者能够根据文中的技术构建出自己的在线升级系统来。
, 软件工程师
孙鸣,软件工程师,《平衡敏捷与规范》一书的译者,擅长对象技术。
, 软件工程师
邓辉,软件工程师,《敏捷软件开发:原则、模式与实践》等书的译者,对如何优雅地构建软件有浓厚的兴趣。
Java ClassLoader 技术剖析在本文中,我们将不对 Java ClassLoader 的细节进行过于详细的讲解,而是关注于和构建在线升级系统相关的基础概念。关于 ClassLoader 的详细细节许多资料可以参考,有兴趣的读者可以自行研读。要构建在线升级系统,一个重要的技术就是能够实现 Java 类的热替换 —— 也就是在不停止正在运行的系统的情况下进行类(对象)的升级替换。而 Java 的 ClassLoader 正是实现这项技术的基础。在 Java 中,类的实例化流程分为两个部分:类的加载和类的实例化。类的加载又分为显式加载和隐式加载。大家使用 new 关键字创建类实例时,其实就隐式地包含了类的加载过程。对于类的显式加载来说,比较常用的是 Class.forName。其实,它们都是通过调用 ClassLoader 类的 loadClass 方法来完成类的实际加载工作的。直接调用 ClassLoader 的 loadClass 方法是另外一种不常用的显式加载类的技术。图 1. Java 类加载器层次结构图ClassLoader 在加载类时有一定的层次关系和规则。在 Java 中,有四种类型的类加载器,分别为:BootStrapClassLoader、ExtClassLoader、AppClassLoader 以及用户自定义的 ClassLoader。这四种类加载器分别负责不同路径的类的加载,并形成了一个类加载的层次结构。BootStrapClassLoader 处于类加载器层次结构的最高层,负责 sun.boot.class.path 路径下类的加载,默认为 jre/lib 目录下的核心 API 或 -Xbootclasspath 选项指定的 jar 包。ExtClassLoader 的加载路径为 java.ext.dirs,默认为 jre/lib/ext 目录或者 -Djava.ext.dirs 指定目录下的 jar 包加载。AppClassLoader 的加载路径为 java.class.path,默认为环境变量 CLASSPATH 中设定的值。也可以通过 -classpath 选型进行指定。用户自定义 ClassLoader 可以根据用户的需要定制自己的类加载过程,在运行期进行指定类的动态实时加载。这四种类加载器的层次关系图如
所示。一般来说,这四种类加载器会形成一种父子关系,高层为低层的父加载器。在进行类加载时,首先会自底向上挨个检查是否已经加载了指定类,如果已经加载则直接返回该类的引用。如果到最高层也没有加载过指定类,那么会自顶向下挨个尝试加载,直到用户自定义类加载器,如果还不能成功,就会抛出异常。Java 类的加载过程如
所示。图 2. Java 类的加载过程每个类加载器有自己的名字空间,对于同一个类加载器实例来说,名字相同的类只能存在一个,并且仅加载一次。不管该类有没有变化,下次再需要加载时,它只是从自己的缓存中直接返回已经加载过的类引用。我们编写的应用类默认情况下都是通过 AppClassLoader 进行加载的。当我们使用 new 关键字或者 Class.forName 来加载类时,所要加载的类都是由调用 new 或者 Class.forName 的类的类加载器(也是 AppClassLoader)进行加载的。要想实现 Java 类的热替换,首先必须要实现系统中同名类的不同版本实例的共存,通过上面的介绍我们知道,要想实现同一个类的不同版本的共存,我们必须要通过不同的类加载器来加载该类的不同版本。另外,为了能够绕过 Java 类的既定加载过程,我们需要实现自己的类加载器,并在其中对类的加载过程进行完全的控制和管理。编写自定义的 ClassLoader为了能够完全掌控类的加载过程,我们的定制类加载器需要直接从 ClassLoader 继承。首先我们来介绍一下 ClassLoader 类中和热替换有关的的一些重要方法。findLoadedClass:每个类加载器都维护有自己的一份已加载类名字空间,其中不能出现两个同名的类。凡是通过该类加载器加载的类,无论是直接的还是间接的,都保存在自己的名字空间中,该方法就是在该名字空间中寻找指定的类是否已存在,如果存在就返回给类的引用,否则就返回 null。这里的直接是指,存在于该类加载器的加载路径上并由该加载器完成加载,间接是指,由该类加载器把类的加载工作委托给其他类加载器完成类的实际加载。getSystemClassLoader:Java2 中新增的方法。该方法返回系统使用的 ClassLoader。可以在自己定制的类加载器中通过该方法把一部分工作转交给系统类加载器去处理。defineClass:该方法是 ClassLoader 中非常重要的一个方法,它接收以字节数组表示的类字节码,并把它转换成 Class 实例,该方法转换一个类的同时,会先要求装载该类的父类以及实现的接口类。loadClass:加载类的入口方法,调用该方法完成类的显式加载。通过对该方法的重新实现,我们可以完全控制和管理类的加载过程。resolveClass:链接一个指定的类。这是一个在某些情况下确保类可用的必要方法,详见 Java 语言规范中“执行”一章对该方法的描述。了解了上面的这些方法,下面我们来实现一个定制的类加载器来完成这样的加载流程:我们为该类加载器指定一些必须由该类加载器直接加载的类集合,在该类加载器进行类的加载时,如果要加载的类属于必须由该类加载器加载的集合,那么就由它直接来完成类的加载,否则就把类加载的工作委托给系统的类加载器完成。在给出示例代码前,有两点内容需要说明一下:1、要想实现同一个类的不同版本的共存,那么这些不同版本必须由不同的类加载器进行加载,因此就不能把这些类的加载工作委托给系统加载器来完成,因为它们只有一份。2、为了做到这一点,就不能采用系统默认的类加载器委托规则,也就是说我们定制的类加载器的父加载器必须设置为 null。该定制的类加载器的实现代码如下:清单 1. 定制的类加载器的实现代码class CustomCL extends ClassLoader {
private S // 需要该类加载器直接加载的类文件的基目录
private HashS // 需要由该类加载器直接加载的类名
public CustomCL(String basedir, String[] clazns) {
super(null); // 指定父类加载器为 null
this.basedir =
dynaclazns = new HashSet();
loadClassByMe(clazns);
private void loadClassByMe(String[] clazns) {
for (int i = 0; i & clazns. i++) {
loadDirectly(clazns[i]);
dynaclazns.add(clazns[i]);
private Class loadDirectly(String name) {
Class cls =
StringBuffer sb = new StringBuffer(basedir);
String classname = name.replace('.', File.separatorChar) + ".class";
sb.append(File.separator + classname);
File classF = new File(sb.toString());
cls = instantiateClass(name,new FileInputStream(classF),
classF.length());
private Class instantiateClass(String name,InputStream fin,long len){
byte[] raw = new byte[(int) len];
fin.read(raw);
fin.close();
return defineClass(name,raw,0,raw.length);
protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class cls =
cls = findLoadedClass(name);
if(!this.dynaclazns.contains(name) && cls == null)
cls = getSystemClassLoader().loadClass(name);
if (cls == null)
throw new ClassNotFoundException(name);
if (resolve)
resolveClass(cls);
}在该类加载器的实现中,所有指定必须由它直接加载的类都在该加载器实例化时进行了加载,当通过 loadClass 进行类的加载时,如果该类没有加载过,并且不属于必须由该类加载器加载之列都委托给系统加载器进行加载。理解了这个实现,距离实现类的热替换就只有一步之遥了,我们在下一小节对此进行详细的讲解实现 Java 类的热替换在本小节中,我们将结合前面讲述的类加载器的特性,并在上小节实现的自定义类加载器的基础上实现 Java 类的热替换。首先我们把上小节中实现的类加载器的类名 CustomCL 更改为 HotswapCL,以明确表达我们的意图。现在来介绍一下我们的实验方法,为了简单起见,我们的包为默认包,没有层次,并且省去了所有错误处理。要替换的类为 Foo,实现很简单,仅包含一个方法 sayHello:清单 2. 待替换的示例类public class Foo{
public void sayHello() {
System.out.println("hello world! (version one)");
}在当前工作目录下建立一个新的目录 swap,把编译好的 Foo.class 文件放在该目录中。接下来要使用我们前面编写的 HotswapCL 来实现该类的热替换。具体的做法为:我们编写一个定时器任务,每隔 2 秒钟执行一次。其中,我们会创建新的类加载器实例加载 Foo 类,生成实例,并调用 sayHello 方法。接下来,我们会修改 Foo 类中 sayHello 方法的打印内容,重新编译,并在系统运行的情况下替换掉原来的 Foo.class,我们会看到系统会打印出更改后的内容。定时任务的实现如下(其它代码省略,请读者自行补齐):清单 3. 实现定时任务的部分代码public void run(){
// 每次都创建出一个新的类加载器
HowswapCL cl = new HowswapCL("../swap", new String[]{"Foo"});
Class cls = cl.loadClass("Foo");
Object foo = cls.newInstance();
Method m = foo.getClass().getMethod("sayHello", new Class[]{});
m.invoke(foo, new Object[]{});
catch(Exception ex) {
ex.printStackTrace();
}编译、运行我们的系统,会出现如下的打印:图 3. 热替换前的运行结果好,现在我们把 Foo 类的 sayHello 方法更改为:public void sayHello() {
System.out.println("hello world! (version two)");
}在系统仍在运行的情况下,编译,并替换掉 swap 目录下原来的 Foo.class 文件,我们再看看屏幕的打印,奇妙的事情发生了,新更改的类在线即时生效了,我们已经实现了 Foo 类的热替换。屏幕打印如下:图 4. 热替换后的运行结果敏锐的读者可能会问,为何不用把 foo 转型为 Foo,直接调用其 sayHello 方法呢?这样不是更清晰明了吗?下面我们来解释一下原因,并给出一种更好的方法。如果我们采用转型的方法,代码会变成这样:Foo foo = (Foo)cls.newInstance();
读者如果跟随本文进行试验的话,会发现这句话会抛出 ClassCastException 异常,为什么吗?因为在 Java 中,即使是同一个类文件,如果是由不同的类加载器实例加载的,那么它们的类型是不相同的。在上面的例子中 cls 是由 HowswapCL 加载的,而 foo 变量类型声名和转型里的 Foo 类却是由 run 方法所属的类的加载器(默认为 AppClassLoader)加载的,因此是完全不同的类型,所以会抛出转型异常。那么通过接口调用是不是就行了呢?我们可以定义一个 IFoo 接口,其中声名 sayHello 方法,Foo 实现该接口。也就是这样:IFoo foo = (IFoo)cls.newInstance(); 本来该方法也会有同样的问题的,因为外部声名和转型部分的 IFoo 是由 run 方法所属的类加载器加载的,而 Foo 类定义中 implements IFoo 中的 IFoo 是由 HotswapCL 加载的,因此属于不同的类型转型还是会抛出异常的,但是由于我们在实例化 HotswapCL 时是这样的:HowswapCL cl = new HowswapCL("../swap", new String[]{"Foo"});其中仅仅指定 Foo 类由 HotswapCL 加载,而其实现的 IFoo 接口文件会委托给系统类加载器加载,因此转型成功,采用接口调用的代码如下:清单 4. 采用接口调用的代码public void run(){
HowswapCL cl = new HowswapCL("../swap", new String[]{"Foo"});
Class cls = cl.loadClass("Foo");
IFoo foo = (IFoo)cls.newInstance();
foo.sayHello();
} catch(Exception ex) {
ex.printStackTrace();
}确实,简洁明了了很多。在我们的实验中,每当定时器调度到 run 方法时,我们都会创建一个新的 HotswapCL 实例,在产品代码中,无需如此,仅当需要升级替换时才去创建一个新的类加载器实例。在线升级系统的设计原则在上小节中,我们给出了一个 Java 类热替换的实例,掌握了这项技术,就具备了实现在线升级系统的基础。但是,对于一个真正的产品系统来说,升级本省就是一项非常复杂的工程,如果要在线升级,就会更加复杂。其中,实现类的热替换只是最后一步操作,在线升级的要求会对系统的整体设计带来深远的影响。下面我们来谈谈在线升级系统设计方面的一些原则:在系统设计一开始,就要考虑系统的哪些部分是需要以后在线升级的,哪些部分是稳定的。虽然我们可以把系统设计成任何一部分都是可以在线升级的,但是其成本是非常高昂的,也没有必要。因此,明确地界定出系统以后需要在线升级的部分是明智之举。这些部分常常是系统业务逻辑规则、算法等等。设计出规范一致的系统状态转换方法。替换一个类仅仅是在线升级系统所要做的工作中的一个步骤,为了使系统能够在升级后正常运行,就必须保持升级前后系统状态的一致性。因此,在设计时要考虑需要在线升级的部分所涉及的系统状态有哪些,把这些状态设计成便于获取、设置和转换的,并用一致的方式来进行。明确出系统的升级控制协议。这个原则是关于系统在线升级的时机和流程控制的,不考虑系统的当前运行状态就贸然进行升级是一项非常危险的活动。因此在系统设计中, 就要考虑并预留出系统在线升级的控制点, 并定义清晰、明确的升级协议来协调、控制多个升级实体的升级次序,以确保系统在升级的任何时刻都处在一个确定的状态下。考虑到升级失败时的回退机制。即使我们做了非常缜密细致的设计,还是难以从根本上保证系统升级一定是成功的,对于大型分布式系统来说尤其如此。因此在系统设计时,要考虑升级失败后的回退机制。好了,本小节我们简单介绍了在线升级系统设计时的几个重要的原则,下一小节我们将给出一个简单的实例,来演示一下如何来实现一个在线升级系统。在线升级系统实例首先,我们来简单介绍一下这个实例的结构组成和要完成的工作。在我们的例子中,主要有三个实体,一个是升级控制实体,两个是工作实体,都基于 ActiveObject 实现,通过命令消息进行通信(关于 ActiveObject 的详细信息,可以参见作者的另外一篇文章“”)。升级控制实体以 RMI 的方式对外提供了一个管理命令接口,用以接收外部的在线升级命令。工作实体有两个消息队列,一个用以接收分配给它的任务(我们用定时器定时给它发送任务命令消息),我们称其为任务队列;另一个用于和升级控制实体交互,协作完成升级过程,我们称其为控制队列。工作实体中的任务很简单,就是使用我们前面介绍的 Foo 类简单地打印出一个字符串,不过这次字符串作为状态保存在工作实体中,动态设置给 Foo 类的实例的。升级的协议流程如下:当升级控制实体接收到来自 RMI 的在线升级命令时,它会向两个工作实体的任务队列中发送一条准备升级消息,然后等待回应。当工作实体在任务队列中收到准备升级消息时,会立即给升级控制实体发送一条准备就绪消息,然后切换到控制队列等待进一步的升级指令。升级控制实体收齐这两个工作实体发来的准备就绪消息后,就给这两个工作实体的控制队列各发送一条开始升级消息,然后等待结果。工作实体收到开始升级消息后,进行实际的升级工作,也就是我们前面讲述的热替换类。然后,给升级控制实体发送升级完毕消息。升级控制实体收到来自两个工作实体的升级完毕消息后,会给这两个工作实体的控制队列各发送一条继续工作消息,工作实体收到继续工作消息后,切换到任务队列继续工作。升级过程结束。主要的代码片段如下(略去命令消息的定义和执行细节):清单 5. 主要的代码片段// 升级控制实体关键代码
class UpgradeController extends ActiveObject{
int nready
int nfinished = 0;
// 收到外部升级命令消息时,会触发该方法被调用
public void askForUpgrade() {
for(int i=0; i&workers. i++)
workers[i].getTaskQueue().enqueue(new PrepareUpgradeCmd(workers[i]));
// 收到工作实体回应的准备就绪命令消息时,会触发该方法被调用
public void readyForUpgrade(String worker_name) {
if(nready == workers.length){
for(int i=0; i&workers. i++)
workers[i].getControlQueue().enqueue(new
StartUpgradeCmd(workers[i]));
// 收到工作实体回应的升级完毕命令消息时,会触发该方法被调用
public void finishUpgrade(String worker_name) {
nfinished++;
if(nfinished == workers.length){
for(int i=0; i&workers. i++)
workers[i].getControlQueue().enqueue(new
ContineWorkCmd(workers[i]));
// 工作实体关键代码
class Worker extends ActiveObject{
String state = "hello world!";
// 收到升级控制实体的准备升级命令消息时,会触发该方法被调用
public void prepareUpgrade() {
switchToControlQueue();
ugc.getMsgQueue().enqueue(new ReadyForUpdateCMD(ugc,this));
// 收到升级控制实体的开始升级命令消息时,会触发该方法被调用
public void startUpgrade(String worker_name) {
doUpgrade();
ugc.getMsgQueue().enqueue(new FinishUpgradeCMD(ugc,this));
// 收到升级控制实体的继续工作命令消息时,会触发该方法被调用
public void continueWork(String worker_name) {
switchToTaskQueue();
// 收到定时命令消息时,会触发该方法被调用
public void doWork() {
foo.sayHello();
// 实际升级动作
private void doUpgrade() {
hscl = new HowswapCL("../swap", new String[]{"Foo"});
Class cls = hscl.loadClass("Foo");
foo = (IFoo)cls.newInstance();
foo.SetState(state);
//IFoo 接口定义
interface IFoo {
void SetState(String);
void sayHello();
}在 Foo 类第一个版本的实现中,只是把设置进来的字符串直接打印出来。在第二个版本中,会先把设置进来的字符串变为大写,然后打印出来。例子很简单,旨在表达规则或者算法方面的升级变化。另外,我们并没有提及诸如:消息超时、升级失败等方面的异常情况,这在实际产品开发中是必须要考虑的。小结在本文中,我们对 Java 在线升级系统中设计的基础技术:类的热替换,进行了详细的讲解。此外,还给出了在线升级系统设计时的一些主要指导原则。为了使读者更好地理解这些技术和原则,我们在最后给出了一个在线升级系统的实例。值得注意的是,构建在线升级系统不仅仅是一个技术问题,还牵扯到很多管理方面的因素,比如:如何管理、部署系统中的可在线升级部分和不可在线升级部分以降低系统的管理、维护成本等。希望本文在读者构建自己的在线升级系统时能够提供一些帮助。
参考资料 参考:“”。参考:“”。“”(developerWorks,2001 年 6 月):创建自己的 ClassLoader 可以以实用且有趣的方式定制 JVM,这样可以让您彻底重新定义如何将类文件引入系统。“”(developerWorks,2002 年 4 月):介绍通过构建一个并发模型框架,使得开发多线程的应用变得容易。:查找关于 Java 编程各方面的数百篇文章。加入 。查看 的最新信息。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
为灾难恢复构建应用,赢取现金大奖。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Java technologyArticleID=461623ArticleTitle=Java 类的热替换 —— 概念、设计与实现publish-date=}

我要回帖

更多关于 java 常量类设计 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信