??xml version="1.0" encoding="utf-8" standalone="yes"?>国产av无码专区亚洲av桃花庵,亚洲精品无码不卡在线播HE,亚洲乱码无限2021芒果http://www.tkk7.com/lijiajia418/category/14361.htmlVicent's blogzh-cnWed, 28 Feb 2007 03:45:39 GMTWed, 28 Feb 2007 03:45:39 GMT60Log4J学习(fn)W记http://www.tkk7.com/lijiajia418/archive/2006/09/01/67072.htmlBinaryBinaryFri, 01 Sep 2006 05:25:00 GMThttp://www.tkk7.com/lijiajia418/archive/2006/09/01/67072.htmlhttp://www.tkk7.com/lijiajia418/comments/67072.htmlhttp://www.tkk7.com/lijiajia418/archive/2006/09/01/67072.html#Feedback0http://www.tkk7.com/lijiajia418/comments/commentRss/67072.htmlhttp://www.tkk7.com/lijiajia418/services/trackbacks/67072.html

一、简?br />  在程序中输出信息的目的有三:(x)一是监视程序运行情况;一是将E序的运行情况记录到日志文g中,以备来查看Q一是做试器。但信息输出的手D不仅限于System.out.println()或System.out.print()Q还有日志记录工具可以选择。与System.out.pringln()和System.out.print()相比Q日志记录工具可以控制输出别,q且可以在配|文件中对输出别进行设|,q样开发阶D늚信息在程序发布后可以通过讄输出U别来消除掉Q而无d代码q行修正?jin)。现在流行的日志记录工具很多Q?Log4J是其中的佼D?br />  Log4J是由著名开源组lApache推出的一ƾ日志记录工P供Java~码人员做日志输Z用,可以从网?a >http://logging.apache.org/log4j上免费获得,最新版?.2.11。获得logging-log4j-1.2.11.zip文g后,解压~,需要的是其中的log4j-1.2.11.jar文gQ将该文件放到特定的文g夹中备用Q我攑ֈ?jin)我机器的G:\YPJCCK\Log4J\lib文g夹中?br />  q里选择的IDE是Eclipse和JBuilder。Eclipse用的?.0.1加语a包,可以?a >www.eclipse.org|站上下载;JBuilder用的是JBuilder 2005?br />二、配|类?br />  下面打开Eclipse或JBuilder?br />  如果使用的是EclipseQ那么在Eclipse打开后,点击菜单"文g"->"新徏"->"目"Q打开"新徏目"对话框:(x)

请选中"Java目"Q点?下一?Q进?新徏Java目"对话框:(x)

在这个对话框中需要设|项目的名称以及(qing)目所在目录,我ؓ(f)自己的项目v名ؓ(f)Log4JTestQ目录ؓ(f)G:\YPJCCK\Log4J\Eclipse\ Log4JTest。设|好后点?下一?Q进入下一个窗口。在q个H口中选择名ؓ(f)"?的选项卡,然后点击"d外部JAR"按钮Q将保存于特定文件夹中的log4j-1.2.11.jar文g引用q来?/p>

讄好后Q点?完成"Q至此,已经具备?jin)在Eclipse下用Log4J的环境?br />  如果使用的是JBuilderQ那么在JBuilder打开后,点击菜单"Tools"->"Configure" ->"Libraries"Q打开"Configure Libraries"对话框:(x)

点击"New"按钮Q打开"New Library Wizard"对话框:(x)

使用"Add"按钮保存于特定文g夹中的log4j-1.2.11.jar文g引用q来Qƈ讄NameQ即该类库的名字Q我Name讄?Log4J。设|好后点?OK"按钮Q回?Configure Libraries"对话框,再点?OK"按钮Q则JUnitcd已经被添加到JBuilder当中?br />  下面l箋Q在JBuilder中创建项目。点击菜?File"->"New Project"Q打开"Project Wizard"对话框:(x)

在这个窗口中讄目名称?qing)存攄录,我的目名称仍?f)Log4JTestQ\径ؓ(f)G:/YPJCCK/log4J/JBuilder/Log4JTest。点?Next"q入下一个窗口:(x)

在这个窗口中选择"Required Libraries"选项卡,点击"Add"按钮Q将刚才讄的JUnit库引用进来。然后点?Next"按钮Q进入下一个窗口:(x)

在这个窗口中用鼠标点击Encoding下拉列表框,然后按一?G"键,选中相应选项Q此时该目的字W集p讄成GBK?jin)。如果做的是国内目Q这l对是个好习(fn)惯。最后点?Finish"Q项目创建完成?br />三、编写一个简单的CZ
  在了(jin)解Log4J的用方法之前,先编写一个简单的CZQ以对Log4J有个感性认识?br />如果使用的是EclipseQ请点击"文g"->"新徏"->"c?Q打开"新徏Javac?对话框,讄包ؓ(f) piv.zheng.log4j.testQ名UCؓ(f)TestQƈ保"public static void main(String[] args)"选项选中Q如果用的是JBuilderQ请点击"File"->"New Class"Q打开"Class Wizard"对话框,讄Package为piv.zheng.log4j.testQClass name为TestQƈ保"Generate main method"选项选中。设|完成后Q点?OK"。代码如下:(x)
  package piv.zheng.log4j.test;
  
  import org.apache.log4j.Logger;
  import org.apache.log4j.Level;
  import org.apache.log4j.SimpleLayout;
  import org.apache.log4j.ConsoleAppender;
  
  public class Test {
    
    public static void main(String[] args) {
      SimpleLayout layout = new SimpleLayout();
      
      ConsoleAppender appender = new ConsoleAppender(layout);
      
      Logger log = Logger.getLogger(Test.class);
      log.addAppender(appender);
      log.setLevel(Level.FATAL);
      
      log.debug("Here is DEBUG");
      log.info("Here is INFO");
      log.warn("Here is WARN");
      log.error("Here is ERROR");
      log.fatal("Here is FATAL");
    }
  }
xQ示例编写完成。请点击q行按钮旁边的倒三角,选择"q行?->"2 Java应用E序"QEclipseQ,或者在Testcȝ选项卡上点击鼠标右键Q在调出的快捯单中点击"Run using defaults"QJBuilderQ,q行E序Q观察从控制台输出的信息?br />四、Log4J入门
  看过E序的运行效果后可能?x)奇怪,Z控制台只输出?FATAL - Here is FATAL"q样一条信息,而程序代码中的log.debug()、log.info(){方法也都设|了(jin)cM的内容,却没有被输出Q其实答案很单,但在公布之前Q先来了(jin)解一下Log4J的用?br />  请先看前边的CZ代码Q会(x)发现Q示例中用到?jin)Logger、Level?ConsoleAppender、SimpleLayout{四个类。其中LoggercM用最多,甚至输出的信息也是在其对象log的fatalҎ(gu)中设|的Q那么LoggerI竟是做什么的呢?其实Logger是传说中的日志记录器(在Log4J中称为CategoryQ,创徏Ҏ(gu)有三Q?br />  1Q根CategoryQ默认创建,获取Ҏ(gu)Q?/p>

Logger log = Logger.getRootLogger();

  2Q用户创建的CategoryQ方法:(x)

Logger log = Logger.getLogger("test");

其中字符串test是ؓ(f)Category讑֮的名U。Category的名U允怋用Q何字W,但区分大写Q例如:(x)

Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("X");

l1和l2是两个CategoryQ而如果名U完全相同,例如Q?/p>

Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("x");

l1和l2是同一个Category。此外,W号"."在Category的名UC有特D作用,q一点将在后边介l?br />  3Q与Ҏ(gu)2cMQ只是参数由字符串换成了(jin)cd象,其目的是通过cd象获取类的全名。这个方法比较常用,CZ中用的是q个Ҏ(gu)?br />  那么Category是如何输Z息的呢?其实CZ中用到的debug、info、warn、error、fatal{五个方法都是用来输Z息的。什么,怎么q么多?原因很简单,Log4J支持分输出。Log4J的输出别有五个Q由低到高依ơ是DEBUGQ调试)(j)、INFOQ信息)(j)、WARNQ警告)(j)、ERRORQ错误)(j)和FATALQ致命)(j)Q分别与以上Ҏ(gu)对应。当输出U别讄为DEBUGӞ以上Ҏ(gu)都能够输Z息,当输出别设|ؓ(f)INFO Ӟ则只有debugҎ(gu)不能再输出信息Q依此类推,当输出别设|ؓ(f)FATALӞ只有fatalҎ(gu)可以输出信息?jin)。现在再回头看前边的问题Qؓ(f)何只有设|给fatalҎ(gu)的信息被输出׃隄解了(jin)Q示例中有这样一行代码:(x)

log.setLevel(Level.FATAL);

正是q行代码log对象的输出别设成了(jin)FATAL。在为log对象讄输出U别时用C(jin)Levelc,该类中定义了(jin)DEBUG、INFO、WARN?ERROR、FATAL{五个静(rn)态对象,与五个输出别相对应。此外,Levelq有两个Ҏ(gu)的静(rn)态对象ALL和OFFQ前者允许所有的Ҏ(gu)输出信息Q其U别其实比DEBUGq低Q后者则?x)禁止所有的Ҏ(gu)输出信息Q其U别比FATAL要高。除前边CZ中用到的五个Ҏ(gu)QLoggerq提供了(jin)q五个方法的重蝲Q以在输Z息的同时抛出异常Q以fatalҎ(gu)ZQ?/p>

log.fatal("Here is FATAL", new Exception("Exception"));

执行后输Z息:(x)
  FATAL - Here is FATAL
  java.lang.Exception: Exception
    at piv.zheng.log4j.test.Test.main(Test.java:24)
其他Ҏ(gu)cM。此外,Loggerq提供了(jin)logҎ(gu)Q该Ҏ(gu)不针对Q何输出别,需要在调用时设|,例如Q?/p>

log.log(Level.FATAL, "Here is FATAL");
log.log(Level.FATAL, "Here is FATAL", new Exception("Exception"));

虽然一般情况下logҎ(gu)不如其它Ҏ(gu)方便Q但׃允许讄U别Q因此logҎ(gu)在很多时候反而比其它Ҏ(gu)更灵z,甚至可以在输出别ؓ(f)OFF时输Z息。不qlogҎ(gu)主要是给用户自定义的输出U别用的Q而且讄OFF输出U别的目的也Z(jin)不输ZQ何信息,因此请不要在logҎ(gu)中用OFF来输Z息?br />  此外QCategory的输出别ƈ非必,若未讄Q子Category?x)默认用其父Category的输出别,若父Category也没讄Q就使用再上一UCategory的设|,直到根Category为止。根Category默认输出U别为DEBUGQ因此在CZ中,若将 "log.setLevel(Level.FATAL);"一行注释掉Q则所有方法都?x)输Z息?br />  下面单介l一下Log4J?Category的承关pR其实在Log4J中Category之间是存在承关pȝQ根Category默认创徏Q是U别最高的CategoryQ用户创建的Category均承自它。而用户创建的Category之间也存在承关p,例如Q?/p>

Logger lx = Logger.getLogger("x");
Logger lxy = Logger.getLogger("xy");
Logger lx_y = Logger.getLogger("x.y");
Logger lx_z = Logger.getLogger("x.z");
Logger lx_y_z = Logger.getLogger("x.y.z");

其中的lx_y、lx_z是lx的子CategoryQ而lx_y_z是lx_y的子Category。但lxyq不是lx的子Category。也许有点ؕQ下面来一个一个看。首先看与lx_y、lx_z对应的Category的名U?x.y"?x.z"Q?."前边的是什么,"x"Q这说明与名UCؓ(f) "x"的Category对应lx是它们的父CategoryQ而与lx_y_z对应的Category的名U?x.y.z"Q最后一?."前边的是什么,"x.y"Q这说明lx_y是lx_y_z的父CategoryQ至于lxyQ由于与之对应的Category名称"xy"之间没有"."Q因此它是一个与lx同的CategoryQ其父Category是根Category器。此外还有一U情况,例如有一个名UCؓ(f)"a.b"?CategoryQ如果没有名UCؓ(f)"a"的CategoryQ那么它的父Category也是根Category。前边说q,"."在Category名称中有Ҏ(gu)作用Q其实它的作用就是ѝ至此,Z使用cd象来创徏Category也就不难理解?jin)?br />  可是Q仅有Category是无法完成信息输出的Q还需要ؓ(f)CategorydAppenderQ即Category的输出源。前边的例子使用的是ConsoleAppenderQ即指定 Category信息输出到控制台。其实Log4J提供的Appender有很多,q里选择几常用的q行介绍?br />  1Qorg.apache.log4j.WriterAppenderQ可以根据用户选择信息输出到Writer或OutputStream?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout ();
    
    //向文件中输出信息QOutputStreamCZ
    WriterAppender appender1 = null;
    try {
      appender1 = new WriterAppender(layout, new FileOutputStream("test.txt"));
    }
    catch(Exception ex) {}
    
    //向控制台输出信息QW(xu)riterCZ
    WriterAppender appender2 = null;
    try {
      appender2 = new WriterAppender(layout, new OutputStreamWriter(System.out));
    }
    catch(Exception ex) {}
    
    //Category支持同时向多个目标输Z?br />    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender1);
    log.addAppender(appender2);
    
    log.debug("output");
q个CZq一个示例修改而来Q没有设|输出别,而且向Category中添加了(jin)两个输出源,q行后会(x)在控制台中输?DEBUG - output"Qƈ在工E目录下生成test.txt文gQ该文g中也记录着"DEBUG - output"。若要将test.txt文g攑ֈ其它路径下,例如f:Q则?test.txt"改ؓ(f)"f:/test.txt"Q又如e:下的temp 文g夹,改?e:/temp/test.txt"。后边FileAppender、RollingFileAppender以及(qing) DailyRollingFileAppender讄目标文g时也都可以这h写?br />  2Qorg.apache.log4j.ConsoleAppenderQ向控制台输Z息,l承?jin)WriterAppenderQ前边的CZ使用的就是它?br />  3Qorg.apache.log4j.FileAppenderQ向文g输出信息Q也l承?jin)WriterAppender?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout();
    
    //若文件不存在则创建文Ӟ若文件已存在则向文g中追加信?br />    FileAppender appender = null;
    try {
      appender = new FileAppender(layout, "test.txt");
    } catch(Exception e) {}
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
q个CZ也由W一个示例修改而来Q运行后?x)在工程目录下生成test.txt文gQ该文g中记录着"DEBUG - output"。再ơ运行程序,查看文gQ则"DEBUG - output"有两行?br />  另外QFileAppenderq有一个构造:(x)

FileAppender(Layout layout, String filename, boolean append)

与示例的cMQ只是多?jin)一个boolean型的参数append。append参数是个开养I用来讄当程序重启,而目标文件已存在Ӟ是向目标文gq加信息q是覆盖原来的信息,当gؓ(f)true时就q加Q这是FileAppender默认的,当gؓ(f)false时则覆盖。此外,FileAppenderq提供了(jin)setAppendҎ(gu)来设|a(chn)ppend开兟?br />  4Qorg.apache.log4j.RollingFileAppenderQ承了(jin) FileAppenderQ也是向文g输出信息Q但文g大小可以限制。当文g大小过限制Ӟ该文件会(x)被{为备份文件或删除Q然后重新生成。文件的转换或删除与讄的备份文件最大数量有养I当数量大?时就转ؓ(f)备䆾文gQ否则(于{于0Q删除,默认的备份文件数量是1。{换备份文仉常简单,是修改文g名,在原文g名之后加?.1"Q例如文件test.txtQ{为备份文件后文g名ؓ(f)"test.txt.1"。但若同名的备䆾文g已存在,则会(x)先将该备份文件删除或更名Q这也与讄的备份文件最大数量有养I若达到最大数量就删除Q否则更名。若备䆾文g更名时也遇到同样情况Q则使用同样的处理方法,依此cLQ直到达到设|的备䆾文g最大数量。备份文件更名也很简单,是扩展名?Q例如test.txt.1文g更名后变为test.txt.2Q?test.txt.2文g更名后变为test.txt.3?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout();
    
    //若文件不存在则创建文Ӟ若文件已存在则向文g中追加内?br />    RollingFileAppender appender = null;
    try {
      appender = new RollingFileAppender(layout, "test.txt");
    } catch(Exception e) {}
    //限制备䆾文g的数量,本例??br />    appender.setMaxBackupIndex(2);
    //限制目标文g的大,单位字节Q本例ؓ(f)10字节
    appender.setMaximumFileSize(10);
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    
    log.debug("output0");
    log.debug("output1");
    log.debug("output2");
E序q行后,?x)在工程目录下生成test.txt、test.txt.1和test.txt.2三个文gQ其中test.txt内容为空Q而后两个文g则分别记录着"DEBUG - output2"?DEBUG - output1"Q这是怎么回事Q原来由于目标文件大被限制?0字节Q而三ơ用log.debugҎ(gu)输出的信息都过?0字节Q这样就D?jin)三ơ备份文件{换,所以test.txt内容为空。而备份文件最大数量被设ؓ(f)2Q因此第一ơ{换的备䆾文gp删掉?jin),而后两次的则保存下来。此外,׃ test.txt转换备䆾文g时是先{为test.txt.1Q再转ؓ(f)test.txt.2Q因此最后test.txt.2的内Ҏ(gu)"DEBUG - output1"Q而test.txt.1?DEBUG - output2"Q这点千万别弄؜?jin)?br />  另外QRollingFileAppenderq提供了(jin)两个Ҏ(gu)Q?br />  Q?QsetMaxFileSizeQ功能与setMaximumFileSize一P但参数是字符Ԍ有两U情况:(x)一是仅由数字组成,默认单位为字节,例如"100"Q即表示限制文g大小?00字节Q一是由数字?qing)存储单位组成,例?1KB"?1MB"?1GB"Q其中单位不区分大小写,分别表示限制文g大小?K?M?G?br />  Q?QrollOverQ手动将目标文g转换为备份文Ӟ使用h较灵z,适用于复杂情c(din)?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout();
    
    RollingFileAppender appender = null;
    try {
      appender = new RollingFileAppender(layout, "test.txt");
    } catch(Exception e) {}
    appender.setMaxBackupIndex(2);

    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    
    log.debug("output0");
    appender.rollOver();
    log.debug("output1");
    appender.rollOver();
    log.debug("output2");
    appender.rollOver();
q里没限制目标文件大,但程序运行后Q效果与上例相同?br />  5Qorg.apache.log4j.DailyRollingFileAppenderQ也l承?jin)FileAppenderQƈ且也是向文g输出信息Q但?x)根据设定的旉频率生成备䆾文g?br />  旉频率格式介:(x)
  '.'yyyy-MMQ按月生成,生成旉为每月最后一天午夜过后,例如test.txt?005q??1日午夜过后会(x)被更名ؓ(f)test.txt.2005-07Q然后重新生成?br />  '.'yyyy-wwQ按周生成,生成旉为每周六午夜q后Q例如test.txt?005q??3日午夜过后会(x)被更名ؓ(f)test.txt.2005-33Q?3表示当年W?3周?br />  '.'yyyy-MM-ddQ按天生成,生成旉为每天午夜过后,例如2005q??6日午夜过后,test.txt?x)被更名为test.txt.2005-08-16?br />  '.'yyyy-MM-dd-aQ也是按天生成,但每天会(x)生成两次Q中?2:00q后一ơ,午夜q后一ơ,例如test.txt?005q??6 ?2:00q后?x)被更名为test.txt.2005-8-16-上午Q午夜过后会(x)被更名ؓ(f)test.txt.2005-8-16-下午?br />  '.'yyyy-MM-dd-HHQ按时生成Q例如test.txt?005q??6?2:00q后?x)被更名为test.txt.2005-8-16-11?br />  '.'yyyy-MM-dd-HH-mmQ按分钟生成Q例如test.txt?005q??6?2:00q后?x)被更名为test.txt.2005-8-16-11-59?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout();
    
    DailyRollingFileAppender appender = null;
    try {
      appender = new DailyRollingFileAppender(layout, "test.txt", "'.'yyyy-MM-dd-HH-mm");
    } catch(Exception e) {}
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
~码完成后运行程序,{一分钟后再ơ运行,׃我是?005q??7?5:42分第一ơ运行程序的Q因此工E目录下最l有两个文gtest.txt和test.txt.2005-08-17-15-42?br />  6Qorg.apache.log4j.AsyncAppenderQ用于管理不同类型的AppenderQ也能实现同时向多个源输Z息,但其执行是异步的?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout();
    
    //向控制台输出
    ConsoleAppender appender1 = null;
    try {
      appender1 = new ConsoleAppender(layout);
    } catch(Exception e) {}
    
    //向文件输?br />    FileAppender appender2 = null;
    try {
      appender2 = new FileAppender(layout, "test.txt");
    } catch(Exception e) {}
    
    //使用AsyncAppender实现同时向多个目标输Z?br />    AsyncAppender appender = new AsyncAppender();
    appender.addAppender(appender1);
    appender.addAppender(appender2);
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
此外QAsyncAppender和Logger都提供了(jin)更多的方法来理AppenderQ例如getAppender?getAllAppenders、removeAppender和removeAllAppendersQ分别用来获取指定的Appender、获取全?Appender、移除指定的Appender以及(qing)U除全部Appender?br />  7Qorg.apache.log4j.jdbc.JDBCAppenderQ将信息输出到数据库?br />  CZ代码Q?br />    JDBCAppender appender = new JDBCAppender();
    appender.setDriver("com.mysql.jdbc.Driver");
    appender.setURL("jdbc:mysql://localhost:3306/zheng");
    appender.setUser("root");
    appender.setPassword("11111111");
    appender.setSql("insert into log4j (msg) values ('%m')");
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
q里使用的数据库是MySQL 5.0.4betaQ用户名rootQ密?1111111Q我在其中徏?jin)一个库zhengQ包含表log4jQ该表只有一个字DmsgQ类型ؓ(f)varchar(300)。此外,本例用到的JDBC驱动可以?a >http://dev.mysql.com/downloads/connector/j/3.1.html下蝲Q版?.1.8aQ下载mysql-connector-java-3.1.8a.zip文g后解压羃Q需要其中的mysql-connector- java-3.1.8-bin.jar文g。下面再来看代码。由于JDBCAppender内部默认使用PatternLayout格式化输Z息,因此q里没用到SimpleLayoutQ而appender.setSql所讄的SQL语句是PatternLayout所需的格式化字符Ԍ故此其中才有"%m"q样的字W,有关PatternLayout的具体内容后边介l。执行后Q表log4j增加一条记录,内容?output"?br />  8Qorg.apache.log4j.nt.NTEventLogAppenderQ向Windows NTpȝ日志输出信息?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout();
    
    NTEventLogAppender appender = new NTEventLogAppender("Java", layout);

    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
注意Q要完成此示例,q需向C:\WINNT\system32文g夹(我的操作pȝ装在?jin)C:\Q中复制一个名?NTEventLogAppender.dll的文件。如果跟我一L(fng)的是Log4J 1.2.11Q实在对不住QLog4J 1.2.11q未提供该文件。虽然logging-log4j-1.2.11.zip文g解压~后Q其下的src\java\org\apache\ log4j\nt文g夹中有一个make.bat文g执行后可以编译出该文Ӟ但还需要配|,很麻?ch)。还好,条条大道通罗马,1.2.11不行Q就?1.2.9Q可以从http://apache.justdn.org/logging/log4j/1.2.9下蝲Q下载后解压~logging-log4j-1.2.9.zip文gQ在其下的src\java\org\apache\log4j\nt文g夹中扑ֈ NTEventLogAppender.dllQ复制过d可以?jin)。程序执行后Q打开"事g查看?Q选择"应用E序日志"Q其中有一条来源ؓ(f)Java的记录,q条记录是刚才输出的信息了(jin)?br />  9Qorg.apache.log4j.lf5.LF5AppenderQ执行时?x)弹Z个窗口,信息在该H口中以表格的Ş式显C?br />  CZ代码Q?br />    LF5Appender appender = new LF5Appender();
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
׃LF5Appender不需要Layout格式化输Z息,因此q里没有讄。此外LF5Appenderq提供了(jin)一个setMaxNumberOfRecordsҎ(gu)Q用来限制信息在表格中显C的行数?br />  10Qorg.apache.log4j.net.SocketAppenderQ以套接字方式向服务器发送日志,然后由服务器信息输出?br />  CZ代码Q?br />  //指定要连接的服务器地址?qing)端口,q里使用的是本机9090端口
  SocketAppender appender = new SocketAppender("localhost", 9090);
  Logger log = Logger.getLogger(Test.class);
  log.addAppender(appender);
  log.debug("output");
SocketAppender不需要设|LayoutQ因为SocketAppender不负责输Z息。那么如何看C息输出的效果呢?q就需要SocketServer和SimpleSocketServer?jin)?br />  CZ代码1Q?br />    package piv.zheng.log4j.test;
    
    import org.apache.log4j.net.SocketServer;
    
    public class TestServer {
      public static void main(String[] args) {
        SocketServer.main(new String[]{"9090", "test.properties", "G:/YPJCCK/Log4J"});
      }
    }
q是SocketServer的示例。SocketServer只有一个静(rn)态方法mainQ该Ҏ(gu)意味着SocketServer不仅可以在代码中被调用,也可以用java命o(h)执行。mainҎ(gu)只有一个参敎ͼ是个字符串数l,但要求必L三个元素Q元素一用来指定端口Q本例ؓ(f)9090Q元素二用来指定输出信息旉要的配置文gQ该文g攑֜工程目录下,本例使用的test.properties内容如下Q?br />  log4j.rootLogger=, console
  log4j.appender.console =org.apache.log4j.ConsoleAppender
  log4j.appender.console.layout=org.apache.log4j.SimpleLayout
该配|指定SocketServer使用ConsoleAppender以SimpleLayout格式输出信息Q元素三用来指定一个\径,以存?lcf 文gQ我指定的是本机的G:/YPJCCK/Log4J文g夏V?lcf文g也是输出信息时用的配置文gQ格式与元素二所指定的配|文件一P?test.properties是默认配|文Ӟ卛_.lcf文g找不到时才用。那?lcf文g如何命名呢?其实.lcf文g的名Uƈ不是随意L(fng)Q当SocketAppender与SocketServer建立q接ӞSocketServer׃(x)获得SocketAppender所在计机的IP 地址与网lIDQƈ其格式化成"|络ID/IP地址"q样的字W串Q然后获取其中的|络ID作ؓ(f).lcf文g的主名,例如 "zhengyp/127.0.0.1"Q其中的"zhengyp"是L件名Q而后再根据这个文件名来调用相应的.lcf文g。这意味着对不同的计算机可以提供不同的配置文gQ信息输出时有不同的效果。此外,SocketServerq默认了(jin)一个名为generic.lcf的文Ӟ用于处理|络ID 获取不到或其他情况,本例是用的就是这个文Ӟ内容如下Q?br />  log4j.rootLogger=, console
  log4j.appender.console =org.apache.log4j.ConsoleAppender
  log4j.appender.console.layout=org.apache.log4j.PatternLayout
  log4j.appender.console.layout.ConversionPattern=%m%n
该配|指定SocketServer使用ConsoleAppender以PatternLayout格式输出信息。运行程序时请先q行 SocketServerQ再q行SocketAppender。SocketAppenderq行l束后,可以从SocketServer的控制台看到输出的信息了(jin)?br />  CZ代码2Q?br />    package piv.zheng.log4j.test;
    
    import org.apache.log4j.net.SimpleSocketServer;

    public class TestServer {
      public static void main(String[] args) {
        SimpleSocketServer.main(new String[]{"9090", "test.properties"});
      }
    }
q是SimpleSocketServer的示例,与SocketServer相比Q只允许指定一个默认的配置文gQ而无法对不同计算Z用不同的配置文g?br />  11Qorg.apache.log4j.net.SocketHubAppenderQ也是以套接字方式发送日志,但与SocketAppender相反QSocketHubAppender是服务器端,而不是客L(fng)?br />  CZ代码Q?br />    //指定服务器端口,q里使用的是本机9090端口
    SocketHubAppender appender = new SocketHubAppender(9090);
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    while (true) {
      Thread.sleep(1000);
      log.debug("output"); //输出信息
    }
׃SocketHubAppender一旦运行就开始发送消息,而无论有无接收者,因此q里使用?jin)while语句q将条g设ؓ(f)true以保证程序持l运行。不qؓ(f)?jin)保证性能Q这里还使用?jin)Thread.sleep(1000)Q这L(fng)序每循环一ơ都休眠1U,如果机器性能不好Q还可以D的再大些。此外,׃SocketHubAppender也不负责输出信息Q因此同样不需要设|Layout。那么如何看C息输出的效果呢?q里我自己写?jin)个客户端程序,代码如下Q?br />  package piv.zheng.log4j.test;
  
  import java.net.Socket;
  import java.lang.Thread;
  import org.apache.log4j.LogManager;
  import org.apache.log4j.PropertyConfigurator;
  import org.apache.log4j.net.SocketNode;
  
  public class TestClient {
    public static void main(String[] args) throws Exception {
      //创徏客户端套接字对象
      Socket s = new Socket("localhost", 9090);
      //调用配置文g
      PropertyConfigurator.configure("test.properties");
      //从套接字中恢复LoggerQƈ输出信息
      new Thread(new SocketNode(s, LogManager.getLoggerRepository())).start();
    }
  }
׃SocketHubAppender与SocketAppender一P发送的也是SocketNode对象Q因此编写该E序时参考了(jin) SocketServer的源码。此外,q里的配|文件直接用了(jin)上例的test.properties文g。运行程序时请先q行 SocketHubAppenderQ再q行客户端程序,然后从客L(fng)的控制台可以看到效果了(jin)?br />  13Qorg.apache.log4j.net.TelnetAppenderQ与SocketHubAppender有些cMQ也是作为服务器发送信息,但TelnetAppender发送的不是SocketNode对象Q而是Category输出的结果?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout();
    
    TelnetAppender appender = new TelnetAppender();
    appender.setLayout(layout); //讄Layout
    appender.setPort(9090); //讄端口?br />    appender.activateOptions(); //应用讄
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    
    while (true) {
      java.lang.Thread.sleep(1000);
      log.debug("output"); //输出信息
    }
    //appender.close();
注意最后一行被注释掉的代码Q若该行代码执行Q则TelnetAppender的资源会(x)被清理,从而导致TelnetAppender无法l箋q行。那么如何看C息输出的效果呢?q里提供两种Ҏ(gu)Q方法一Q用Telnet工具Q我使用的就是Windows自带的Telnet。运?TelnetAppenderE序后,点击[开始]菜单->[q行]Q在"q行"框中输入"telnet"Q回车,telnet客户端弹出,q是一个命令行E序Q输入命?open localhost 9090"Q回车,然后可以看到效果了(jin)。方法二Q自己写E序Q代码如下:(x)
  package piv.zheng.log4j.test;
  
  import java.net.*;
  import java.io.*;
  
  public class TestClient {
    public static void main(String[] args) throws Exception {
      //创徏客户端套接字对象
      Socket s = new Socket("localhost", 9090);
      //BufferedReader与Socketl定Q以输出Socket获得的信?br />      BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
      //获得信息q输?br />      String line = in.readLine();
      while (line != null) {
        System.out.println(line);
        line = in.readLine();
      }
    }
  }
  13Qorg.apache.log4j.net.SMTPAppenderQ向指定的电(sh)子邮件发送信息,但只能发送ERROR和FATALU别的信息,而且q没提供w䆾验证功能?br />  CZ代码Q?br />  SimpleLayout loyout = new SimpleLayout();
  
  SMTPAppender appender = new SMTPAppender();
  appender.setLayout(loyout); //讄Layout
  appender.setFrom("zhengyp@126.com"); //讄发g?br />  appender.setSMTPHost("smtp.126.com"); //讄发送邮件服务器
  appender.setTo("zhengyp@126.com"); //讄收g?br />  appender.setSubject("Log4J Test"); //讄邮g标题
  appender.activateOptions(); //应用讄
  
  Logger log = Logger.getLogger(Test.class);
  log.addAppender(appender);
  log.debug("Here is DEBUG");
  log.info("Here is INFO");
  log.warn("Here is WARN");
  log.error("Here is ERROR");
  log.fatal("Here is FATAL");
要运行此CZQ还需要JavaMail 和JAFQ前者是Sun推出的电(sh)子邮件类库,可以?a >http://java.sun.com/products/javamail/downloads/index.html下蝲Q最新版?.3.3Q下载j(lu)avamail-1_3_3-ea.zip压羃包后需要其中的mail.jar文gQ后者全U是JavaBeans Activation FrameworkQ提供了(jin)对输入Q意数据块的支持,q能相应地对其进行处理,可以?a >http://www.sun.com/download中找刎ͼ最新版?.1Q下载j(lu)af-1_1-ea.zip压羃包后需要其中的activation.jar文g。不q,E序q行后会(x)抛出两次异常Q分别是log.error和log.fatalҎ(gu)D的,p|的原因很单,我用的邮件服务器需要n份验证?br />  14Qpiv.zheng.log4j.test.SMTPAppenderQ自定义的,依照Log4J提供的SMTPAppender修改而来Q增加了(jin)w䆾验证功能QƈL?jin)对U别的限制。由于代码太长,所以放C(jin)另一文章《自定义SMTPAppender的源码》中Q有兴趣的请自行L看?br />  CZ代码Q?br />    SimpleLayout layout = new SimpleLayout();
    
    SMTPAppender appender = new SMTPAppender(layout);
    appender.setFrom("zhengyp@126.com"); //发g?br />    appender.setSMTPHost("smtp.126.com"); //发送邮件服务器
    appender.setTo("zhengyp@126.com"); //收g?br />    appender.setSubject("Log4J Test"); //邮g标题
    appender.setAuth("true"); //w䆾验证标识
    appender.setUsername("zhengyp"); //用户?br />    appender.setPassword("1111111"); //密码
    appender.activateOptions(); //应用讄
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
同样需要JavaMail 和JAF。程序运行后?x)发送一邮Ӟ快去查看一下自q邮箱吧^_^
  此外QLog4Jq提供了(jin)SyslogAppender、JMSAppenderQ均在org.apache.log4j.net包下Q以?qing)更多?AppenderQ或者用来向Unix操作pȝ的syslogd服务发送信息,或者通过JMS方式发送信息,或者以其他方式发送信息。由于条件有玎ͼ׃再介l了(jin)?br />  不过Q在前边的示例中q用了(jin)SimpleLayout和PatternLayout来格式化输出的信息,q里也简单介l一下?br />  1Qorg.apache.log4j.SimpleLayoutQ一直用的就是它Q输出的格式比较单,是"U别 - 信息"?br />  2Qorg.apache.log4j.HTMLLayoutQ以HTML格式输出信息?br />  CZ代码Q?br />    HTMLLayout layout = new HTMLLayout();
    layout.setTitle("Log4J Test"); //HTML|?br />    
    FileAppender appender = null;
    try {
      appender = new FileAppender(layout, "test.html");
    } catch(Exception e) {}
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
E序q行后会(x)在工E目录下生成一个HTML,可以用浏览器来查看?br />  3Qorg.apache.log4j.xml.XMLLayoutQ以XML格式输出信息?br />  CZ代码Q?br />    XMLLayout layout = new XMLLayout();
    
    FileAppender appender = null;
    try {
      appender = new FileAppender(layout, "test.xml");
    } catch(Exception e) {}
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
E序q行后会(x)在工E目录下生成一个test.xml文g?br />  4Qorg.apache.log4j.TTCCLayoutQ输Z息的同时输出日志产生旉、相关线E及(qing)Category{信息?br />  CZ代码Q?br />    TTCCLayout layout = new TTCCLayout();
    //是否打印与TTCCLayout兌的Category的名Uͼ默认为trueQ表C打?br />    layout.setCategoryPrefixing(true);
    //是否打印当前U程Q默认ؓ(f)trueQ表C打?br />    layout.setThreadPrinting(true);
    //是否打印输出和当前线E相关的NDC信息Q默认ؓ(f)trueQ表C打?br />    layout.setContextPrinting(true);
    //讄日期旉格式
    layout.setDateFormat("iso8601");
    //讄时区
    layout.setTimeZone("GMT+8:00");
    //讄时区后需要调用此Ҏ(gu)应用讄
    layout.activateOptions();
    
    ConsoleAppender appender = new ConsoleAppender(layout);
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
注意QTTCCLayout输出的时间格式及(qing)时区是可以设|的Q?br />  Q?QsetDateFormatQ设|日期时间格式,有五个常用|(x)"NULL"Q表CZ输出Q?RELATIVE"Q输Z息所用的旉Q以毫秒为单位,默认使用该|"ABSOLUTE"Q仅输出旉部分Q?DATE"Q按当前所在地区显C日期和旉Q?ISO8601"Q按ISO8601标准昄日期和时间。这些字W串不区分大写。此外,q可以用时间模式字W来格式化日期时_(d)详细内容请参考J2SE文档中的 java.text.SimpleDateFormatcR?br />  Q?QsetTimeZoneQ设|时区,详细内容请参考J2SE文档中的java.util.TimeZonecdjava.util.SimpleTimeZonecR但h意,当日期格式ؓ(f)"RELATIVE"Ӟ讄时区?x)造成冲突?br />  5Qorg.apache.log4j.PatternLayoutQ用模式字符灉|指定信息输出的格式?br />  CZ代码Q?br />    String pattern = "Logger: %c %n"
        + "Date: %d{DATE} %n"
        + "Message: %m %n";
    PatternLayout layout = new PatternLayout(pattern);
    
    ConsoleAppender appender = new ConsoleAppender(layout);
    
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    log.debug("output");
模式字符串简介:(x)
  %cQCategory名称。还可以使用%c{n}的格式输出Category的部分名Uͼ其中n为正整数Q输出时?x)从Category名称的右侧v?n?."Q然后截取第n?."右侧的部分输出,例如Category的名UCؓ(f)"x.y.z"Q指定格式ؓ(f)"%c{2}"Q则输出"y.z"?br />  %CQ输Z息时Category所在类的名Uͼ也可以?C{n}的格式输出?br />  %dQ输Z息的旉Q也可以?d{FormatString}的格式输出,其中FormatString的D参考TTCCLayout的setDateFormatҎ(gu)Q但NULL和RELATIVE?d中无法用?br />  %FQ输Z息时Category所在类文g的名U?br />  %lQ输Z息时Category所在的位置Q?%C.%M(%F:%L)"可以产生同样的效果?br />  %LQ输Z息时Category在类文g中的行号?br />  %mQ信息本w?br />  %MQ输Z息时Category所在的Ҏ(gu)?br />  %nQ换行符Q可以理解成回R?br />  %pQ日志别?br />  %rQ输Z息所用的旉Q以毫秒为单位?br />  %tQ当前线E?br />  %xQ输出和当前U程相关的NDC信息?br />  %XQ输Z当前现成相关的MDC信息?br />  %%Q输??br />此外Q还可以?与模式字W之间加上修饰符来设|输出时的最宽度、最大宽度及(qing)文本寚w方式Q例如:(x)
  %30d{DATE}Q按当前所在地区显C日期和旉Qƈ指定最宽度ؓ(f)30Q当输出信息于30个字W时?x)补以空格ƈ叛_齐?br />  %-30d{DATE}Q也是按当前所在地区显C日期和旉Q指定最宽度ؓ(f)30Qƈ在字W少?0时补以空|但由于用了(jin)"-"Q因此对齐方式ؓ(f)左对齐,与默认情况一栗?br />  %.40d{DATE}Q也是按当前所在地区显C日期和旉Q但指定最大宽度ؓ(f)40Q当输出信息多于40个字W时?x)将左边多出的字W截掉。此外,最大宽度只支持默认的左寚w方式Q而不支持叛_齐?br />  %30.40d{DATE}Q如果输Z息少?0个字W就补空格ƈ叛_齐,如果多于40个字W,将左边多出的字W截掉?br />  %-30.40d{DATE}Q如果输Z息少?0个字W就补空格ƈ左对齐,如果多于40个字W,将左边多出的字W截掉?br />五、Log4Jq阶
  ?jin)解以上内容后,已l初步掌握Log4J?jin),但要想灵zM用Log4JQ则q需要了(jin)解其配置功能。这里简单介l一下?br />  1Qorg.apache.log4j.BasicConfiguratorQ默认用ConsoleAppender以PatternLayout Q用PatternLayout.TTCC_CONVERSION_PATTERNQ即"%r [%t] %p %c %x - %m%n"格式Q输Z息?br />  CZ代码Q?br />    BasicConfigurator.configure();
    Logger log = Logger.getLogger(Test.class);
    log.debug("output");
注意QBasicConfigurator?qing)其它Configurator其实都只Ҏ(gu)Categoryq行配置Q但׃用户创徏的Category?x)承根Category的特性(声明Q许多资料介lCategoryl承关系旉主要在讨出别,而事实上QCategory间承的不仅是输出别,所有特性都可以l承Q,因此输出时仍?x)显CBasicConfigurator配置的效果。此外,q可以用configureҎ(gu)指定AppenderQ以自定义输出。BasicConfigurator允许同时指定多个Appender?br />  CZ代码Q?br />    SimpleLayout layout1 = new SimpleLayout();
    ConsoleAppender appender1 = new ConsoleAppender(layout1);
    BasicConfigurator.configure(appender1);
    
    String pattern = "Logger: %c %n"
        + "Date: %d{DATE} %n"
        + "Message: %m %n";
    PatternLayout layout2 = new PatternLayout(pattern);
    FileAppender appender2 = null;
    try {
      appender2 = new FileAppender(layout2, "test.log", false);
    }
    catch(Exception e){}
    BasicConfigurator.configure(appender2);
    
    Logger log = Logger.getLogger(Test.class);
    log.debug("output");
q里用BasicConfigurator指定?jin)两个AppenderQ即ConsoleAppender和FileAppenderQ程序运行后信息?x)在以SimpleLayout输出到控制台的同时以PatternLayout输出到test.log文g。若要清除这些AppenderQ可以调?BasicConfigurator的resetConfigurationҎ(gu)?br />  2Q?org.apache.log4j.PropertyConfiguratorQ调用文本配|文件输Z息,通常使用.properties文g。配|文件以"??的Ş式保存数据,注释?#"开头。PropertyConfigurator和配|文件在介绍SocketAppender?SocketHubAppender时曾提到q。用PropertyConfigurator可以避免编码?br />  CZ代码Q?br />    PropertyConfigurator.configure("test.properties");
    Logger log = Logger.getLogger(Test.class);
    log.debug("output");
要完成该CZQ还需要在工程目录下创Z个test.properties文gQ内容如下:(x)
  ##讄根CategoryQ其值由输出U别和指定的Appender两部分组?br />  #q里讄输出U别为DEBUG
  log4j.rootLogger=DEBUG,appender
  ##输出信息到控制台
  #创徏一个名为appender的AppenderQ类型ؓ(f)ConsoleAppender
  log4j.appender.appender=org.apache.log4j.ConsoleAppender
  #讄a(chn)ppender以SimpleLayout输出
  log4j.appender.appender.layout=org.apache.log4j.SimpleLayout
此外QPropertyConfigurator也允许同时指定多个AppenderQ例如:(x)
  #q里没有讄输出U别Q但指定?jin)两个Appender
  log4j.rootLogger=,appender1,appender2
  #输出信息到控制台
  log4j.appender.appender1=org.apache.log4j.ConsoleAppender
  log4j.appender.appender1.layout=org.apache.log4j.SimpleLayout
  #输出信息到文?br />  log4j.appender.appender2=org.apache.log4j.FileAppender
  log4j.appender.appender2.File=test.log
  log4j.appender.appender2.Append=false
  log4j.appender.appender2.layout=org.apache.log4j.PatternLayout
  log4j.appender.appender2.layout.ConversionPattern=Logger: %c %nDate: %d{DATE} %nMessage: %m %n
关于更多配置Q网上示例很多,q里不再赘述。但要说明一件事Q就是配|文件中的键是怎么来的。参照后一个示例,查看 PropertyConfigurator源码Q会(x)发现"log4j.rootLogger"是定义好的,只能照写Q?log4j.appender" 字样也可以找刎ͼ与指定的Appender名称appender1、appender2联系hQlog4j.appender.appender1?log4j.appender.appender2也就不难理解?jin);再看下去Q还能找?prefix + ".layout""Q这样log4j.appender.appender1.layout也有?jin);可?log4j.appender.appender2.File 和log4j.appender.appender2.Append呢?q记得前边介lFileAppender时曾提到的setAppendҎ(gu)吗?其实FileAppenderq有个getAppendҎ(gu)Q这说明FileAppenderhAppend属性。那么F(tun)ile呢?当然也是 FileAppender的属性了(jin)。至于log4j.appender.appender2.layout.ConversionPattern也一P只不qFileAppender换成?jin)PatternLayout。其实别的Appender和Layout的属性也都是q样定义成键来进行设|的。此外,定义键时Q属性的首字母不区分大小写,例如"File"Q也可以写成"file"?br />  3Q?org.apache.log4j.xml.DOMConfiguratorQ调用XML配置文g输出信息。其定义文档是log4j- 1.2.11.jar中org\apache\log4j\xml包下的log4j.dtd文g。与PropertyConfigurator相比Q?DOMConfiguratorg是趋ѝ?br />  CZ代码Q?br />    DOMConfigurator.configure("test.xml");
    Logger log = Logger.getLogger(Test.class);
    log.debug("output");
要完成该CZQ也需要在工程目录下创Z个test.xml文gQ内容如下:(x)
  <?xml version="1.0" encoding="UTF-8" ?>
  <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
  <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <!-- 输出信息到控制台
    创徏一个名为appender的AppenderQ类型ؓ(f)ConsoleAppender -->
    <appender name="appender" class="org.apache.log4j.ConsoleAppender">
      <!-- 讄a(chn)ppender以SimpleLayout输出 -->
      <layout class="org.apache.log4j.SimpleLayout"/>
    </appender>
    <!-- 讄根CategoryQ其值由输出U别和指定的Appender两部分组?br />    q里讄输出U别为DEBUG -->
    <root>
      <priority value ="debug" />
      <appender-ref ref="appender"/>
    </root>
  </log4j:configuration>
此外QDOMConfigurator也允许同时指定多个AppenderQ例如:(x)
  <?xml version="1.0" encoding="UTF-8" ?>
  <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
  <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <!-- 输出信息到控制台 -->
    <appender name="appender1" class="org.apache.log4j.ConsoleAppender">
      <layout class="org.apache.log4j.SimpleLayout"/>
    </appender>
    <!-- 输出信息到文?-->
    <appender name="appender2" class="org.apache.log4j.FileAppender">
      <param name="File" value="test.log"/>
      <param name="Append" value="false"/>
      <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="Logger: %c %nDate: %d{DATE} %nMessage: %m %n"/>
      </layout>
    </appender>
    <!-- q里没有讄输出U别Q但指定?jin)两个Appender -->
    <root>
      <appender-ref ref="appender1"/>
      <appender-ref ref="appender2"/>
    </root>
  </log4j:configuration>
׃以上两个CZ是在PropertyConfigurator的两个示例基上改的,而且也写?jin)注释,因此q里只简单介l一?lt;param> 标记?lt;param>标记有两个属性,name和valueQ前者的g是Appender或Layout的属性名Q作用与 log4j.appender.appender2.Fileq样的键一栗设|时Q首字母同样不区分大写Q例?File"也可以写?file"。此外还h意,使用q两DXML代码时应中文注释去掉,或者把<?xml version="1.0" encoding="UTF-8" ?>中的UTF-8Ҏ(gu)GBK或GB2312Q否则会(x)D错误。这里用的UTF-8是XML默认的字W集?br />  4Q?org.apache.log4j.lf5.DefaultLF5ConfiguratorQ默认用LF5Appender来输Z息,需要调?log4j-1.2.11.jar中org\apache\log4j\lf5\config包下的defaultconfig.properties文g?br />  CZ代码Q?br />    try {
      DefaultLF5Configurator.configure();
    }
    catch(Exception e){}
    Logger log = Logger.getLogger(Test.class);
    log.debug("output");
  下面讨论另外一个话题:(x)Diagnostic Context。Diagnostic Context意ؓ(f)诊断环境Q针对于多用户ƈ发环境,在这U环境下Q通常需要对每个客户端提供独立的U程以处理其hQ此时若要在日志信息中对客户端加以区分,为每个线E分别创建Category是个办法。但q样做ƈ不高效,反而会(x)D大量资源被占用。Diagnostic Context所要解决的是q个问题。Diagnostic Context?x)?f)当前U程提供一定空_(d)然后信息保存到该空间供Category调用。与创徏一个Category相比Q这点信息所占的资源自然要少得多?br />  1Qorg.apache.log4j.NDC。NDC是Nested Diagnostic Context的简写,意ؓ(f)嵌套诊断环境Q用时提供一个堆栈对象来保存信息。堆栈的特点是数据后q先出、先q后出,x理堆栈时Q后保存的数据会(x)被先清掉Q而先保存的数据则被后清掉?br />  CZ代码Q?br />    PatternLayout layout = new PatternLayout("%m %x%n");
    ConsoleAppender appender = new ConsoleAppender(layout);
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    
    String tmp = "zhengyp"; //模拟从客L(fng)获取的信?br />    log.debug("Start");
    NDC.push(tmp); //d信息到堆栈中
    log.debug("Before");
    NDC.pop(); //信息从堆栈中移?br />    log.debug("After");
    NDC.remove(); //当前线E移除,退出NDC环境
    log.debug("End");
q里使用?jin)PatternLayout来格式化信息Q其模式字符%x是用来输出NDC信息的。程序运行后?x)输出如下内容?x)
  Start
  Before zhengyp
  After
  End
可以看到Q第二行输出时由于已向堆栈中d?jin)信息,因?zhengyp"也会(x)同时输出Q而第三行输出时由于信息已被移除,因此没再输?zhengyp"。不q这个示例仅单演CZ(jin)NDC的用法,而没有显C出NDC的堆栈特性,所以下面再提供一个示例,代码如下Q?br />  TTCCLayout layout = new TTCCLayout();
  ConsoleAppender appender = new ConsoleAppender(layout);
  Logger log = Logger.getLogger(Test.class);
  log.addAppender(appender);
  
  log.debug("Start");
  NDC.push("zhengyp"); //d信息到堆栈中
  log.debug("Test1");
  NDC.push("192.168.0.1"); //向堆栈中q加信息
  log.debug("Test2");
  NDC.pop(); //从堆栈中U除信息Q但U除的只是最后的信息
  log.debug("Test3");
  NDC.pop(); //再次从堆栈中U除信息
  log.debug("Test4");   
  log.debug("End");
q里格式化输Z息用的是TTCCLayoutQ还记得其setContextPrintingҎ(gu)吗?E序q行后,从输出的信息可以看到效果了(jin)。此外,NDCq提供了(jin)其他Ҏ(gu)Q?br />  Q?QgetQ获取堆栈中的全部信息。以上例ZQ当输出Test2Ӟ使用该方法会(x)获得"zhengyp 192.168.0.1"?br />  Q?QpeekQ获取堆栈中最后的信息。仍以上例ؓ(f)例,当输出Test1时会(x)获得"zhengyp"QTest2时ؓ(f)"192.168.0.1"Q而当输出Test3时由?192.168.0.1"已被U除Q?zhengyp"又成?jin)最后的信息Q因此获得的仍是"zhengyp"?br />  Q?QclearQ清I堆栈中的全部信息?br />  Q?QsetMaxDepthQ设|堆栈的最大深度,卛_前的信息可以保留多少Q对之后q加的信息没有媄(jing)响。当需要一ơ清掉多条信息时Q用setMaxDepth?x)比多次调用pop方便?br />  2Qorg.apache.log4j.MDC。MDC是Mapped Diagnostic Context的简写,意ؓ(f)映射诊断环境Q提供了(jin)一个Map对象来保存信息。Map对象使用Key、Value的Ş式保存倹{?br />  CZ代码Q?br />    PatternLayout layout = new PatternLayout("%m %X{name} %X{ip}%n");
    ConsoleAppender appender = new ConsoleAppender(layout);
    Logger log = Logger.getLogger(Test.class);
    log.addAppender(appender);
    
    log.debug("Start");
    //d信息到Map?br />    MDC.put("name", "zhengyp1");
    MDC.put("ip", "192.168.1.1");
    log.debug("Test1");
    
    //d信息到Map中,若Key重复Q则覆盖之前的?br />    MDC.put("name", "zhengyp2");
    MDC.put("ip", "192.168.1.2");
    log.debug("Test2");
    
    //信息从Map中移除,此时信息不再输出
    MDC.remove("name");
    MDC.remove("ip");
    log.debug("End");
q个CZ演示?jin)MDC的基本用法,格式化信息用的也是PatternLayoutQ模式字Wؓ(f)"%X"Q其格式必须?%X{Key}"。其中Key是?Map对象d信息时putҎ(gu)所用的KeyQ这里ؓ(f)name和ip。由于可以?%X{Key}"输出信息Q因此MDC使用h?x)比NDC更灵zR此外,MDCq提供了(jin)getҎ(gu)来获取指定Key的信息?br />六、小l?br />  用了(jin)q半个月Q终于大概掌握了(jin)Log4J。由于本文是边学边写的,目的是将Log4J的用法记录下来,而非提供一份中文参考,因此内容q不l致Q但量提供?jin)示例。不q到最后才发现Q示例存在问题,其实Logger做ؓ(f)cȝstatic成员比较恰当Q而我Z(jin)图方便,竟直接写C(jin)mainҎ(gu)中,q一点还h意?br />  此外Q这里再推荐一下《The Complete log4j Manual》,是对Log4J较详l的介绍Q在|上可以扑ֈQ只不过是英文的?/p>


Binary 2006-09-01 13:25 发表评论
]]>
使用Jakarta Commons Pool处理对象池化http://www.tkk7.com/lijiajia418/archive/2006/08/24/65600.htmlBinaryBinaryThu, 24 Aug 2006 09:49:00 GMThttp://www.tkk7.com/lijiajia418/archive/2006/08/24/65600.htmlhttp://www.tkk7.com/lijiajia418/comments/65600.htmlhttp://www.tkk7.com/lijiajia418/archive/2006/08/24/65600.html#Feedback0http://www.tkk7.com/lijiajia418/comments/commentRss/65600.htmlhttp://www.tkk7.com/lijiajia418/services/trackbacks/65600.html阅读全文

Binary 2006-08-24 17:49 发表评论
]]>
Apache Commons Chain明手?/title><link>http://www.tkk7.com/lijiajia418/archive/2006/08/22/64999.html</link><dc:creator>Binary</dc:creator><author>Binary</author><pubDate>Tue, 22 Aug 2006 02:47:00 GMT</pubDate><guid>http://www.tkk7.com/lijiajia418/archive/2006/08/22/64999.html</guid><wfw:comment>http://www.tkk7.com/lijiajia418/comments/64999.html</wfw:comment><comments>http://www.tkk7.com/lijiajia418/archive/2006/08/22/64999.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/lijiajia418/comments/commentRss/64999.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/lijiajia418/services/trackbacks/64999.html</trackback:ping><description><![CDATA[     摘要: 基本对象 1Q?   Command 接口。它? Commons Chain ?..  <a href='http://www.tkk7.com/lijiajia418/archive/2006/08/22/64999.html'>阅读全文</a><img src ="http://www.tkk7.com/lijiajia418/aggbug/64999.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/lijiajia418/" target="_blank">Binary</a> 2006-08-22 10:47 <a href="http://www.tkk7.com/lijiajia418/archive/2006/08/22/64999.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开始用Commons Chain Q第一部分Q?/title><link>http://www.tkk7.com/lijiajia418/archive/2006/08/22/64990.html</link><dc:creator>Binary</dc:creator><author>Binary</author><pubDate>Tue, 22 Aug 2006 02:40:00 GMT</pubDate><guid>http://www.tkk7.com/lijiajia418/archive/2006/08/22/64990.html</guid><wfw:comment>http://www.tkk7.com/lijiajia418/comments/64990.html</wfw:comment><comments>http://www.tkk7.com/lijiajia418/archive/2006/08/22/64990.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/lijiajia418/comments/commentRss/64990.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/lijiajia418/services/trackbacks/64990.html</trackback:ping><description><![CDATA[ <a border="" src="http://www.matrix.org.cn/resource/upload/forum/2005_12_14_001758_XrhGVLRcZQ.gif" alt="" align="" hspace="" vspace="" width="" height=""> <span id="LblContent">作ؓ(f)E序开发h员,我们l常需要对一个实际上E序性的pȝ应用面向对象的方法。商业分析家和管理h员描q这L(fng)pȝ旉常不用类层次和序列图Q而是使用程囑֒工作图表。但是不论如何,使用面向对象的方法解册些问题时?x)带来更多的灉|性。面向对象的设计模式提供?jin)有用的l构和行为来描述q种序的处理,比如模版Ҏ(gu)QTemplate MethodQ[GoF]和责任链QChain of ResponsibilityQ[GoF]?br /><br />Jakarta Commons的子目Chain上qC个模式组合成一个可复用的Java框架用于描述序的处理流E。这个在Jakarta Commons projectC֌中开发的框架Q已l被q泛的接受ƈ且用于许多有趣的应用中Q特别的是他被Struts和Shale应用框架作ؓ(f)处理HTTPh处理的基机制。你可以在需要定义和执行一l连l的步骤时用Commons Chain?br /><br />至于l典设计模式Q开发者和架构师普遍用模版方法(Template MethodQ造型序处理。模版方法(Template MethodQ中使用一个抽象的父类定义使用的算法:(x)处理的步骤,具体实现交给子类。当?dng)父类也可以?f)法所使用的方法提供一个缺省实现?br /><br />׃模版Ҏ(gu)QTemplate MethodQ依赖扎(k)—子cdȝ承定义了(jin)法的父cZ—因此用这个模式的软g表现出紧耦合而且~少灉|性。又׃实现cL加自q行ؓ(f)前必L展父c,沟每⑷嗽北q拗朴诶嗖愦沃校䄦拗屏顺l蛏杓频牧榛钚ʎommons Chain使用配置文g定义法Q在E序q行时解析配|文Ӟ从而很好的解决?jin)这个问题?br /><br />现在来看一下Commons Chain是怎样工作的,我们从一个h造的例子开始:(x)二手车销售员的商业流E。下面是销售流E的步骤Q?br />1.        得到用户信息<br />2.        试R<br />3.        谈判销?br />4.        安排财务<br />5.        l束销?br /><br />现在假设使用模版Ҏ(gu)QTemplate MethodQ造型q个程。首先徏立一个定义了(jin)法的抽象类Q?br /><br /><b>清单1</b><br /><pre class="overflow" title="pre code">public abstract class SellVehicleTemplate {<br />        public void sellVehicle() {<br />        getCustomerInfo();<br />        testDriveVehicle();<br />        negotiateSale();<br />        arrangeFinancing();<br />        closeSale();<br />        }<br /><br />        public abstract void getCustomerInfo();<br />        public abstract void testDriveVehicle();<br />        public abstract void negotiateSale();<br />        public abstract void arrangeFinancing();<br />        public abstract void closeSale();        <br />}</pre><br /><br />现在来看一下怎样用Commons Chain实现q个程。首先,下蝲Commons Chain。你可以直接下蝲最新的zip或tar文gQ也可以从CVS或者SubVersion源码库检出Commons Chain模块得到最新的代码。解压羃打包文gQ将commons-chain.jar攑օ你的classpath中?br /><br />使用Commons Chain实现q个商业程Q必d程中的每一步写成一个类Q这个类需要有一个public的方法execute()。这和传l的命o(h)模式QCommand patternQ实现相同。下面简单实C(jin)“得到用户信息”:(x)<br /><br /><b>清单2</b><br /><pre class="overflow" title="pre code">package com.jadecove.chain.sample;<br /><br />import org.apache.commons.chain.Command;<br />import org.apache.commons.chain.Context;<br /><br />public class GetCustomerInfo implements Command {<br />        public boolean execute(Context ctx) throws Exception {<br />                System.out.println("Get customer info");<br />                ctx.put("customerName","George Burdell");<br />                return false;<br />        }<br />}</pre><br /><br />׃只是演示Q这个类q没有做很多工作。这里将用户名放入了(jin)Context对象ctx中。这个Context对象q接?jin)各个命令。暂时先这个对象想象成Ҏ(gu)关键字存取值的哈希表。所有后来的命o(h)可以通过它访问刚才放入的用户名。TestDriveVehicleQNegotiateSale?ArrangeFinancing命o(h)的实现只是简单的打印?jin)将执行什么操作?br /><br /><b>清单3</b><br /><pre class="overflow" title="pre code">package com.jadecove.chain.sample;<br /><br />import org.apache.commons.chain.Command;<br />import org.apache.commons.chain.Context;<br /><br />public class TestDriveVehicle implements Command {<br />        public boolean execute(Context ctx) throws Exception {<br />                System.out.println("Test drive the vehicle");<br />                return false;<br />        }<br />}<br /><br />public class NegotiateSale implements Command {<br />        public boolean execute(Context ctx) throws Exception {<br />                System.out.println("Negotiate sale");<br />                return false;<br />        }<br />}<br /><br />public class ArrangeFinancing implements Command {<br />        public boolean execute(Context ctx) throws Exception {<br />                System.out.println("Arrange financing");<br />                return false;<br />        }<br />}</pre><br /><br />CloseSale从Context对象中取出GetCustomerInfo攑օ的用户名Qƈ其打印?br /><b>清单4</b><br /><pre class="overflow" title="pre code">package com.jadecove.chain.sample;<br /><br />import org.apache.commons.chain.Command;<br />import org.apache.commons.chain.Context;<br /><br />public class CloseSale implements Command {<br />        public boolean execute(Context ctx) throws Exception {<br />                System.out.println("Congratulations "<br />                  +ctx.get("customerName")<br />                        +", you bought a new car!");<br />                return false;<br />        }<br />}</pre><br /><br />现在你可以将q个程定义成一个序列(或者说“命令链”)(j)?br /><b>清单5</b><br /><pre class="overflow" title="pre code">package com.jadecove.chain.sample;<br /><br />import org.apache.commons.chain.impl.ChainBase;<br />import org.apache.commons.chain.Command;<br />import org.apache.commons.chain.Context;<br />import org.apache.commons.chain.impl.ContextBase;<br /><br />public class SellVehicleChain extends ChainBase {<br />        public SellVehicleChain() {<br />                super();<br />                addCommand(new GetCustomerInfo());<br />                addCommand(new TestDriveVehicle());<br />                addCommand(new NegotiateSale());<br />                addCommand(new ArrangeFinancing());<br />                addCommand(new CloseSale());<br />        }<br />        public static void main(String[] args) throws Exception {<br />                Command process = new SellVehicleChain();<br />                Context ctx = new ContextBase();<br />                process.execute(ctx);<br />        }<br />}</pre><br /><br />q行q个cd?x)输Z下结果:(x)<br /><span style="COLOR: blue">Get customer info<br />Test drive the vehicle<br />Negotiate sale<br />Arrange financing<br />Congratulations George Burdell, you bought a new car!</span><br /><br />在进一步深入之前,让我们来看一下我们用了(jin)的Commons Chain的类和接口?br /><br /></span> </a> <img src="http://www.matrix.org.cn/resource/upload/forum/2005_12_14_001758_XrhGVLRcZQ.gif" /> <br />Command cdChaincȝ关系是l合模式QComposite patternQ[GoF]的例子:(x)Chain不仅由多个Commandl成Q而且自己也是Command。这使你可以非常单得单个命令(CommandQ替换成由多个命令(CommandQ组成的链(ChainQ。这个由Command对象唯一操作定义的方法代表了(jin)一个直接的命o(h)Q?br /><br />public boolean execute(Context context);<br /><br />参数context仅仅是一个存放了(jin)名称-值对的集合。接口Context在这里作Z个标记接口:(x)它扩展了(jin)java.util.Map但是没有dMҎ(gu)的行为。于此相反,cContextBase不仅提供?jin)对Map的实现而且增加?jin)一个特性:(x)属?域透明。这个特性可以通过使用Map的put和get Ҏ(gu)操作JavaBean的域Q当然这些域必须使用标准的getFoo和setFooҎ(gu)定义。那些通过JavaBean的“setter”方法设|的|可以通过对应的域名称Q用Map的getҎ(gu)得到。同P那些用Map的putҎ(gu)讄的值可以通过JavaBean的“getter”方法得到?br /><br />例如Q我们可以创Z个专门的context提供昑ּ的customerName属性支持?br /><b>清单6</b><br /><pre class="overflow" title="pre code">package com.jadecove.chain.sample;<br /><br />import org.apache.commons.chain.impl.ContextBase;<br /><br />public class SellVehicleContext extends ContextBase {<br /><br />        <br />        private String customerName;<br /><br />        public String getCustomerName() {<br />                return customerName;<br />        }<br />        <br />        public void setCustomerName(String name) {<br />                this.customerName = name;<br />        }<br />}</pre><br /><br />现在你既可以q行Map的一般属性存取操作同时也可以使用昑ּ的JavaBean的访问和修改域的Ҏ(gu)Q这两个生同L(fng)效果。但是首先你需要在q行SellVehicleChain时实例化SellVehiceContext而不是ContextBase?br /><b>清单7</b><br /><pre class="overflow" title="pre code">public static void main(String[] args) throws Exception {<br />                Command process = new SellVehicleChain();<br />                Context ctx = new SellVehicleContext();<br />                process.execute(ctx);<br />        }</pre><br /><br />管你不改变GetCustomerInfo中存攄户名的方法——仍然用ctx.put("customerName", "George Burdell")——你可以在CloseSale中用getCustomerName()Ҏ(gu)得到用户名?br /><b>清单8</b><br /><pre class="overflow" title="pre code">        public boolean execute(Context ctx) throws Exception {<br />            SellVehicleContext myCtx = (SellVehicleContext) ctx;<br />            System.out.println("Congratulations "<br />                                   + myCtx.getCustomerName()<br />                                  + ", you bought a new car!");<br />            return false;<br />        }</pre><br /><br />那些依赖cd安全和context的显式域的命令(CommandQ可以利用标准的getter和setterҎ(gu)。当一些新的命令(CommandQ被dӞ它们可以不用考虑context的具体实玎ͼ直接通过Map的get和put操作属性。不论采用何U机ӞContextBasec都可以保证命o(h)QCommandQ间可以通过context互操作?br /><br />下面q个例子展示?jin)如何用Commons Chain的API建立q执行一l顺序的命o(h)。当?dng)和现在大多数Java软g一PCommons Chain可以使用XML文g作ؓ(f)配置文g。你可以“汽车销售”流E的步骤在XML文g中定义。这个文件有个规范的命名chain- config.xml?br /><br /><b>清单9</b><br /><pre class="overflow" title="pre code"><catalog><br />  <chain name="sell-vehicle"><br />    <command   id="GetCustomerInfo"<br />        className="com.jadecove.chain.sample.GetCustomerInfo"/><br />    <command   id="TestDriveVehicle"<br />        className="com.jadecove.chain.sample.TestDriveVehicle"/><br />    <command   id="NegotiateSale"<br />        className="com.jadecove.chain.sample.NegotiateSale"/><br />    <command   id="ArrangeFinancing"<br />        className="com.jadecove.chain.sample.ArrangeFinancing"/><br />    <command   id="CloseSale"<br />        className="com.jadecove.chain.sample.CloseSale"/><br />  </chain><br /></catalog></pre><br /><br />Chain的配|文件可以包含多个链定义Q这些链定义可以集合q不同的~目中。在q个例子中,铑֮义在一个默认的~目中定义。事实上Q你可以在这个文件中定义多个名字的编目,每个~目可拥有自q铄?br /><br />现在你可以用Commons Chain提供的类载入~目q得到指定的链,而不用像SellVehicleChain中那栯己在E序中定义一l命令:(x)<br /><b>清单10</b><br /><pre class="overflow" title="pre code">package com.jadecove.chain.sample;<br /><br />import org.apache.commons.chain.Catalog;<br />import org.apache.commons.chain.Command;<br />import org.apache.commons.chain.Context;<br />import org.apache.commons.chain.config.ConfigParser;<br />import org.apache.commons.chain.impl.CatalogFactoryBase;<br /><br />public class CatalogLoader {<br />        private static final String CONFIG_FILE = <br />                "/com/jadecove/chain/sample/chain-config.xml";<br />        private ConfigParser parser;<br />        private Catalog catalog;<br />        <br />        public CatalogLoader() {<br />                parser = new ConfigParser();<br />        }<br />        public Catalog getCatalog() throws Exception {<br />                if (catalog == null) {<br />                <br />        parser.parse(this.getClass().getResource(CONFIG_FILE));                <br />        <br />                }<br />                catalog = CatalogFactoryBase.getInstance().getCatalog();<br />                return catalog;<br />        }<br />        public static void main(String[] args) throws Exception {<br />                CatalogLoader loader = new CatalogLoader();<br />                Catalog sampleCatalog = loader.getCatalog();<br />                Command command = sampleCatalog.getCommand("sell-vehicle");<br />                Context ctx = new SellVehicleContext();<br />                command.execute(ctx);<br />        }<br />}</pre><br /><br />Chain 使用Commons Digester来读取和解析配置文g。因此你需要将Commons Digester.jar加入classpath中。我使用?.6版本q且工作得很好。Digester使用?jin)Commons CollectiosQ我使用的版本是3.1Q,Commons LoggingQ版?.0.4Q,Commons BeanUtilsQ?.7.0Q,因此你也需要将它们的jar文g加入classpath中。在加入q些jar后,CatalogLoader可以被~译和运行,它的输出和另外两个测试完全相同?br /><br />现在你可以在XML文g中定义链Qƈ可以在程序中得到q个链(别忘?jin)链也是命o(h)Q,q样扩展的可能性和E序的灵zL可以说是无限的。假设过E“安排胦(ch)务”实际上׃个完全分ȝ商业部门处理。这个部门希望ؓ(f)q种销售徏立自q工作程?Chain提供?jin)嵌套链来实现这个要求。因为链本n是命o(h)Q因此你可以用指向另一个链的引用替换一个单一用途的命o(h)。下面是增加?jin)新程的链的定义?x)<br /><b>清单11</b><br /><pre class="overflow" title="pre code"><catalog name="auto-sales"><br />   <chain name="sell-vehicle"><br />         <command   id="GetCustomerInfo"<br />                 className="com.jadecove.chain.sample.GetCustomerInfo"/><br />         <command   id="TestDriveVehicle"<br />                 className="com.jadecove.chain.sample.TestDriveVehicle"/><br />         <command   id="NegotiateSale"<br />                 className="com.jadecove.chain.sample.NegotiateSale"/><br />         <command<br />                 className="org.apache.commons.chain.generic.LookupCommand"<br />             catalogName="auto-sales"<br />                      name="arrange-financing"<br />                  optional="true"/><br />         <command   id="CloseSale"<br />                 className="com.jadecove.chain.sample.CloseSale"/><br />   </chain><br />   <chain name="arrange-financing"><br />         <command   id="ArrangeFinancing"<br />                 className="com.jadecove.chain.sample.ArrangeFinancing"/><br />   </chain><br /></catalog></pre><br /><br />Commons Chain提供?jin)一个常用的命o(h)LookupCommand来查扑֒执行另一个链。属性optional用于控制当指定的嵌套链没有找到时如何处理?optional=trueӞ即链没扑ֈQ处理也?x)l。反之,LookupCommand抛?IllegalArgumentExceptionQ告知指定的命o(h)未找到?br /><br />在下面三U情况下Q命令链结束:(x)<br />1.        命o(h)的executeҎ(gu)q回true<br />2.        q行C(jin)铄头<br />3.        命o(h)抛出异常<br /><br />当链完全处理完一个过E后Q命令就q回true。这是责任链模式QChain of ResponsibilityQ的基本概念。处理从一个命令传递到另一个命令,直到某个命o(h)QCommandQ处理了(jin)q个命o(h)。如果在到达命o(h)序列头时仍没有处理q回trueQ也假设铑ַl正常结束?br /><br />当有命o(h)抛出错误旉׃(x)非正常结束。在Commons Chain中,如果有命令抛出错误,铄执行׃(x)中断。不论是q行旉误(runtime exceptionQ还是应用错误(application exceptionQ,都会(x)抛出l链的调用者。但是许多应用都需要对在命令之外定义的错误做明的处理。Commons Chain提供?jin)Filter接口来满个要求。Filterl承?jin)CommandQ添加了(jin)一个名为postprocess的方法?br /><br />public boolean postprocess(Context context, Exception exception);<br />只要Filter的executeҎ(gu)被调用,不论铄执行q程中是否抛出错误,Commons Chain都将保证Filter的postprocessҎ(gu)被调用。和servlet的过滤器QfilterQ相同,Commons Chain的Filter按它们在链中的顺序依ơ执行。同PFilter的postprocessҎ(gu)按倒序执行。你可以使用q个Ҏ(gu)实现自q错误处理。下面是一个用于处理我们例子中的错误的FilterQ?br /><b>清单12</b><br /><pre class="overflow" title="pre code">package com.jadecove.chain.sample;<br /><br />import org.apache.commons.chain.Context;<br />import org.apache.commons.chain.Filter;<br /><br />public class SellVehicleExceptionHandler implements Filter {<br /><br />        public boolean execute(Context context) throws Exception {<br />                System.out.println("Filter.execute() called.");<br />                return false;<br />        }<br /><br />        public boolean postprocess(Context context,<br />                                 Exception exception) {<br />                if (exception == null) return false;<br />                System.out.println("Exception "<br />                              + exception.getMessage()<br />                              + " occurred.");<br />                return true;<br />        }<br />}</pre><br /><br />Filter在配|文件中的定义就和普通的命o(h)QCommandQ定义相同:(x)<br /><b>清单13</b><br /><pre class="overflow" title="pre code"><chain name="sell-vehicle"><br />  <command   id="ExceptionHandler"<br />     className =<br />           "com.jadecove.chain.sample.SellVehicleExceptionHandler"/><br />  <command   id="GetCustomerInfo"<br />      className="com.jadecove.chain.sample.GetCustomerInfo"/></pre><br /><br />Filter 的executeҎ(gu)按定义的序列调用。然而,它的postprocessҎ(gu)在链执行完毕或抛出错误后执行。当一个错误被抛出Ӟ postprocessҎ(gu)处理完后?x)返回trueQ表C错误处理已l完成。链的执行ƈ不会(x)此l束Q但是本质上来说q个错误被捕捉而且不会(x)再向外抛出。如果postprocessҎ(gu)q回falseQ那错误?x)l向外抛出,然后铑ְ?x)非正常l束?br /><br />让我们假设ArrangeFinancing因ؓ(f)用户信用卡损坏抛出错误。SellVehicleExceptionHandlerp捕捉到这个错误,E序输出如下Q?br /><span style="COLOR: blue">Filter.execute() called.<br />Get customer info<br />Test drive the vehicle<br />Negotiate sale<br />Exception Bad credit occurred.</span><br /><br />l合?jin)过滤器QfilterQ和子链技术后Q你可以造型很复杂的工作程?br /><br />Commons Chain是一个很有前途的框架Q现在仍在开发,新的功能被频J地d到其中。在下一关于Commons Chain的文章中Q我们将研究Struts 1.3中是如何使用Commons Chain的?br /><br />Struts 1.3中用完全使用Commons Chain的类替换?jin)原来的处理HTTPh的类。如果你以前自己定制qStruts的请求处理(request processorQ,你将发现处理q个问题时Commons Chain为程序带来了(jin)很好的灵zL?img src ="http://www.tkk7.com/lijiajia418/aggbug/64990.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/lijiajia418/" target="_blank">Binary</a> 2006-08-22 10:40 <a href="http://www.tkk7.com/lijiajia418/archive/2006/08/22/64990.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.tkk7.com/" title="亚洲av成人片在线观看">亚洲av成人片在线观看</a> <div class="friend-links"> </div> </div> </footer> վ֩ģ壺 <a href="http://masfd.com" target="_blank">˳ɫ99999߹ۿ</a>| <a href="http://lyczyb.com" target="_blank">Ѵվ߹</a>| <a href="http://kkxzz.com" target="_blank">Ƭ߹ۿѴȫӰ</a>| <a href="http://flowsns.com" target="_blank">eeuss</a>| <a href="http://102sds.com" target="_blank">þùƷһ</a>| <a href="http://339258.com" target="_blank">һƵѹۿ</a>| <a href="http://meyume.com" target="_blank">ĻƵ</a>| <a href="http://znboxcdn107.com" target="_blank">һëƬëƬëƬ</a>| <a href="http://6nn5.com" target="_blank">vƬ߹ۿ</a>| <a href="http://tpwelert.com" target="_blank">Ʒϵ߹ۿ </a>| <a href="http://aplus178.com" target="_blank">99ƵѾƷǿ6</a>| <a href="http://fenxue520.com" target="_blank">պƵ</a>| <a href="http://xx16xx.com" target="_blank">aԴ</a>| <a href="http://lemonbt.com" target="_blank">߹ۿѲ</a>| <a href="http://wulegu.com" target="_blank">˳վ18ֹһ</a>| <a href="http://xieehuomh.com" target="_blank">޾Ʒھþ </a>| <a href="http://ywzms.com" target="_blank">2021ƷƵѲ</a>| <a href="http://yg36.com" target="_blank">պѸƵ</a>| <a href="http://njbyz.com" target="_blank">Ƶ</a>| <a href="http://27simnjingmiguan.com" target="_blank">Ѹ</a>| <a href="http://dqmovie.com" target="_blank">˳ӰԺ߹ۿ</a>| <a href="http://chuoche.com" target="_blank">ƷѾƷ</a>| <a href="http://pengkaimjg.com" target="_blank">˳վ߲ӰԺ </a>| <a href="http://002309.com" target="_blank">AV߹ۿ</a>| <a href="http://www-777768.com" target="_blank">avַ</a>| <a href="http://www-715111.com" target="_blank">޾Ʒɫҹרպ</a>| <a href="http://gift1town.com" target="_blank">avr</a>| <a href="http://gyqcy.com" target="_blank">þþƷһԡ</a>| <a href="http://9797ww.com" target="_blank">޾Ʒҹ߹ۿ</a>| <a href="http://hhrrrr.com" target="_blank">ĻŮ</a>| <a href="http://micehunan.com" target="_blank">AVר߳</a>| <a href="http://yangguang882.com" target="_blank">ֳִִӲ3pƵ</a>| <a href="http://dwj28.com" target="_blank">mv߹ۿ</a>| <a href="http://520xiang.com" target="_blank">һëƬ</a>| <a href="http://wuiso.com" target="_blank">պѵӰ߹ۿ</a>| <a href="http://www-774220.com" target="_blank">ݺɫúݺݺۺ</a>| <a href="http://sdkjkj.com" target="_blank">޹˾þþƷ</a>| <a href="http://jjv5.com" target="_blank">һŷһ</a>| <a href="http://lanchenews.com" target="_blank">պѸһëƬ</a>| <a href="http://whlnrsqsie.com" target="_blank">AVƬ</a>| <a href="http://peipeixiu.com" target="_blank">Ƭѹۿ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>