随笔 - 11, 文章 - 1, 评论 - 20, 引用 - 0
数据加载中……

2005年12月5日

小经验两则

1.Oracle 8i 下使用最新的oracle thin driver时用DatabaseMetaData获取主键等信息时,需要将
connection.getMetaData().getPrimaryKeys(connection.getCatalog(),null,tableName);
中的tableName转为大写,否则无法得到数据。

2.正则表达式中,需要以","分割字符串,但是要分割的字串中含有","号,为了避免冲突,引入前置转义字符"\",这样的正则怎么写呢?
例如:
String txt = "STATE_COUNTY=kj\\\\,,ADDR_LINE1=l=j,ADDR_LINE2=mj\n\n,ADDR_LINE3=n\\,o,\n\nADDR_LINE4=\np";
需要把键值对切分出来:
 Pattern.compile("[^\\\\],)");
这个是不行的,会将","号前一个字符消耗掉。

 Pattern.compile("(?![\\\\]),)");
也不行
Pattern p = Pattern.compile,",(?![\\\\])");
倒是可以,但是把转义字符放后面似乎有点诡异。
找了一个折衷办法,不切割使用正则获取"键=值"子串:
Pattern p = Pattern.compile("\\w+\\s*=.*?[,]*.*?(?=,|$)",Pattern.DOTALL);
但是还是带来了子串中不能含有"="的问题。
最后查了一个JDK1.4 DOC,发现了一个反向的非匹配串写法:
Pattern p = Pattern.compile("(?<!\\\\),\\s*");
这样一来就解决了以上问题。

posted @ 2006-08-03 09:54 wolfsquare 阅读(549) | 评论 (0)编辑 收藏

回复 乱弹权限系统续一

乱弹权限系统续一
原文在这:http://www.blogjava.net/RongHao/archive/2006/07/03/56258.html

仔细分析一,二,三,四权限背后的实质可以发现:
一系统权限的概念有一些冗余,很难想象这样一种情况:你已经有了子系统下的很多权限,结果因为没有模块权限而使得无法使用该模块进行任何操作,分配权限的人要非常小心才行.这个世界已经够复杂了,不要再给开发,部署人员增加复杂度了.很明白的,这个权限是不需要资源的权限
二数据库操作权限的概念,有一点疑惑,不知道为什么要建立这样的一个概念,和行级权限有什么区别呢? 从你的上下文理解来看,似乎是这样子的:有操作X表的业务,如果用户有增加权限,则可以任意增加数据,如果用户有编辑权限,则可以编辑任意数据.实际上对应标准权限模型为:不需要限定资源的操作,即不需要资源标识的权限.
三行级数据权限,这个概念很直白,对应标准权限模型就是: 资源(行数据)+操作
四列级数据权限,由于不是针对某特定行数据,所以它也是无资源型权限
就这样,所有的权限最终可划为需要资源标识和不需要资源标识,换句话说,所有权限可划分为控制某些集合的权限和控制单体的权限两种,在某些时候,也称之为 功能权限和数据权限


谈到把权限分给别人,很自然的就是如何控制权限的权限的问题了,很拗口,是吧?仔细想想,这样很直观,也没有什么后遗症,权限自递归控制和自解释,真是一个完美的循环.
有爱思考的同学想深了,会觉得非常麻烦,难实现.当然,概念上一回事,具体实现上可以是另一回事,可以做很多的变通来达到目的.只要保持概念上的简单性,就足以使得非常多的人得以解脱了。

另外,作为架构设计者,非常非常不赞成动辄就把很底层的概念扯进高层设计中(例如行级,数据库什么的),很容易把自己和别人搞胡涂。
可以最近状态不好,要不好好blog一篇,8过,有句话怎么说来着:“都素那浮云而已。。。”

posted @ 2006-07-04 22:45 wolfsquare 阅读(1928) | 评论 (1)编辑 收藏

不完美的世界-看到了IOC工具的又一个发展方向

     摘要: 在本篇文章中,作者在一个系统的构建中深度地被各种配置逻辑所困扰,由此发现了IOC工具(如Spring,Nuts等)的又一个发展方向。  阅读全文

posted @ 2006-06-08 00:30 wolfsquare 阅读(1933) | 评论 (7)编辑 收藏

结合WebWork实现高复用度系统的探索(上)

需求: 某机构体系下,机构类型分为子公司,部门,人员等,以后可能在某机构或者其子孙机构下可能会再分出其他子机构类型,希望在增加新类型过程中,尽可能的避免修改已有代码。

情况:子公司,部分,人员等已完成所有编码(界面,商业逻辑,数据逻辑)
变化:需要把这个机构体系组成为一颗树状结构
策略:鉴于除了树结构外的其他部分代码已经完成,那么应该首先保持这些代码不予改动。复用修改的优先级从高到低的顺序如下:
  界面×JSP,Action层
  商业逻辑 Service层
  数据逻辑层
  数据物理层
有经验的人知道,大部分情况下,越是下层的改动,越是影响越广泛(注意不是修改难度),所以我们只有在无计可施的情况下,才进行低层的修改。

分析: 回到我们的需求,从功能上看,维护一个组织机构的需求,已经涵盖了每一个子结构的维护需求,以部门的建立为例,在新建一个部门时,同时也必须建立机构树上的节点,
 这样,如果需要直接使用原有的创建部门的所有代码,需要在其上加上创建组织机构所需要的父节点,以及当前节点名称信息(在这里department的增加界 面JSP是需要修改的,不过实际上我没有修改该文件,而是利用DHTML来动态加入需要新增加的信息),然后提交给原创建部门的URI (departmentSave.action)和组织机构创建URI(orgCreate.action),在这里我们利用ww提供的action chain功能来完成这两个操作。
 这里需要修改department.action的配置,拦截save方法使其执行完后跳过原来的relist结果页面转向组织结构的创建orgCreate.action:
 <action name="unitSave" class="com.wolfsquare.ibase.org.action.UnitAction" method="save">
   <result name="input">/org/unit/input.jsp</result>
   <result name="relist" type="chain">
       <param name="actionName">orgCreate</param>
                <param name="namespace">/org</param>  
            </result>
   <result name="xxx" type="redirect">/org/unit.action?start=${start}</result>
   <interceptor-ref name="validationStack"/>
  </action>
可能有同学看到这里会问:创建组织节点时应该还需要关联前面创建的部门对象啊,这个操作是如何实现的?信息是如何传递的?
在这里,由于整个架构体系并没有支持这种信息传递的功能,所以只好以一种比较”脏“的方式实现:
        在department.action类里增加了一个方法getModel()返回刚刚创建的部门对象,然后在org.action类中增加一个接收的方法setModel(object o)这样在整action chain执行的时候,ww会自动将getModel后的数据填入setModel中,这样做的后果是以后增加新的机构类型的功能时,action必须也照这样的语意设置getModel方法。(如果要解决这个问题,这能需要使用一个特定的Context,然后拦截指定Service的创建方法,把创建结果放入Context,不过这又带来如何清除Context的问题,于是又要求助与ww的interspector,专门写一个拦截器来擦屁股,够麻烦。。。)

        就这样,我们完成了新增,修改组织机构的功能合成,虽然有点拖沓,但是还是达到了复用,少修改原有代码,而且扩展性也很好的目标。这上篇说的是两个简单业务的功能揉合问题,下篇我们来看看稍微复杂点的情况,看看还能不能继续依葫芦画瓢来完成功能合的成
  
(未完待续)  

posted @ 2006-05-17 23:40 wolfsquare 阅读(1213) | 评论 (0)编辑 收藏

log4j配置简要说明

虽然以前一直在用log4j,但是对其配置不甚了了,突然间因为需解决某些问题,要理解log4j的配置,
然而用google搜了一下,却发现网上没有一个简单直观的说明,于是只好看log4j的官方介绍,终于
理解了log4j的配置用法,以下是我对log4j配置的一点认识,如有谬误还请不吝赐教.

首先我们搞清楚log4j能干什么,简单来说就是提供一个记录不同级别信息内容的日志工具,
可以把不同级别,不同包路径的信息,以指定格式输出到多种设备(控制台,文件等)
在程序中,可以以以下方式来使用
   Log log = org.apache.commons.logging.LogFactory.LogFactory.getLog(yourClassName.class);
  log.debug("debug message -------------------");
  log.info("info message ******************");
  log.warn("warn message +++++++++++++++");
  log.error("error msg=================");
  
本文主要讲的是如何配置log4j,先让我们先看看一个典型的log4j配置:  

==========log4j.properties==================

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{MM-dd HH\:mm\:ss.SSS} %-5p [%F\:%L]%x %m%n

log4j.appender.fileout=org.apache.log4j.RollingFileAppender
log4j.appender.fileout.File=D:/workspace/log4jtest/log/application.log
log4j.appender.fileout.MaxFileSize=10000KB
log4j.appender.fileout.MaxBackupIndex=10
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
log4j.appender.fileout.layout.ConversionPattern=%d{MM-dd HH:mm:ss.SSS}[%24F:%-3L:%-5p]%x %m%n

log4j.rootCategory=INFO, stdout, fileout
log4j.logger.com.wolfsquare.log2=DEBUG,stdout
===================================

这个文件可以划为三小块

===========第一块定义了一个名为 stdout 的appender和layout (appender,layout的概念后面再解释,目前先记着有这样两个名词):

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
定义stdout的实际输出实现类,从这个appender实现类名可以猜到,这个类是负责控制台输出的。
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
定义stdout的输出装饰器
log4j.appender.stdout.layout.ConversionPattern=%d{MM-dd HH\:mm\:ss.SSS} %-5p [%F\:%L]%x %m%n
装饰器参数配置


============第二块定义了一个名为 fileout 的appender和layout:
log4j.appender.fileout=org.apache.log4j.RollingFileAppender
同理可猜这个实现类是输出到文件的
log4j.appender.fileout.File=D:/workspace/log4jtest/log/application.log
log4j.appender.fileout.MaxFileSize=10000KB
log4j.appender.fileout.MaxBackupIndex=10
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
log4j.appender.fileout.layout.ConversionPattern=%d{MM-dd HH:mm:ss.SSS}[%24F:%-3L:%-5p]%x %m%n

============第三块定义了名字分别为rootCategory,log4j.logger.com.wolfsquare.log2的两个logger
log4j.rootCategory=INFO, stdout, fileout
log4j.logger.com.wolfsquare.log2=DEBUG,stdout

rootCategory logger是缺省的logger,记录所有的包的信息输出。
第二个logger是只输出指定包com.wolfsquare.log2下的日志信息。
那么INFO,DEBUG又是什么意思呢,他们是信息的分级标识,通过继承实现这个实现自定义级别的分级。
第三块配置两句的意思是这样的:
rootCategory 把所有类的INFO级别以上的信息输出到stdout和fileout两个appender中,
logger.com.wolfsquare.log2,把com.wolfsquare.log2包中的所有类(包括子包)DEBUG级别(含)以上的信息输出到stdout 中
一个logger可以输出到很多个设备中(appender),如果需要增加输出设备则用分号分隔开appender名称即可。

输出信息的分类级别是DEBUG > INFO > WARN > ERROR,信息细节由细到粗,指定输出某一级别的信息时,
过细的信息输出将会被忽略

如果一个配置中有多个logger,他们之间会有什么关系呢?答案是,在输出上,他们没有任何关系,都是独立运作的,
不相关的,但是在配置上,父包的配置会传给子包,如果子包没有另外定义配置的话。
例如上面配置文件中的两个logger:
log4j.logger.com.wolfsquare
log4j.logger.com.wolfsquare.log2

这里认为 log4j.logger.com.wolfsquare.log2 继承自 log4j.logger.com.wolfsquare,他们的配置声明如下:
log4j.rootCategory=INFO, stdout, fileout
log4j.logger.com.wolfsquare.log2=,stdout
注意第二句没有指定输出级别,那么根据配置继承规则会继承父logger的配置,在这里就是INFO。

同时需要强调的是,如果两个logger有继承关系,且输出到同一个appender,根据输出独立原则,那么将会出现两行一样的信息,
例如上面的两个logger定义会导致这样的情况。
最后以一幅图来概括:

posted @ 2006-04-20 23:21 wolfsquare 阅读(972) | 评论 (0)编辑 收藏

spring配置中bean的循环引用问题及解决方法

问题:Spring+Hibernate的应用中,定义了两个业务Service,这里分别称它们为serivceA,ServiceB。
它们的关系简单点来说是这样的:
serviceA需要引用serviceB,在serviceB中定义了一个接口列表,serverA必须在serviceB初始化时设置进列表。
在纯bean的情况下,也就是这两个类不需要设置其他bean的情况下,循环引用是正常的,可以通过的。例如下面配置所表示:

    <bean id="serviceA" class="A"  autowire="byName"  lazy-init="true">
     <property name="serviceB"><ref local="serviceB"/></property>
    </bean>
 <bean id="serviceB" class="B"  autowire="byName"  lazy-init="true">
     <property name="serviceA"><ref bean="serviceA"/></property>
 </bean>
但是作为一个业务接口,它应该是不需要关心事务,回滚这些无关的东西,
但现实又有这样的需求,所以我们必须保证透明的实现这个功能,于是引
入了AOP方式解决该问题,利用的是Spring自带的org.springframework.t
ransaction.interceptor.TransactionProxyFactoryBean.
重新声明文件如下:
   <bean id="baseTxProxy" lazy-init="true"
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="proxyTargetClass"><value>true</value></property>
        <property name="transactionAttributes">
            <props>
  <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
     
    <bean id="serviceA" parent="baseTxProxy">
     <property name="target"><ref local="serviceAImpl"/></property>
    </bean>
   
   <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
     <property name="serviceB">
         <ref bean="serviceB"/>
     </property>
   </bean>
   
    <bean id="serviceB" parent="baseTxProxy" lazy-init="true">
     <property name="target"><ref local="serviceBImpl"/></property>
    </bean>
  
   <bean id="serviceBImpl" class="D" lazy-init="true">
     <property name="serviceA">
         <ref bean="serviceA"/>
     </property>
   </bean>
于是问题就出现了,Spring报了FactoryBeanCircularReferenceException,无法继续完成设置工作。
查看TransactionProxyFactoryBean源码,其实现了FactoryBean和InitializingBean接口,应该是
做了代理之后,两个代理Bean需要等待所有Bean设置完成后才会标识状态为初始化完毕,于是造成了
冲突。

    由于两个业务服务互相调用的路径是不相交的,所以采用了一种变通的方法,在声明serviceA时,
直接定义serviceB:
  <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
     <property name="serviceB">
         <bean class="B"  autowire="byName"/>
     </property>
 </bean>
相当于serviceB和serviceA中使用的serviceB不是同一个实例。
 
 但是如果确实调用重合时怎么办?
 
 解决方法是这样的:
 
 <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
     <property name="serviceB">
         <ref bean="serviceBImpl"/>
     </property>
 </bean>
 
  非常简单,serviceAImpl调用时,可能已经在事务环境中了,不需再使用serviceB代理的事务支持,
  于是直接引用serviceB实例。这个方法是我写这篇文章时想到的,-_-!!!,看来知识果真还是好好
  整理呀。

 

posted @ 2006-02-07 08:53 wolfsquare 阅读(2565) | 评论 (0)编辑 收藏

Spring+Hibernate+Websphere5.0经验一则

环境Spring1.1.3,Hibernate 2.1.8, Websphere5.01

hbm文件采用通配符获取:


  
  
   classpath:/**/*.hbm.xml
  

  
 

问题症状:

应用启动报错说不能重复定义某类,去掉该类后仍然报下一个类重复定义。

仔细查看Log输出发现,所有的hbm文件均找到了两份 -_-!!!

项目组认为应该是websphere不太厚道,在classpath中使用了多处目录(web-inf & classes),并以这些目录为根进行递归搜索匹配文件,可是如果这些目录有包含关系,WebSphere就没有处理重复查找的文件了。

于是在以上配置中改为:


  
  
   classpath:/classes/**/*.hbm.xml
  

  
 

问题虽然解决了,可是tomcat中却又无效了。 :(

什么时候,企业应用才能一次拷贝,到处运行啊~~

posted @ 2006-02-07 08:50 wolfsquare 阅读(643) | 评论 (0)编辑 收藏

修改Tds驱动Url声明解决两个Sql Server问题

问题1:JDBC Sql Server varchar的取出最大长度限制

环境: JDBC驱动inet tds驱动(版本不明),SQLServer2K

问题症状:对于数据库声明为varchar的长度大于256的字段,可以正常保存,但是无法取出多于256字符以后的内容

问题2:使用Hibernate映射时0长度字符串保存后,取出多加了一个空格

环境:inet tds驱动Hibernate2.1.8,SQL Server2K

问题症状:保存0长度字符串后,取出增加了多余的空格。

以上两个问题都是因为没有使用最新的通讯协议引起的,修改URL声明方式如下:

jdbc:inetdae7:127.0.0.1:1433?database=xxx

问题解决,收工。

ps:发现协议inetdae时,数据库字段为Null时,Hibernate取出声明为基本类型(例如boolean)的对象属性并不会报错,实际上在其他数据库如Oracle和新协议上是会报错的。为了避免此类问题出现,最好还是严格遵守:Hibernate声明对象的基本类型属性,一定不能在数据库端置为空值。

ps2:在解决以上问题中发现,Oracle居然对传人0长度字符串,会转为空值,不知道是为了节省空间还是别的什么理由。-_-!!!

全文完

posted @ 2006-02-07 08:49 wolfsquare 阅读(540) | 评论 (0)编辑 收藏

Java高精度打印

    在Java环境中,可以使用 java.awt.Toolkit.getScreenResolution()可以得到屏幕每英寸的象素数,但是好像没有什么方法能知道某一台打印机的分辨率,更别提去控制打印粒度了。于是可耻的使用着丑陋的缺省打印精度几年后,终于找到了解决方法,不知道该高兴还是悲伤,其原理说出来也是非常的简单:
    提高打印精度,其实就是把本来是A3纸的内容往A4纸里画,也就是说,打印区域(这里对应着Java里的Graphics对象)需要缩小,然后由于缺省情况下打印是照72DPI来打的,不做改变的话,打印内容也会跟着变小。这样就不是我们想要的效果了,所以还得把打印内容成比例放大。一个缩小,一个放大,于是画完后,在指定大小的纸张内,便容纳了比以往更多象素的内容,这下世界总算完美了。

    以上做法形象的说应该是这样:把需要产生的图形对象先放大,画在一张“纸上”,然后整体缩小,这样精度就提高了。

    tips 1:在一般企业报表表格打印中,使用144DPI得到的表格线的宽度看起来最舒服。
    tips 2:现在号称600DPI的打印机其实是576DPI,如果想使用这个分辨率的精度,需要用好一点的纸张,因为已经到极限了,纸张稍差点,打印墨粉就沾不上,导致线体残缺。

附源码(修改分辨率就改动变量iResMul就好):

 

import java.awt.*;
import java.awt.print.*;

public class MyPrintableObject implements Printable {
 
public int iResMul = 1// 1 = 72 dpi; 4 = 288 dpi

 
public int print(Graphics g, PageFormat pf, int iPage)
   
throws PrinterException {
  
final int FONTSIZE = 12;
  
final double PNT_MM = 25.4 / 72.;
  
if (0 != iPage)
   
return NO_SUCH_PAGE;
  
try {
   
int iPosX = 1;
   
int iPosY = 1;
   
int iAddY = FONTSIZE * 3 / 2 * iResMul;
   
int iWdth = (int) Math.round(pf.getImageableWidth() * iResMul) - 3;
   
int iHght = (int) Math.round(pf.getImageableHeight() * iResMul) - 3;
   
int iCrcl = Math.min(iWdth, iHght) - 4 * iResMul;
   Graphics2D g2 
= (Graphics2D) g;
   PrinterJob prjob 
= ((PrinterGraphics) g2).getPrinterJob();
   g2.translate(pf.getImageableX(), pf.getImageableY());
   g2.scale(
1.0 / iResMul, 1.0 / iResMul);
   g2.setFont(
new Font("SansSerif", Font.PLAIN, FONTSIZE * iResMul));
   g2.setColor(Color.black);
   g2.drawRect(iPosX, iPosY, iWdth, iHght);
   g2.drawLine(iPosX, iHght 
/ 2 + iWdth / 50, iPosX + iWdth, iHght / 2
     
- iWdth / 50);
   g2.drawLine(iPosX, iHght 
/ 2 - iWdth / 50, iPosX + iWdth, iHght / 2
     
+ iWdth / 50);
   g2.drawOval(iPosX 
+ 2 * iResMul, iHght - iCrcl - 2 * iResMul,
     iCrcl, iCrcl);
   iPosX 
+= iAddY;
   iPosY 
+= iAddY / 2;
   g2.drawString(
"PrinterJob-UserName: " + prjob.getUserName(), iPosX,
     iPosY 
+= iAddY);
   g2.drawString(
"Betriebssystem: " + System.getProperty("os.name")
     
+ " " + System.getProperty("os.version"), iPosX,
     iPosY 
+= iAddY);
   g2
     .drawString(
"Java-Version: JDK "
       
+ System.getProperty("java.version"), iPosX,
       iPosY 
+= iAddY);
   g2.drawString(
"Width/Height: " + dbldgt(pf.getWidth()) + " / "
     
+ dbldgt(pf.getHeight()) + " points = "
     
+ dbldgt(pf.getWidth() * PNT_MM) + " / "
     
+ dbldgt(pf.getHeight() * PNT_MM) + " mm", iPosX,
     iPosY 
+= iAddY);
   g2.drawString(
"Imageable Width/Height: "
     
+ dbldgt(pf.getImageableWidth()) + " / "
     
+ dbldgt(pf.getImageableHeight()) + " points = "
     
+ dbldgt(pf.getImageableWidth() * PNT_MM) + " / "
     
+ dbldgt(pf.getImageableHeight() * PNT_MM) + " mm", iPosX,
     iPosY 
+= iAddY);
   g2.drawString(
"Imageable X/Y: " + dbldgt(pf.getImageableX())
     
+ " / " + dbldgt(pf.getImageableY()) + " points = "
     
+ dbldgt(pf.getImageableX() * PNT_MM) + " / "
     
+ dbldgt(pf.getImageableY() * PNT_MM) + " mm", iPosX,
     iPosY 
+= iAddY);
   g2.drawString(
"versuchte Druckaufl sung: " + 72 * iResMul + " dpi",
     iPosX, iPosY 
+= iAddY);
  }
 catch (Exception ex) {
   
throw new PrinterException(ex.getMessage());
  }

  
return PAGE_EXISTS;
 }


 
private static double dbldgt(double d) {
  
return Math.round(d * 10.) / 10.; // show one digit after point
 }


 
public static void main(String[] args) {
  PrinterJob pj 
= PrinterJob.getPrinterJob();
  pj.setPrintable(
new MyPrintableObject());
  
if (pj.printDialog()) {
   
try {
    pj.print();
   }
 catch (PrinterException e) {
    System.out.println(e);
   }

  }

 }

}



全文完)   

posted @ 2006-02-06 21:44 wolfsquare 阅读(1710) | 评论 (2)编辑 收藏

基于拦截器的企业应用构造

    在上一篇文章里,我们使用了基于事件传递的机制来对企业应用的子系统进行解耦,但是由于需要强制地继承或者实现一个广播事件的接口EventBrocast,实际上,就职责分离和功能单一的角度来看,前篇文章中的例子中,这个机制对OrderService侵入太大了,我们必须寻找更为有效的方法,不需要程序实现某个接口或继承某个超类来完成这个工作,这一切必须对具体程序完全透明,这个责任谁能承担呢,毫无疑问,历史的重担就落在了AOP身上 ;) 。下面我们来看看具体的实现:
    OrderService已经实现,除了订单的处理,没有任何的职责,为了完成事件的广播,必须要有一个途径能够拦截到OrderService的所有方法调用,然后分析调用的语义(参数),并根据这些内容给广播出去。而恰好,AOP组织统一的接口MethodInterceptor可以完成这个功能。于是上篇文章的程序可以这样修改:

   // 订单服务只负责做好自己的事
  

 public class OrderService {
     
public Order saveOrder(Order order){
     。。。。处理订单
     。。。保存
     }
  }

 

  而为了拦截任何的方法调用,则实现了拦截器EventBrocaster:
 

public class EventBrocaster extends LifeEventBrocast implements MethodInterceptor  {
    
private List eventListeners;
    
public void setEventListener(List list){
     
this.eventListeners=list;
    }
    
public List geteEventListeners(){
     
return eventListeners;
    }
    
public Object invoke(MethodInvocation invoke) {
      obj 
= invoke.proceed();// 执行被拦截的方法完成业务操作
      Object[] params = invoke.getArguments();
     Object param 
= params.length > 1 ? params : params[0];
     Event le 
= new Event(param, eventType);
     brocast(le);
// 广播
    }
  }

 

  事件侦听器:
 

 public OrderEventListener implements EventListener{
  
private FinancialService  financialService;
   
public void setFinancialService(FinancialService fs){
     
this.financialService=fs;
   }
  
public void performed(Event e){
   Order order 
=(Order) e.getObject();
    financialService.createRequestOfMoney(order.getAmount());
  }
 }

 


  然后,在Spring配置里将这些组件全部连接起来:

 1.OrderService实现:
 <bean id="orderServiceImpl" class="OrderService" autowire="byName">
 </bean>

 2. 声明OrderService代理:

 <bean id="orderService" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="target">
   <ref local="orderServiceImpl"/>
  </property>
  <property name="interceptorNames"> <!--拦截器列表-->
   <list>
    <value>eventBrocaster</value>
   </list>
  </property>
  <property name="singleton">
   <value>true</value>
  </property>
 </bean>
  3.事件广播拦截器
 <bean id="eventBrocaster" class="com.wolfsquare.core.service.EventBrocaster" singleton="true">
  <property name="lifecycleListeners">
      <list>
       <ref bean="orderEventListener"/>
      </list>
     </property>
 </bean>
  4.具体的财务子系统的侦听器实现与财务系统的通讯:
  <bean id="orderEventListener" class="OrderEventListener" autowire="byName">
   <propety name="financialService"><ref bean="financialService"/></property>
 </bean>

    这样,我们与具体实现无关的事件广播就做到了,聪明的朋友看到这里,肯定想到了拦截器方式不仅仅适用与事件广播,还可以实现事务的统一管理,事实上Spring的事务管理就是这样完成的,还可以实现权限的控制例如Acegi,简直有点象万能的胶水,呵呵。

    从两篇文章的逐步探讨下,同一个机器,同一个虚拟机之内的数据通讯都可以实现了,那么异构系统和多虚拟机间的通讯又如何处理呢,于是ESB(企业服务总线)的概念就慢慢浮现出来了,不过这个不在本文探讨的范畴了,也许在不久的将来,我会补上这一篇。

(全文完)

 

 

posted @ 2005-12-06 20:49 wolfsquare 阅读(2809) | 评论 (6)编辑 收藏

基于事件分发机制的企业应用开发

    做过DOS编程的人都知道,Dos编程和Window编程最大不同之一就是事件机制的编程,普遍的,目前事件机制的使用已经在Windows下的应用程序中遍地开花了,可是基于事件传播的应用仅仅限于window应用程序吗?答案是:不。
    在IOC概念的不断冲击下,我们需要回头去审视前两年自己开发的,心中为之骄傲的,认为非常优秀的程序,在这过程中,我们就会看到这些骄傲在最新的概念冲击下烟消云散了。变成了丑陋的,紧耦合的反面范例。让我们来看看以下场景:

 某公司有基于某平台的两子系统,订购系统A和财务系统B。当采购员在预采购某物品前,先在定购系统A中登记需要购买的货物和价格,然后财务系统B就开始该笔购物款项的申请流程。
 首先让我们来看看两年前的代码是怎么实现的:

 

public class OrderService {
   
private FinancialService  financialService=new FinancialServiceImpl();
     
public Order saveOrder(Order order){
  。。。。处理订单
                financialService.createRequestOfMoney(order.getAmount());
   }

 }


从上面的代码中可以很明显看到,定购系统A和财务子系统发生了耦合,也许有同志说,那我采用Spring等IOC框架来解耦:
 

public class OrderService {
   
private FinancialService  financialService;
   
public void setFinancialService(FinancialService fs){
     
this.financialService=fs;
   }

     
public Order saveOrder(Order order){
  。。。。处理订单
                financialService.createRequestOfMoney(order.getAmount());
   }

 }


财务子系统B的实现FinancialServiceImpl是通过Spring等IOC框架设置进去的。这样不是很完美了吗?

但是我仍然要说,这个只是五十步笑百步罢了。再请看以下场景:该公司的业务规则起了变化,金额少于1万元的定购不需要通过财务申请流程。这样财务子系统B升级到B2了,多了一个直接拨款的API payMoney()。这时候,定购系统该怎么办?除了修改代码没有别的办法。这个时候,基于事件分发处理的机制就大派用场了。
  修改原来的订单系统实现下单消息的广播。
 

public class OrderService extend EventBrocast{
   
private FinancialService  financialService=new FinancialServiceImpl();
     
public Order saveOrder(Order order){
  。。。。处理订单
                brocastEvent(
new Event(order)); // ***
   }

 }

 

 实现一个消息监听器:

public OrderEventListener implements EventListener{
  
public void performed(Event e){
   Order order 
=(Order) e.getObject();
    financialService.createRequestOfMoney(order.getAmount());
  }

 }

 

 在配置中设置(这里我们使用的是Spring)
 <bean id="orderService" ...>
  <property name="eventListener">
   <list>
    <ref local="orderListener"/>
   </list>
  </property>
 </bean>
 <bean id="orderListener" ...>

 这样我们就实现了订单系统和财务系统的解耦,如果财务系统发生的修改,我们就只需实现新的监听器就可以了:

 

public  NewOrderEventListener implements EventListener{
    
private FinancialService  financialService;
    
public void setFinancialService(FinancialService fs){
     
this.financialService=fs;
   }


  
public void performed(Event e){
   Order order 
=(Order) e.getObject();
    financialService.payMoney(order.getAmount());
  }

 }

 

 下一篇文章,我将引入AOP的概念来将系统提高到更高的抽象层次。
 (全文完)

posted @ 2005-12-05 20:31 wolfsquare 阅读(2295) | 评论 (4)编辑 收藏