tooling aspects [mindmap]

其实就是一个brainstorm的整理

MoBarbr – 记账

这个是前一阵子有空的时候用swing写的一个简单的记账软件。很简单的功能,无非就是记账,添加顾客,充值,修改,报表。用Spring来管理swing ui组件之间的依赖,发现也很方便,而且看起来更清晰,不像以前那种panel,frame,dialog的引用传来传去。本来还准备用thinlet重写一遍,后来因为thinlet只支持awt而没做。同时还试了一些方法来发布这个桌面应用,见Java桌面应用程序发布 截图: 下载 源代码

使用canvas画的一个登录的Form

<!–code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }– 使用canvas也就是通常说的低级组件(low-level user interface api)画了一个登录的form,这个用high-level的form很简单,但是自己写确实很麻烦。因为所有的控制都要自己写:键盘输入(输入框),组件绘制,动画线程(光标闪烁), 输入框长度控制, 组件焦点, 密码mask. 刚刚开始接触wtk,练习一下.

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
public class LoginForm extends Canvas implements CommandListener {

private Image img;
private Font font;
public int currentPosX, currentPosY;
public int width, height;
public int inputBoxWidth = 150;

public int offset = 5;
public int curX = 0, curY = 0;

static final int USER_INPUT = 1, PASS_INPUT = 2, REMEMBER_CHECK = 3;

public String username = “”, password = “”;
public String usernameShow = “”,passwordShow=“”;//the display part of these two fields
public int userInputLimitation = 0,passInputLimitation = 0; //characters limitation of input box

public int userInputX, userInputY;
public int passInputX, passInputY;
public int remeCheckX, remeCheckY;
public int curSel = USER_INPUT;

public int cursorX,cursorY;
public int cursorColor = 0x00000000;
public CursorBlink cursorBlink;

private StringBuffer input = new StringBuffer(“default input”);
boolean initialized = false;

public LoginForm() {
setCommandListener(this);
width = getWidth();
height = getHeight();
// The current cursor position for the text
currentPosX = width / 2;
currentPosY = height / 2;
// The text font
font = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN,
Font.SIZE_LARGE);

// setFullScreenMode (true);
// Cursor blink thread
cursorBlink = new CursorBlink(this);
Thread blinkThrd = new Thread(cursorBlink);
blinkThrd.start();

setCommandListener(this);

try {
img = Image.createImage(“/imgs/” + “gtalk.jpg”);
} catch (Exception e) {
e.printStackTrace();
img = null;
}
}

protected void paint(Graphics g) {
if (!initialized) {
// Fill a white background
g.setColor(0xffffff);

g.fillRect(0, 0, width, height);
// Draw logo image
g.drawImage(img, curX, curY, Graphics.TOP | Graphics.LEFT);

// Draw form
// set font and color-black
g.setFont(font);
g.setColor(0, 0, 0);
// Draw name label and box
curY += img.getHeight() + offset;
g.drawString(“Username “, curX, curY, Graphics.TOP | Graphics.LEFT);
curY += offset + font.getHeight();
userInputX = curX;
userInputY = curY;
g.drawRect(curX, curY, inputBoxWidth, font.getHeight());
curY += offset / 2 + font.getHeight();
g
.drawString(“@gmail.com”, curX, curY, Graphics.TOP
| Graphics.LEFT);
// Draw password label and box
curY += offset + font.getHeight();
g.drawString(“Password “, curX, curY, Graphics.TOP | Graphics.LEFT);
curY += offset + font.getHeight();
passInputX = curX;
passInputY = curY;
g.drawRect(curX, curY, inputBoxWidth, font.getHeight());

cursorX = userInputX;
cursorY = userInputY;

initialized =true;
}else{
update(g);
}

}

public void update(Graphics g) {
highLightSelection(g);
//remove previous input
g.setColor(255, 255, 255);
//1px offset for the 1px border
g.fillRect(userInputX+1, userInputY+1, inputBoxWidth-1, font.getHeight()1);
g.fillRect(passInputX+1, passInputY+1, inputBoxWidth-1, font.getHeight()1);
//draw the input again
drawInput(g);
showCursor(g);
}

private void drawInput(Graphics g) {
g.setColor(0,0,0);
g.setFont(font);
usernameShow = username;
//add a blank space to avoid the char cross line, as the space available is not enough for a char like “k”, but enough for “l”
if(font.stringWidth(usernameShow+” “) >= inputBoxWidth){
if(userInputLimitation == 0) userInputLimitation = username.length();
usernameShow = username.substring(username.length()-userInputLimitation);
}
passwordShow = mask(password);
if(font.stringWidth(passwordShow+” “) >= inputBoxWidth){
if(passInputLimitation == 0) passInputLimitation = password.length();
passwordShow = password.substring(password.length()-passInputLimitation);
}

g.drawString(usernameShow, userInputX, userInputY, Graphics.TOP
| Graphics.LEFT);
g.drawString(mask(passwordShow), passInputX, passInputY, Graphics.TOP
| Graphics.LEFT);
}

private void showCursor(Graphics g) {
g.setColor(cursorColor);
if(curSel == USER_INPUT){
g.drawLine(userInputX+font.stringWidth(usernameShow),userInputY+1,userInputX+font.stringWidth(usernameShow),userInputY+font.getHeight()1);
}else if(curSel == PASS_INPUT){
g.drawLine(passInputX+font.stringWidth(mask(passwordShow)),passInputY+1,passInputX+font.stringWidth(mask(passwordShow)),passInputY+font.getHeight()1);
}

}

private String mask(String pass){
StringBuffer sb = new StringBuffer();
int le = pass.length();
for(int i=0;i<le;i++){
sb.append(‘*’);
}
return sb.toString();
}

private void highLightSelection(Graphics g) {
// set blue background
g.setColor(0, 0, 255);
if (curSel == USER_INPUT) {
g.drawRect(userInputX, userInputY, inputBoxWidth, font.getHeight());
g.setColor(0, 0, 0);
g.drawRect(passInputX, passInputY, inputBoxWidth, font.getHeight());

} else if (curSel == PASS_INPUT) {
g.drawRect(passInputX, passInputY, inputBoxWidth, font.getHeight());
g.setColor(0, 0, 0);
g.drawRect(userInputX, userInputY, inputBoxWidth, font.getHeight());

}
}

public void keyPressed(int keyCode) {
int gameCode = getGameAction(keyCode);
if (gameCode == UP) {
curSel = USER_INPUT;
} else if (gameCode == DOWN) {
curSel = PASS_INPUT;
} else if (gameCode == LEFT) {
//TODO move cursor
} else if (gameCode == RIGHT) {
//TODO move cursor
} else if (keyCode == –8 || keyCode == 8) { // -8 clear key, 8 backspace
if (curSel == USER_INPUT) {
if(“”.equals(username))return;
username = username.substring(0, username.length()1);
} else if (curSel == PASS_INPUT) {
if(“”.equals(password))return;
password = password.substring(0,password.length()1);
}
} else {
char c = (char) keyCode;
if (curSel == USER_INPUT) {
username += c;
} else if (curSel == PASS_INPUT) {
password += c;
}
}
repaint();
}

public void commandAction(Command c, Displayable d) {
System.out.println(c);
}

class CursorBlink implements Runnable {
LoginForm form;

public CursorBlink (LoginForm form) {
this.form = form;
}
public void run () {
int seed=0;
while (true) {
seed++;
if( seed%2 == 0){
cursorColor = 0x00ffffff; //white
}else{
cursorColor = 0x00000000; //black
}
form.repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}


jBPM中的assignment

ZhangV

在jbpm的流程定义文件(processdefinition.xml)中当定义一个task的assignment的时候有几种选择:

  • Swimlane
  • Pooled Actors
  • Expression
  • Actor
  • Handler

这里分别描述一下每种的用法,以及使用场景。使用场景的理解很重要,只有清楚每一种用法的所适合的情形,以及他们之间的相互转换才能说是真的“会用了”。

Swimlane
泳道是用来处理一个流程中的多个任务必须由同一个人来处理的机制。所以,当一个流程的第一个任务实例被创建到指定的泳道后,第一个任务实例中的执行人被保 存,该泳道中的后续任务的处理人都是第一个任务的处理人。所以一个泳道有一个人员被分配,并且泳道中的任务不需要再分配人员。这里是指这个流程中的参与者或者角色。在实际的业务系统中,他可能根本就不是一个业务角色,可能是一个外部应用,也可能是一个处理过程,或者是几个业务角色的结合。所以要清楚他是指“在该流程中”的角色。在jPDL的user guide中写的很清楚也反复强调 – “process role”。
通过在流程定义文件中定义:
<swimlane name=”leader”>
<assignment class=”com.modofo.engine.swimlane.LeaderSwimlane”></assignment>
</swimlane>
同样可以实现运行时的结合实际应用进行灵活的任务分配。这也是目前我看到的使用比较多的方式。

Actor
相当于设置TaskInstance的actorId属性,主要用于获取某个actor的所有任务列表。设置方法在定义中描述actor-id属性,或者调用TaskInstance.setActorId(String)方法,或者在AssignmentHandler中调用assignable.setActorId(String)方法。

Pooled Actors
用于定义所有的任务参与人(candidates for task instance)。在查找某个参与者可参与的任务时使用,其实就是一个actor的集合,jBPM通过TaskMgmtSession.findPooledTaskInstances(String actorId)或者TaskMgmtSession.findPooledTaskInstances(List actorIds) 来查找任务集合。
注意:actor的优先级比pooled actors要高,所以如果同时设置两者,那jBPM只会用actor来查找,而忽略pooled actors。

一句话: actor被用来获取某个参与者的个人任务列表,pooled actors用来获取某个参与者可以参与的所有任务列表,或者说组任务列表。
使用这个actor集合的初衷就是为了不让任务分配和角色体系耦合。流程定义中始终都是只有参与者的名称和集合,而不存在用户,角色和用户组的具体设计。而使用Expression就耦合了这些信息在流程定义里。

Expression
如果你使用jBPM的identity component的话。你可以使用表达式定义的任务分配模式。通常这个表达式的形式是:

<assignment expression=’previous –> group(hierarchy) –> member(boss)’ />
我看了之后也不是很清楚,group和member是jBPM的identity componenet的概念,这个表达的意思大概是:使用之前的actor,然后在hierarchy组,并且member(角色)是boss的所有actor。这种方法仅限于使用jBPM的IC时使用。

Handler

指定你自己的AssignmentHandler实现。该接口只有assign方法。这种任务分配方式主要用于运行时的任务分配,而不是hardcode到定义文件里。使用场景,如果不想让原本应用中的访问控制与jBPM耦合起来,就可以使用这种方法来将jBPM中的任务分配代理出来。当然更好的定义是用swimlane,这样可以让定义文件看起来不是很凌乱。

看板在行动(Kanban in Action)

作者:DavidAnderson 原文
翻译:ZhangV 2007-11-26

之前,在讨论我在corbis所使用的软件工程管理方法时 ,我曾提到引入看板系统来维持项目运行。因为它的引入,我们现在每个月平均发布两个新的版本。然而,这些并没有使用传统的敏捷开发方法如两周迭代(two week iteration)或快速迭代(sprint)。而是用看板系统来管理变更请求(CR-Change Request)。当CR被完成时,它被设在准备发布状态,并在每周三被发布。

尽管我们让没有用Visual Studio的开发人员使用了outlook的teamlook客户端联入Team Foundation Server跟踪所有CR,但基本上的日常工作,我们都是使用白板作为看板来跟踪所有CR,把Post-It当作看板卡。

我们的支持过程是由一组常见的软件工程资源驱动的,没有一个专职的支持维护团队。通过设置看板,我们可以向管理层保证 – 我们正在履行我们的承诺,我们使用了一定的资源来进行支持活动,而不需要特别指出是谁在负责。

每天早会上所有团队成员会围绕看板来进行当天的工作安排,讨论进度和当前存在的问题。达伦戴维斯,作为项目经理,会主持会议,并分析每张卡片并与其他成员讨论。每张卡上写着CR的标题以及ID,及负责人的名字。每个负责人有责任根据当前CR的进行阶段移动相应CR的卡片。达伦利用每天的早会来保证Team Foundation Server上的数据和看板是同步的。该看板系统基本上是自我组织(self organizing),但整个团队每天都会验证它的有效性。

一些关键概念。红星 – 这个任务已经严重超时的,超过28个工作日服务水平协议( SLA),通过该系统。这使”迟到”的项目能够自我加速,不需直接管理干预。粉红色卡表示这个CR在Team Foundation Server里还是open的。这些粉红色卡包含了描述信息和在Team Foundation Server里的ID 。还有一些其他类型的卡片:黄色-客户提出(customer-valued)的CR;蓝色-客户提出(和要求)的BUG;绿色-IT维护项,如:的sql 2005的升级;橙色-新增(或额外)的BUG;粉红-issue。

看板限制卡片最多只能有20张,分为不同阶段,系统的分析,开发,测试,构建/合并,部署。此外,我们也允许有3个bug可以放在任何地方。这样可以有效地利用闲散资源。当人手不够的时候,我们会减少这个数量,或者干脆不要。此外,还有两个维护项,这让我们能解决固定分配一些资源到大大系统维护和升级,例如API版本升级,与平台的升级。 如net2.0或sql server 2005 。

看板赋予我们三个成功要素:

  • 减少“进行中(work-in-progress)”, (讽刺的是,这些事情本身会限制工作的完成)
  • 量入而出(如果有新的CR,首先看看板上有没有足够的空间)
  • 确定优先顺序

我们每周和公司的技术总监开会讨论,决定任务的优先等级。他们从后台日志中拿出新的CR并考虑怎么把他们配置到看板上。这迫使他们思考哪些是最重要的事情。看板强迫他们给出优先等级。

看板还把我们从定时迭代的压迫(constraints of time-boxed iterations)中解放出来。尽管我们现在每两个星期发布一个新版本,大的任务可以在系统中最多可以存在60天。那些对于“一(两)周迭代”来说太大的任务同样可以放到看板里,而不需要特殊对待。

最后,看板解放了我们,我们不再把每个迭代都当作一个小型的项目而投入过多的管理成本它基本上是自行组织和管理,除非有特殊情况出现,如需要加快请求或因环境或系统的维修问题变更发布时间。

technorati的标签:敏捷,朱+安德森,精益生产,看板,软件+工程

顺便问一下customer-valued怎么翻译?

最后修改:2009-8-12

AOP也是注入?

如果IOC是运行时对象依赖关系的注入的话,那么AOP是不是就是运行期执行流的注入呢?

每个注入点(point cut)就对应IOC中的sette或构造函数参数.每个处理方法(advice)就对应IOC中的一个对象

Java桌面应用程序发布

java桌面,java桌面应用,桌面应用发布,java,exe,jsmooth,installer,安装包

最近用Java做了一个单机桌面程序, 就是想看看用Java开发桌面程序到底能够达到什么样的程度.

通常网上介绍的开发桌面程序(这里没有特指则都指用Java开发桌面程序)都是说用工具打包成一个jar,然后通过配置MANIFAST.MF来配置主程序和第三方类库.以后就可以通过双击jar使用本地的jvm来执行.

我这里在用一些工具把jar包装成exe,再把依赖的资源文件和exe一起发布为windows安装包.
使用的工具:
ant/eclipse – 把程序打包为jar
proguard4.0beta -混淆
jsmooth0.9.9 – 把jar打包为exe
HM NIS Edit2.0.3 -制作安装包程序

一个完整的发布过程应该还包括:加密,签名,这里暂不涉及。
下面我详细的说一下这四个步骤

第一步:把程序打包为jar
首先用ant或者eclipse打包程序为可执行的jar。也就是要在MANIFEST.MF指定main class,然后如果又第三方库的话还要指定Class-Path,如:
Manifest-Version: 1.0
Main-Class: com.modofo.mobarbr.Launcher
Class-Path: lib/ibatis.jar lib/spring.jar lib/commons- logging.jar lib/quaqua.jar lib/commons-dbcp.jar lib/commons-pool.jar lib/alloy.jar

注意:
1.Manifest-Version必须要指定,否则MANIFEST.MF文件将无效,也就是和没有一样
2.Class-Path里每一个jar要以一个空格隔开
3.如果用Eclipse打包的话,最好是使用自己写一个MANIFEST.MF,而不要让Eclipse自己去生成,因为自动生成的无法设置Class-Path
4.Class-Path中的路径指的是打包好的jar包执行所在的路径的相对路径,好像有点表达的不清楚:),例如
如果你打包好的jar叫modofo.jar并放到了D盘的根目录下,那么你需要在D:/lib中放入那些第三方依赖包,这样你的jar才能执行。
(我一开始以为是要把这些也打包到jar里)
第二步:混淆
使用proguard4.0beta,这是一个很强大的优化混淆工具,使用很简单,可以用。不详细介绍。还有一个国内的Java混淆器,感觉不是很好用,可能是不会用吧。
我的程序因为用到了spring,所以混淆起来不太方便,后来也就没有混淆。看来用spring做桌面程序有点问题,DI的信息都暴露了?有没有把文本文件也混淆掉的工具呢?-这个似乎把问题搞复杂了。-为什么要混淆呢? – 2007-11-11

第三步:把jar打包为exe
jsmooth,最新版本0.9.9,
可以设置依赖包的位置,jre的位置,这样你可以同时在制作安装包时捆绑一个jre,用你随同程序发布的jre。不过这样的代价是在发布的时候把jre也打包进去,80多兆阿!
jsmooth同时支持ant,你可以把这个过程也整合到build.xml里不需要每次都手动生成

<taskdef name=”jsmoothgen” classname=”net.charabia.jsmoothgen.ant.JSmoothGen” classpath=”resources/jsmoothgen-ant.jar” /> –定义task,指定路径
<target name=”genexe” depends=”archive”>
<copy todir=”${dist.dir}/db”>
<fileset dir=”db” />
</copy>
<copy todir=”${dist.dir}/lib”>
<fileset dir=”lib” />
</copy>
<jsmoothgen project=”resources/mobarbr.jsmooth” skeletonroot=”resources/skeletons” /> –必须要指定jsmooth配置文件,是你用jsmooth的配置程序生成的文档,skeletons是jsmooth用到的骨架文件,必须要指定
</target>

打包之后的文件路径结构:
projectroot
|_dist
| |_lib/*.jar
| |_db/*.*
| |_jre/*.* (如果有jre,你需要把jre所有文件copy到这里)
| |_myapp.jar
| |_myapp.exe
|_build.xml

这个时候如果把dist下的文件做成压缩包就可以发布了,绿色且跨平台的。如果这个已经满足了要求,下面的可以不用看了 。

第四步:制作安装包程序
NSIS是一个很很牛x的制作安装程序工具,但是它是要自己编写脚本。我懒,但我知道我不是第一个懒人,所以在sf上找到了 HM NIS Edit,可以通过一个配置向导直接生成NSIS脚本,进而生成安装包,向导很简单基本不需要看帮助,试几次就应该可以用的很熟了。
生成好的安装包在安装时可以选择安装的component,可以配置jre作为可选组件,这样用户在安装你的程序时可以选择不安装jre。
NSIS还会自动帮你生成卸载程序。
这样一个 myapp-setup.exe文件就生成了,运行起来和一般的window程序几乎没有差别。(启动稍稍慢一点),但它只是在window下使用的。

此外还试用了其他的安装包程序:
advanced installer:很强大,但是收费,推荐
installjammer:开源,但安装过程不支持中文
小兵安装包制作工具:国产,但收费
NSIS开源社区最流行的打包程序,需要学习脚本,但有NIS Edit,一切就简单了。
上面的过程基本没有涉及很细节的地方,如果要细节的话每一步都需要一篇详细介绍还要截图(都说了我很懒了),我觉得最重要的是工具的使用,怎么在各种各样的工具里找到最适合你使用的。
如果有问题可以一起探讨

如何实现改变JTable中的曾选中过的cell的背景

 

2006/5/14

扩展一个JTable,增加一个List记录每次点击选择的cell,将其index记录.然后用一个Renderer,如果cell被选择过,则修改背景颜色为红色.使用时候先实例化一个MyTable,在setCellRenderer.

public class MyTable extends JTable {

private ArrayList selected;

public MyTable() {
super();
selected = new ArrayList();

}
//将选中的cell列,行数记录
public void addSelected(Point p){
if(selected.contains(p)){
selected.remove(p);
}else{
selected.add(p);
}
}

//某个cell是否被记录过

public boolean hasSelected(int row, int column) {
return selected.contains(new Point(row,column));
}

}
实现一个renderer,当cell的index在记录历史中,则修改其背景

public class MyRenderer extends DefaultTableCellRenderer{
public MyRenderer(){
super();
setHorizontalAlignment(SwingConstants.CENTER);
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
MyTable mt = (MyTable)table;
if(isSelected){
mt.addSelected(new Point(row,column));
}
if(mt.hasSelected(row,column)){
this.setBackground(Color.RED);
}else{
this.setBackground(Color.WHITE);
}
return this;
}
}

啥是Spring

(最初发布在JavaResearch.org,现在整理好放到自己的地方.)

有人说spring aop+ spring ioc, 才~~~是spring
简单一点,是一个容器. 什么容器,容纳什么?是对象,或者说bean的容器.
那为什么叫轻量级容器呢?相对于EJB container,使用spring不需要写符合容器规范的代码,即容器不会”侵入”了你的代码.

这 个容器会提供你的应用(程序)中用到的所有对象,并对这些对象进行统一的生命周期管理和组装.在通常的开发中,我们在需要某个对象的时候只是 new MyObject(). 在Java中,这样没有什么不好,因为gc会打理好”善后”工作,是系统级的. 而用spring,在需要某个对象 时,只要向容器请求相应的对象,spring会找到并准备好这些对象并提供给你.她也会打理好”善后”工作,但是是在应用级的.
另一方面,spring还会帮助你打理对象之间的依赖关系.
比如原来的做法:

class A{
}
class B{
A a ;
public B(){ a = new A();}
}

而使用spring的做法
class A{
}
class B{
A a;
public B(){}
void setA(A a){this.a=a}
A getA(){return this.a}
}
(希望你不要单纯地认为spring会写很多代码)
但 从前一个方面,你可能觉得spring只是一个对象容器.从这里你就应该看出,spring是bean容器,因为spring需要你的类符合bean规 范:相应于每一个成员域,都需要提供setter和getter方法.spring要使用这些方法来注入依赖关系,也就是 dependence injection, 或者inversion of control. 我个人觉得还是di更容易理解,直到现在我还是要考虑 怎么去向别人很好的解释ioc.控制反转(倒转),我的理解是就如同上面的两个例子里看到的,依赖(控制)不在体现在代码逻辑里(如第一个例子),而是在 配置文件里,而在代码中我们只提供注入点(也就是setter和getter).

希望我对IoC的概念的讲解能够给你一些启发.
你 可能要问了,为什么我要这样做呢?原来的做法有什么不妥的地方么?没有什么不妥,只是两种理念而已,没有绝对的好还是不好,但我还是给你我的解释–我理 解的IoC的好处,希望有所帮助.通常在程序设计的时候,我们在需要某些功能时,会相应的去设计一些方法,然后根据OO去将方法和一成员变量组成一个类. 实际上,我们最终设计出的程序是:一组类的实例互相交互完成某个特定的任务.
除了一些核心的业务方法,以外我们还要做组装对象的工作.比如我有了 一个工厂,里面有很多机器,机器在开动时要装配相应的模具.那么在工厂的生产过程中, 首先我要有工厂,机器,模具这样三个类.然后我的”动作”有:装 配,开机.通常的做法我们要做装配,然后再去开机.而用spring,我们只是专注于开机.这样我们就把装配这个动作抽离出了核心的”生产过程”.当某些 机器改变了装配模具时,不在需要修改核心业务代码.这就是解耦.如:

public class Production{
public static void main(String[] args){
Factory factory = (Factory)BeanFactory.getBean(“factory”);
factory.launchProduction();
}
}

class Factory{
Machine machine1,machine2;
void launchProduction(){
machine1.start(); machine2.start();
}
// setters and getters
}

class Machine{
Tool tool;
void start(){
}
// setters and getters
}

在launchProduction()方法中只需要开动每台机器即可.而不需要每次都装配机器.装配的工作交给了别人.现在只要按下start按钮.生产就开始了!要是原来:

void launchProduction(){
machine1 = new MachineA();
machine1.setTool(new ToolA());
machine2 = new MachineB();
machine2.setTool(new ToolB());
machine1.start();
machine2.start();
}

这就是工作分工,是不是感觉轻松了许多?从此以后,我们都是面向构件去开发,而不需要过多地在代码中体现构件之间的依赖关系.

AOP
推荐你看一下<<effective enterprise java>>的第一章,对AOP有很清晰,易懂的解释.其实AOP并非很艰深晦涩的概念,但是从架构角度去理解她的重要性可能不是我这样的new fish一时半会儿可以领悟到的.
我这里只是想说,有些概念你要知道是怎么回事,但理解到多深,除了天赋以外更多的是经验和悟.所以不要心急.–像是在自我解嘲.
也许在不知不觉中你就使用了很多AOP的概念,比如servlet里的filter,比如在写一个command类时,给她的调用类在每次调用command时前后加上:preProcess和postProcess…
我不想解释太多,<<eej>>的解释已经足够.

Hope helps.

OSGi 技术

作者:OSGi.org
翻译:ZhangV 2006-03-13
原文出自:OSGi.org
原文链接:http://www.osgi.org/osgi_technology/index.asp?section=2

简介

OSGi 规范为网络化的服务定义了一个标准的,面向组件的计算环境.给网络设备添加OSGi服务平台,使之可以有能力在网络的任何地点管理软件组件的生命周期.组 件可以在运行期间被安装,更新或删除,无需打断设备的其他操作.软件组件是可以动态查找和使用其他组件的库或程序.软件组件可以通过购买或自行开发. OSGi联盟已经开发了许多标准的组件接口,诸如HTTP服务器,配置,日志管理,安全,用户管理,xml等等这些常用功能.可以从不同的提供商那里获得 这些即插即用的兼容实现.

软件组件体系正面临一个日益凸现的问题:大量需要开发和维护的配置.而标准化的OSGi组件体系极大的简化了这个配置过程.

框架
OSGi规范的核心组件是OSGi框架.他为应用提供了一个被称为包捆(bundle)标准环境.OSGi包括这样四层:
L0.执行环境
L1.模块
L2.生命周期管理
L3.服务注册表
此外还包含一个安全系统.

执行环境就是Java环境.J2SE,CDC,CLDC,MIDP等都是合法的执行环境.OSGi同时根据核心定义(foundation profile)和一个最简版本制定了一个标准的执行环境.
模块层定义了类装载策略.OSGi框架是一个强大的类装载模型.以Java为基础但加入了模块化的思想.在Java中,只有一个包含了所有的可用类和资源类路径.OSGi的模块层还为一个模块提供了私有类以及模块之间的连接.
生命周期层将包捆动态地安装,启动,停止,更新和卸载,包捆依赖于模块层的类装载但提供了一套API来在运行期管理所有的模块.扩展的依赖机制用于确保正确模块依赖关系.
服 务注册表为包捆提供一个互操作模型实现动态性.包捆可以通过传统的类共享来互操作,但是类共享在当动态安装和卸载代码时会表现得很不稳定.服务注册表提供 了一个易于理解的模型来在包捆之间共享对象.大量的事件被定义于处理服务之间的交互.服务如同Java对象一样,可以代表任何事物.许多服务和对象是一样 的,像http服务器,服务就是现实中的一个对象,比如一个附近的蓝牙电话.

安全是建立在Java和Java2安全模型之上.语言级的限 制避免了许多可能出现的隐患(The language by design limits many possible constructs). 比 如,病毒常用的缓冲区溢出是不可能的.访问修饰符可以限制代码的可见性.OSGi通过”允许私有类”扩展了这一模型,在标准Java中不包含这种机制. Java2安全模型提供一个易理解的模型来检查代码对资源的访问权限.OSGi加入了完全的对访问请求动态管理.

标准服务

在框架之上,OSGi联盟制定了许多服务.服务由一个Java接口定义.包捆可以实现这个接口并把实现注册到服务注册表.服务的用户可以从服务注册表中找到他,或者对服务的加入或取消做出反应.
以下各部分给出OSGi release 3服务的一个简单介绍.更多资料可以找OSGi服务平台release 3的书籍或PDF下载.要注意每个服务都是先被抽象地定义然后由不同厂商独立实现.

框架服务

OSGi 框架提供一个权限管理服务(permission admin service ),一个包管理服务(package admin servie),和一 个启动级别服务(start level service).这些服务是可选的 and direct the orperation of the Framework(不知道怎么译).
• 访问管理(Permission Admin) – 当前或将来的包捆的访问权限可以通过这个服务来操作.
• 包管理(Package Admin) – 包捆之间共享类和资源.包捆的更新可能需要系统重新更新包捆之间的依赖关系.该服务提供系统实际包的共享状态并刷新共享的包.如,取消或者重新计算依赖关系.
• 启动级别(Start Level) – 启动级别是一个包捆的集合,这个集合中的包捆必须一同运行或者要在其他服务启动之前初始化.启动级别服务设置当前的启动级别,制定某个包捆的启动级别,查看当前设置.

系统服务

系 统服务提供横向功能(horizontal function),这些在每个真实的系统中都是存在的.比较常见的有,日志服务 (Log Service),配置管理服务(Configuration Admin Service),设备访问服务 (Device Access Service),用户管理服务(User Admin SErvice),IO连接服务 (IO Connector Service)和参数选择服务(Preferences Service).
• 日志服务 – 信息,警告,调试信息的纪录,或者处理错误.他接收日志纪录信息然后分配他们给其他订阅了这些信息的包捆
• 配置管理服务 – 该服务提供一个易伸缩的动态模型来设置和获得配置信息
• 设备访问服务 – 设备访问是配置一个驱动器到一个新设备并自动下载实现该驱动器的包捆的机制
• 用户管理服务 – 该服务使用一个用户信息数据库(私有和公有)来鉴权和授权.
• IO连接服务 – 实现了CDC/CLDC的javax.microedition.io包作为一个服务.该服务允许包捆提供新的,可选协议方案.
• 参数选择服务 – 提供属性数据库的访问.和Windows的注册表或Java的Preferences类.

协议服务
OSGi联盟定义了以下外部协议服务:
• Http服务 – 一个servlet容器.包捆可以提供servlet.OSGi的动态更新机制服务平台使得Http服务变得非常诱人,加入新的servlet而不需要重启.

• UPnp 服务 – 通用即插即用(Universal Plug and Play)是一个正在兴起的用于用户电子设备的标准,OSGi UPnP 将一个 UPnP网络设备映射到服务注册表.或者,将一个服务映射到UPnP网络(该服务在release 3中被推荐).
• Jini服务 – Jini是一个网络协议,用于在网络上发现Jini服务并下载执行.(该服务在release 3中被推荐).

其他服务
• 关联管理服务(Wire Admin Service) – 通常包捆会自己建立查找服务的规则.但是在很多情况下,这是一个发布时需要考虑的问题.因此关联管理服务会通过一个配置文件把不同的服务连接起来.使用消费者和生产者的概念来达到对象之间的互操作.
• XML解析服务 – 允许一个包捆定位一个解析器并使用相应的属性.与JAXP兼容.

结论
OSGi规范的适用范围很广.因为它通过一个很简单的层使得同一个虚拟机的Java组件高效率的交互.通过一个扩展的安全模型来使组件运行在一个安全的环境中.通过恰当的访问限制,组件可以被重用和组合.OSGi 框架还提供了一个扩展的部署机制来让这种交互可行,安全.

各 个领域构建在OSGi之上的中间件的大量出现,这就给OSGi软件组件创造了一个很大的软件市场. The rigid definition of the OSGi Service Platform enables components that can run on a variety of devices, from very small to very big.

采用OSGi规范可以降低软件开发成本,同时提供新的商业机会.

进一步阅读
OSGi Service Platform, Release 3下载:
http://www.osgi.org/osgi_technology/download_specs2.asp?section=2
或买书:
https://secure.inventures.com/osgi/sales/R3Book_order_form.asp

OSGi联盟的技术白皮书:
http://www.osgi.org/documents/osgi_technology/osgi-sp-overview.pdf
更深入介绍OSGi的白板白皮书(whiteboard whitepaper):
http://www.osgi.org/documents/osgi_technology/whiteboard.pdf