﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-关注基于J2EE的系统设计与实现,总结项目经验与思考。-随笔分类-java</title><link>http://www.blogjava.net/NeonWay/category/7515.html</link><description>spring、hibernate、webwork框架实践与探索。</description><language>zh-cn</language><lastBuildDate>Thu, 01 Mar 2007 23:02:49 GMT</lastBuildDate><pubDate>Thu, 01 Mar 2007 23:02:49 GMT</pubDate><ttl>60</ttl><item><title>在java中严格校验日期正确性</title><link>http://www.blogjava.net/NeonWay/archive/2007/01/25/95855.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Thu, 25 Jan 2007 02:09:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/25/95855.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/95855.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/25/95855.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/95855.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/95855.html</trackback:ping><description><![CDATA[
		<p>   /**<br />     * 检验输入是否为正确的日期格式(不含秒的任何情况),严格要求日期正确性,格式:yyyy-MM-dd HH:mm<br />     * @param sourceDate<br />     * @return<br />     */<br />    public static boolean checkDate(String sourceDate){<br />        if(sourceDate==null){<br />            return false;<br />        }<br />        try {<br />               SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");<br />               dateFormat.setLenient(false);<br />               dateFormat.parse(sourceDate);<br />               return true;<br />        } catch (Exception e) {<br />        }<br />         return false;<br />    }<br />    <br />    /**<br />     * 根据日期获得日期(不含秒的任何情况),严格要求日期正确性,格式:yyyy-MM-dd HH:mm<br />     * @param sourceDate<br />     * @return<br />     */<br />    public static Date getDate(String sourceDate) throws DataFormatException{<br />           if(sourceDate==null){<br />               throw new DataFormatException("源数据为null");<br />           }<br />           try {<br />                     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");<br />                     dateFormat.setLenient(false);<br />                      return dateFormat.parse(sourceDate);<br />           } catch (Exception e) {<br />                     throw new DataFormatException("源数据格式错误");<br />           }<br />    }<br /><br /><br />//日期格式可以自定义.<br />    public static void main(String[] args) throws IOException {<br />             String s="2006-01-30 12:26";<br />             System.out.println(s+"是否为日期:"+Test.checkDate(s));<br /><br />             s="2006-1-30 12:26";<br />             System.out.println(s+"是否为日期:"+Test.checkDate(s));<br />        <br />            s="2006-01-32 12:26";<br />            System.out.println(s+"是否为日期:"+Test.checkDate(s));<br />    }</p>
<img src ="http://www.blogjava.net/NeonWay/aggbug/95855.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-25 10:09 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/25/95855.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java里抽象类和接口的区别 </title><link>http://www.blogjava.net/NeonWay/archive/2007/01/22/95243.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 22 Jan 2007 03:22:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/22/95243.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/95243.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/22/95243.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/95243.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/95243.html</trackback:ping><description><![CDATA[摘自:http://blog.csdn.net/shooray/archive/2006/10/31/1357935.aspx<br /><br /><font face="Verdana">abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制，正是由于这两种机制的存在，才赋予了Java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性，甚至可以相互替换，因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实，两者之间还是有很大的区别的，对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析，试图给开发者提供一个在二者之间进行选择的依据。  <br /><br />理解抽象类  <br /><br />abstract class和interface在Java语言中都是用来进行抽象类（本文中的抽象类并非从abstract class翻译而来，它表示的是一个抽象体，而abstract class为Java语言中用于定义抽象类的一种方法，请读者注意区分）定义的，那么什么是抽象类，使用抽象类能为我们带来什么好处呢？  <br /><br />在面向对象的概念中，我们知道所有的对象都是通过类来描绘的，但是反过来却不是这样。并不是所有的类都是用来描绘对象的，如果一个类中没有包含足够的信息来描绘一个具体的对象，这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念，是对一系列看上去不同，但是本质上相同的具体概念的抽象。比如：如果我们进行一个图形编辑软件的开发，就会发现问题领域存在着圆、三角形这样一些具体概念，它们是不同的，但是它们又都属于形状这样一个概念，形状这个概念在问题领域是不存在的，它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念，所以用以表征抽象概念的抽象类是不能够实例化的。  <br /><br />在面向对象领域，抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述，但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类，而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，因此它可以是不允许修改的；同时，通过从这个抽象体派生，也可扩展此模块的行为功能。熟悉OCP的读者一定知道，为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle)，抽象类是其中的关键所在。  <br /><br /><br />从语法定义层面看abstract class和interface  <br /><br />在语法层面，Java语言对于abstract class和interface给出了不同的定义方式，下面以定义一个名为Demo的抽象类为例来说明这种不同。  <br /><br />使用abstract class的方式定义Demo抽象类的方式如下：  <br /><br />abstract class Demo ｛  <br /> abstract void method1();  <br /> abstract void method2();  <br /> …  <br />｝  <br /><br />使用interface的方式定义Demo抽象类的方式如下：  <br /><br />interface Demo {  <br /> void method1();  <br /> void method2();  <br /> …  <br />}  <br /><br />在abstract class方式中，Demo可以有自己的数据成员，也可以有非abstarct的成员方法，而在interface方式的实现中，Demo只能够有静态的不能被修改的数据成员（也就是必须是static final的，不过在interface中一般不定义数据成员），所有的成员方法都是abstract的。从某种意义上说，interface是一种特殊形式的abstract class。  <br /><br />      从编程的角度来看，abstract class和interface都可以用来实现"design by contract"的思想。但是在具体的使用上面还是有一些区别的。  <br /><br />首先，abstract class在Java语言中表示的是一种继承关系，一个类只能使用一次继承关系。但是，一个类却可以实现多个interface。也许，这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。  <br /><br />其次，在abstract class的定义中，我们可以赋予方法的默认行为。但是在interface的定义中，方法却不能拥有默认行为，为了绕过这个限制，必须使用委托，但是这会 增加一些复杂性，有时会造成很大的麻烦。  <br /><br />在抽象类中不能定义默认行为还存在另一个比较严重的问题，那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面（一般通过abstract class或者interface来表示）以适应新的情况（比如，添加新的方法或者给已用的方法中添加新的参数）时，就会非常的麻烦，可能要花费很多的时间（对于派生类很多的情况，尤为如此）。但是如果界面是通过abstract class来实现的，那么可能就只需要修改定义在abstract class中的默认行为就可以了。  <br /><br />同样，如果不能在抽象类中定义默认行为，就会导致同样的方法实现出现在该抽象类的每一个派生类中，违反了"one rule，one place"原则，造成代码重复，同样不利于以后的维护。因此，在abstract class和interface间进行选择时要非常的小心。  <br /><br /><br />从设计理念层面看abstract class和interface  <br /><br />上面主要从语法定义和编程的角度论述了abstract class和interface的区别，这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面：abstract class和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。  <br /><br />前面已经提到过，abstarct class在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在"is a"关系，即父类和派生类在概念本质上应该是相同的（参考文献〔3〕中有关于"is a"关系的大篇幅深入的论述，有兴趣的读者可以参考）。对于interface 来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。  <br /><br />考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示：  <br /><br />使用abstract class方式定义Door：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br />}  <br /><br />   <br />使用interface方式定义Door：  <br /><br /><br />interface Door {  <br /> void open();  <br /> void close();  <br />}  <br /><br />   <br />其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。  <br /><br />如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstract class和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）？下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。  <br /><br />解决方案一：  <br /><br />简单的在Door的定义中增加一个alarm方法，如下：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br /> abstract void alarm();  <br />}  <br /><br />   <br />或者  <br /><br />interface Door {  <br /> void open();  <br /> void close();  <br /> void alarm();  <br />}  <br /><br />   <br />那么具有报警功能的AlarmDoor的定义方式如下：  <br /><br />class AlarmDoor extends Door {  <br /> void open() { … }  <br /> void close() { … }  <br /> void alarm() { … }  <br />}  <br /><br />   <br />或者  <br /><br />class AlarmDoor implements Door ｛  <br /> void open() { … }  <br /> void close() { … }  <br /> void alarm() { … }  <br />｝  <br /><br />这种方法违反了面向对象设计中的一个核心原则ISP（Interface Segregation Priciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。  <br /><br />解决方案二：  <br /><br />既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用abstract class方式定义；两个概念都使用interface方式定义；一个概念使用abstract class方式定义，另一个概念使用interface方式定义。  <br /><br />显然，由于Java语言不支持多重继承，所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。  <br /><br />如果两个概念都使用interface方式来定义，那么就反映出两个问题：1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。  <br /><br />如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，abstract class在Java语言中表示一种继承关系，而继承关系在本质上是"is a"关系。所以对于Door这个概念，我们应该使用abstarct class方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br />}  <br />interface Alarm {  <br /> void alarm();  <br />}  <br />class AlarmDoor extends Door implements Alarm {  <br /> void open() { … }  <br /> void close() { … }  <br />    void alarm() { … }  <br />}  <br /><br />   <br />这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系，interface表示的是"like a"关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有Door的功能，那么上述的定义方式就要反过来了。  </font> <img src ="http://www.blogjava.net/NeonWay/aggbug/95243.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-22 11:22 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/22/95243.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring事务探索 </title><link>http://www.blogjava.net/NeonWay/archive/2007/01/15/93872.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 15 Jan 2007 02:10:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/15/93872.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/93872.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/15/93872.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/93872.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/93872.html</trackback:ping><description><![CDATA[引自：<a href="http://www.javaeye.com/topic/11190?page=1">http://www.javaeye.com/topic/11190?page=1<br /></a><br /><p>spring自建事务管理模块。而且这个事务管理是一个抽象设计，可以应用到很多场合，包括普通的DataSource，jta，jms和hibernate上。</p><p>要正确使用spring的事务，首先需要了解spring在事务设计上的一些概念 <br />统观spring事务，围绕着两个核心PlatformTransactionManager和TransactionStatus </p><p>PlatformTransactionManager直译过来就是平台相关事务，这里的平台指的是“事务源”，包括刚才我说的DataSource，jta等等。这些无一不是一个事务源。广义的说，凡是可以完成事务性操作的对象，都可以设计出相对应的PlatformTransactionManager，只要这个事务源支持commit，rollback和getTransaction语意。</p><p>查看spring代码，可以发现这些manager实现事务，就是调用事务源的事务操作方法</p><p>比如</p><p>HibernateTransactionManager <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">protected</span><span> </span><span class="keyword">void</span><span> doCommit(DefaultTransactionStatus status) {   </span></span></li><li class=""><span>        HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();   </span></li><li class="alt"><span>        </span><span class="keyword">if</span><span> (status.isDebug()) {   </span></li><li class=""><span>            logger.debug(</span><span class="string">"Committing Hibernate transaction on session ["</span><span> +   </span></li><li class="alt"><span>                    txObject.getSessionHolder().getSession() + </span><span class="string">"]"</span><span>);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            txObject.getSessionHolder().getTransaction().commit();   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>...   </span></li><li class="alt"><span>  </span></li><li class=""><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>jdbc 的DataSourceTransactionManager <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">protected</span><span> </span><span class="keyword">void</span><span> doCommit(DefaultTransactionStatus status) {   </span></span></li><li class=""><span>        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();   </span></li><li class="alt"><span>        Connection con = txObject.getConnectionHolder().getConnection();   </span></li><li class=""><span>        </span><span class="keyword">if</span><span> (status.isDebug()) {   </span></li><li class="alt"><span>            logger.debug(</span><span class="string">"Committing JDBC transaction on connection ["</span><span> + con + </span><span class="string">"]"</span><span>);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            con.commit();   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        ...   </span></li><li class="alt"><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>那么PlatformTransactionManager以什么依据处理事务呢？ <br />是TransactionStatus <br />查看api发现这个接口有三个方法 <br />isNewTransaction() ，isRollbackOnly()，setRollbackOnly() <br />PlatformTransactionManager就是根据前两个方法决定是否要创建一个新事务，是要递交还是回滚。至于第三个方法是改变事务当前状态的，很多地方都要用到，偏偏PlatformTransactionManager自身好像不怎么用，毕竟事务状态的改变是由程序员代码决定的，不需要一个manager多管闲事。</p><p>总结上面所说的，spring的事务由PlatformTransactionManager管理，manager最后调用事务源的方法来实现一个事务过程。而manager通过TransactionStatus 来决定如何实现。 <br />接下去说spring事务中的TransactionTemplate和TransactionInterceptor</p><p>TransactionTemplate其实和spring中其他的template的作用类似，起到化简代码的作用，不要被它那么长的名字吓倒了，事实上这个template并不是什么非常核心的对象。如果比较学究派的，可以去看看template设计模式，在此就不再对此赘述了。 <br />为什么要有TransactionTemplate？先来看看如果没有TransactionTemplate，我们的代码该怎么写</p><p>先来看看spring reference中的一段代码 <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>DefaultTransactionDefinition def = </span><span class="keyword">new</span><span> DefaultTransactionDefinition()   </span></span></li><li class=""><span>def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);   </span></li><li class="alt"><span>  </span></li><li class=""><span>TransactionStatus status = transactionManager.getTransaction(def);   </span></li><li class="alt"><span>  </span></li><li class=""><span></span><span class="keyword">try</span><span> {   </span></li><li class="alt"><span>    </span><span class="comment">// execute your business logic here </span><span>  </span></li><li class=""><span>} </span><span class="keyword">catch</span><span> (MyException ex) {   </span></li><li class="alt"><span>    transactionManager.rollback(status);   </span></li><li class=""><span>    </span><span class="keyword">throw</span><span> ex;   </span></li><li class="alt"><span>}   </span></li><li class=""><span>transactionManager.commit(status);  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />这是直接使用transactionManager的例子，可以看到真正执行business logic 的地方是在try当中那段，前后的代码都是为了完成事务管理的。如果每个business logic都要写上那么一段，我肯定是疯了。我们翻出TransactionTemplate的代码看看他怎么化简了我们的代码 
<p></p><p></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">public</span><span> Object execute(TransactionCallback action) </span><span class="keyword">throws</span><span> TransactionException {   </span></span></li><li class=""><span>        TransactionStatus status = </span><span class="keyword">this</span><span>.transactionManager.getTransaction(</span><span class="keyword">this</span><span>);   </span></li><li class="alt"><span>        Object result = </span><span class="keyword">null</span><span>;   </span></li><li class=""><span>        </span><span class="keyword">try</span><span> {   </span></li><li class="alt"><span>            result = action.doInTransaction(status);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">catch</span><span> (RuntimeException ex) {   </span></li><li class=""><span>            </span><span class="comment">// transactional code threw application exception -&gt; rollback </span><span>  </span></li><li class="alt"><span>            rollbackOnException(status, ex);   </span></li><li class=""><span>            </span><span class="keyword">throw</span><span> ex;   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">catch</span><span> (Error err) {   </span></li><li class="alt"><span>            </span><span class="comment">// transactional code threw error -&gt; rollback </span><span>  </span></li><li class=""><span>            rollbackOnException(status, err);   </span></li><li class="alt"><span>            </span><span class="keyword">throw</span><span> err;   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">this</span><span>.transactionManager.commit(status);   </span></li><li class=""><span>        </span><span class="keyword">return</span><span> result;   </span></li><li class="alt"><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>同上面的代码如出一辙，前后是事务处理代码，当中那段result = action.doInTransaction(status);是我们的应用代码。至于action是什么，全看各位的需要了。但是有一点要主要，如果利用TransactionTemplate，那么他不管你扔出什么异常都会回滚事务，但是回滚的是哪个事务呢？继续挖代码 <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">private</span><span> </span><span class="keyword">void</span><span> rollbackOnException(TransactionStatus status, Throwable ex) </span><span class="keyword">throws</span><span> TransactionException {   </span></span></li><li class=""><span>        </span><span class="keyword">if</span><span> (logger.isDebugEnabled()) {   </span></li><li class="alt"><span>            logger.debug(</span><span class="string">"Initiating transaction rollback on application exception"</span><span>, ex);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            </span><span class="keyword">this</span><span>.transactionManager.rollback(status);   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">catch</span><span> (RuntimeException ex2) {   </span></li><li class="alt"><span>            logger.error(</span><span class="string">"Application exception overridden by rollback exception"</span><span>, ex);   </span></li><li class=""><span>            </span><span class="keyword">throw</span><span> ex2;   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">catch</span><span> (Error err) {   </span></li><li class="alt"><span>            logger.error(</span><span class="string">"Application exception overridden by rollback error"</span><span>, ex);   </span></li><li class=""><span>            </span><span class="keyword">throw</span><span> err;   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />真相大白，是对template所持有的某个transactionManager进行回滚。所以如果你的应用代码用的是事务源a的一些资源，比如到服务器a的一个datasource，但是你的transactionManager管理的是另一些资源，比如服务器b的一个datasource，代码铁定不会正常运行 
<p></p><p>特别是在一些多事务源的程序里，这点千万不能搞错。如果多个事务源之间要完成全局事务，还是老老实实用分布式事务管理服务吧（jta）</p><p>那么TransactionInterceptor是干什么的？这个是spring 的声明式事务的支持方式。因为用TransactionTemplate要硬编码，而且调整事务策略很麻烦（不是说不能调。举个例子原来程序抛出异常A需要回滚，现在不需要要，我就可以把a catch吃掉。这时候template就不会回滚了。但是每次调整都要重写编码。）而用TransactionInterceptor就可以将这些调整写在配置中。我们再来挖TransactionInterceptor的代码</p><p></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">public</span><span> Object invoke(MethodInvocation invocation) </span><span class="keyword">throws</span><span> Throwable {   </span></span></li><li class=""><span>        </span><span class="comment">// Work out the target class: may be null. </span><span>  </span></li><li class="alt"><span>        </span><span class="comment">// The TransactionAttributeSource should be passed the target class </span><span>  </span></li><li class=""><span>        </span><span class="comment">// as well as the method, which may be from an interface </span><span>  </span></li><li class="alt"><span>        Class targetClass = (invocation.getThis() != </span><span class="keyword">null</span><span>) ? invocation.getThis().getClass() : </span><span class="keyword">null</span><span>;   </span></li><li class=""><span>           </span></li><li class="alt"><span>        </span><span class="comment">// Create transaction if necessary </span><span>  </span></li><li class=""><span>        TransactionInfo txInfo = createTransactionIfNecessary(invocation.getMethod(), targetClass);   </span></li><li class="alt"><span>  </span></li><li class=""><span>        Object retVal = </span><span class="keyword">null</span><span>;   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            </span><span class="comment">// This is an around advice. </span><span>  </span></li><li class="alt"><span>            </span><span class="comment">// Invoke the next interceptor in the chain. </span><span>  </span></li><li class=""><span>            </span><span class="comment">// This will normally result in a target object being invoked. </span><span>  </span></li><li class="alt"><span>            retVal = invocation.proceed();   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">catch</span><span> (Throwable ex) {   </span></li><li class=""><span>            </span><span class="comment">// target invocation exception </span><span>  </span></li><li class="alt"><span>            doCloseTransactionAfterThrowing(txInfo, ex);   </span></li><li class=""><span>            </span><span class="keyword">throw</span><span> ex;   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">finally</span><span> {   </span></li><li class="alt"><span>            doFinally(txInfo);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        doCommitTransactionAfterReturning(txInfo);   </span></li><li class=""><span>  </span></li><li class="alt"><span>        </span><span class="keyword">return</span><span> retVal;   </span></li><li class=""><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />万变不离其宗。 
<p></p><p>所以使用spring的事务管理需要作这些事 <br />1，设置好事务源，比如DataSource，hibernate的session。如果有多个事务源要考虑他们之间是否有全局事务，如果有，老老实实用jta，否则就需要自己写一个manager了 <br />2，设置manager，根据你的事务源选择对应的PlatformTransactionManager <br />3，选择实现事物的方式，用template还是interceptor。用template代码直观点，但是template所管辖的manager和你应用代码所用的事务源要一致。如果用interceptor千万注意，一定要调用interceptor那个bean，而不是原始的那个target。在坛子上我已经看到至少有两个朋友说spring事物不起作用，从配置和代码上看都正确，这时要好好查查，调用的bean是哪一个。 <br />4，这个是设计问题了，推荐事务处于一个较高层次，比如service上的某个函数，而底层的dao可以不考虑事务，否则可能会出现事务嵌套，增加程序复杂度。</p><img src ="http://www.blogjava.net/NeonWay/aggbug/93872.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-15 10:10 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/15/93872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解 java中String</title><link>http://www.blogjava.net/NeonWay/archive/2007/01/15/93859.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 15 Jan 2007 01:56:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/15/93859.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/93859.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/15/93859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/93859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/93859.html</trackback:ping><description><![CDATA[
		<a href="http://www.javaeye.com/post/199106">http://www.javaeye.com/post/199106</a>
		<br />
		<br /> 要理解 java中String的运作方式，必须明确一点：String是一个非可变类（immutable）。什么是非可变类呢？简单说来，非可变类的实例是不能被修改的，每个实例中包含的信息都必须在该实例创建的时候就提供出来，并且在对象的整个生存周期内固定不变。java为什么要把String设计为非可变类呢？你可以问问 james Gosling ：）。但是非可变类确实有着自身的优势，如状态单一，对象简单，便于维护。其次，该类对象对象本质上是线程安全的，不要求同步。此外用户可以共享非可变对象，甚至可以共享它们的内部信息。（详见 《Effective java》item 13）。String类在java中被大量运用，甚至在class文件中都有其身影，因此将其设计为简单轻便的非可变类是比较合适的。 
<p>一、创建。<br />    好了，知道String是非可变类以后，我们可以进一步了解String的构造方式了。创建一个Stirng对象，主要就有以下两种方式：<br /></p><div class="code_title">java 代码</div><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>String str1 = </span><span class="keyword">new</span><span> String(</span><span class="string">"abc"</span><span>);    </span></span></li><li class=""><span>Stirng str2 = </span><span class="string">"abc"</span><span>;  </span></li></ol></div><p>     虽然两个语句都是返回一个String对象的引用，但是jvm对两者的处理方式是不一样的。对于第一种，jvm会马上在heap中创建一个String对象，然后将该对象的引用返回给用户。对于第二种，jvm首先会在内部维护的strings pool中通过String的 equels 方法查找是对象池中是否存放有该String对象，如果有，则返回已有的String对象给用户，而不会在heap中重新创建一个新的String对象；如果对象池中没有该String对象，jvm则在heap中创建新的String对象，将其引用返回给用户，同时将该引用添加至strings pool中。注意：使用第一种方法创建对象时，jvm是不会主动把该对象放到strings pool里面的，除非程序调用 String的intern方法。看下面的例子：</p><div class="code_title">java 代码</div><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>String str1 = </span><span class="keyword">new</span><span> String(</span><span class="string">"abc"</span><span>); </span><span class="comment">//jvm 在堆上创建一个String对象 </span><span>  </span></span></li><li class=""><span>  </span></li><li class="alt"><span> </span><span class="comment">//jvm 在strings pool中找不到值为“abc”的字符串，因此 </span><span>  </span></li><li class=""><span> </span><span class="comment">//在堆上创建一个String对象，并将该对象的引用加入至strings pool中 </span><span>  </span></li><li class="alt"><span> </span><span class="comment">//此时堆上有两个String对象 </span><span>  </span></li><li class=""><span>Stirng str2 = </span><span class="string">"abc"</span><span>;   </span></li><li class="alt"><span>  </span></li><li class=""><span> </span><span class="keyword">if</span><span>(str1 == str2){   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str1 == str2"</span><span>);   </span></li><li class=""><span> }</span><span class="keyword">else</span><span>{   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str1 != str2"</span><span>);   </span></li><li class=""><span> }   </span></li><li class="alt"><span>  </span><span class="comment">//打印结果是 str1 != str2,因为它们是堆上两个不同的对象 </span><span>  </span></li><li class=""><span>  </span></li><li class="alt"><span>  String str3 = </span><span class="string">"abc"</span><span>;   </span></li><li class=""><span> </span><span class="comment">//此时，jvm发现strings pool中已有“abc”对象了，因为“abc”equels “abc” </span><span>  </span></li><li class="alt"><span> </span><span class="comment">//因此直接返回str2指向的对象给str3，也就是说str2和str3是指向同一个对象的引用 </span><span>  </span></li><li class=""><span>  </span><span class="keyword">if</span><span>(str2 == str3){   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str2 == str3"</span><span>);   </span></li><li class=""><span>  }</span><span class="keyword">else</span><span>{   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str2 != str3"</span><span>);   </span></li><li class=""><span>  }   </span></li><li class="alt"><span> </span><span class="comment">//打印结果为 str2 == str3</span><span>  </span></li></ol></div><p>   再看下面的例子：<br /></p><div class="code_title">java 代码</div><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>String str1 = </span><span class="keyword">new</span><span> String(</span><span class="string">"abc"</span><span>); </span><span class="comment">//jvm 在堆上创建一个String对象 </span><span>  </span></span></li><li class=""><span>  </span></li><li class="alt"><span>str1 = str1.intern();   </span></li><li class=""><span></span><span class="comment">//程序显式将str1放到strings pool中，intern运行过程是这样的：首先查看strings pool </span><span>  </span></li><li class="alt"><span></span><span class="comment">//有没“abc”对象的引用，没有，则在堆中新建一个对象，然后将新对象的引用加入至 </span><span>  </span></li><li class=""><span></span><span class="comment">//strings pool中。执行完该语句后，str1原来指向的String对象已经成为垃圾对象了，随时会 </span><span>  </span></li><li class="alt"><span></span><span class="comment">//被GC收集。 </span><span>  </span></li><li class=""><span>  </span></li><li class="alt"><span></span><span class="comment">//此时，jvm发现strings pool中已有“abc”对象了，因为“abc”equels “abc” </span><span>  </span></li><li class=""><span></span><span class="comment">//因此直接返回str1指向的对象给str2，也就是说str2和str1引用着同一个对象， </span><span>  </span></li><li class="alt"><span></span><span class="comment">//此时，堆上的有效对象只有一个。 </span><span>  </span></li><li class=""><span>Stirng str2 = </span><span class="string">"abc"</span><span>;   </span></li><li class="alt"><span>  </span></li><li class=""><span> </span><span class="keyword">if</span><span>(str1 == str2){   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str1 == str2"</span><span>);   </span></li><li class=""><span> }</span><span class="keyword">else</span><span>{   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str1 != str2"</span><span>);   </span></li><li class=""><span> }   </span></li><li class="alt"><span>  </span><span class="comment">//打印结果是 str1 == str2 </span><span>  </span></li><li class=""><span>  </span></li></ol></div><p><br /></p><p>    为什么jvm可以这样处理String对象呢？就是因为String的非可变性。既然所引用的对象一旦创建就永不更改，那么多个引用共用一个对象时互不影响。</p><p><br />二、串接（Concatenation）。<br />     java程序员应该都知道滥用String的串接操作符是会影响程序的性能的。性能问题从何而来呢？归根结底就是String类的非可变性。既然String对象都是非可变的，也就是对象一旦创建了就不能够改变其内在状态了，但是串接操作明显是要增长字符串的，也就是要改变String的内部状态，两者出现了矛盾。怎么办呢？要维护String的非可变性，只好在串接完成后新建一个String 对象来表示新产生的字符串了。也就是说，每一次执行串接操作都会导致新对象的产生，如果串接操作执行很频繁，就会导致大量对象的创建，性能问题也就随之而来了。<br />    为了解决这个问题，jdk为String类提供了一个可变的配套类，StringBuffer。使用StringBuffer对象，由于该类是可变的，串接时仅仅时改变了内部数据结构，而不会创建新的对象，因此性能上有很大的提高。针对单线程，jdk 5.0还提供了StringBuilder类，在单线程环境下，由于不用考虑同步问题，使用该类使性能得到进一步的提高。</p><p>三、String的长度<br />   我们可以使用串接操作符得到一个长度更长的字符串，那么，String对象最多能容纳多少字符呢？查看String的源代码我们可以得知类String中是使用域 count 来记录对象字符的数量，而count 的类型为 int，因此，我们可以推测最长的长度为 2^32，也就是4G。<br />    不过，我们在编写源代码的时候，如果使用 Sting str = "aaaa";的形式定义一个字符串，那么双引号里面的ASCII字符最多只能有 65534 个。为什么呢？因为在class文件的规范中， CONSTANT_Utf8_info表中使用一个16位的无符号整数来记录字符串的长度的，最多能表示 65536个字节，而java class 文件是使用一种变体UTF-8格式来存放字符的，null值使用两个字节来表示，因此只剩下 65536－ 2 ＝ 65534个字节。也正是变体UTF-8的原因，如果字符串中含有中文等非ASCII字符，那么双引号中字符的数量会更少（一个中文字符占用三个字节）。如果超出这个数量，在编译的时候编译器会报错。</p><img src ="http://www.blogjava.net/NeonWay/aggbug/93859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-15 09:56 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/15/93859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于网页采集</title><link>http://www.blogjava.net/NeonWay/archive/2006/11/15/81328.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Wed, 15 Nov 2006 09:24:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/11/15/81328.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/81328.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/11/15/81328.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/81328.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/81328.html</trackback:ping><description><![CDATA[
		<a href="/ekinglong/archive/2006/11/12/80704.html">http://www.blogjava.net/ekinglong/archive/2006/11/12/80704.html</a>
		<br />
		<br />
		<a href="/ekinglong/archive/2006/10/27/77688.html">http://www.blogjava.net/ekinglong/archive/2006/10/27/77688.html</a>
<img src ="http://www.blogjava.net/NeonWay/aggbug/81328.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-11-15 17:24 <a href="http://www.blogjava.net/NeonWay/archive/2006/11/15/81328.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate中对Session管理</title><link>http://www.blogjava.net/NeonWay/archive/2006/09/09/68662.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Sat, 09 Sep 2006 02:04:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/09/09/68662.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/68662.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/09/09/68662.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/68662.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/68662.html</trackback:ping><description><![CDATA[
		<strong>
				<font size="2">
						<span class="tpc_title">Hibernate中对Session管理</span>
						<br />
						<br />
				</font>
		</strong>
		<font size="2">
				<span class="tpc_content">在各种Session 管理方案中， ThreadLocal 模式得到了大量使用。ThreadLocal 是Java中一种较为特殊的线程绑定机制。通过ThreadLocal存取的数据，总是与当前线程相关，也就是说，JVM 为每个运行的线程，绑定了私有的本地实例存取空间，从而为多线程环境常出现的并发访问问题提供了一种隔离机制。首先，我们需要知道，SessionFactory负责创建Session，SessionFactory是线程<br />安全的，多个并发线程可以同时访问一个SessionFactory 并从中获取Session 实例。而<br />Session并非线程安全，也就是说，如果多个线程同时使用一个Session实例进行数据存取，<br />则将会导致Session 数据存取逻辑混乱。下面是一个典型的Servlet，我们试图通过一个类<br />变量session实现Session的重用，以避免每次操作都要重新创建：<br />public class TestServlet extends HttpServlet {<br />private Session session;<br />public void doGet( HttpServletRequest request,<br />HttpServletResponse response)<br />throws ServletException, IOException {<br />session = getSession();<br />doSomething();<br />session.flush();<br />}<br />public void doSomething(){<br />......//基于session的存取操作<br />}<br />}<br />代码看上去正确无误，甚至在我们单机测试的时候可能也不会发生什么问题，但这样的代<br />Hibernate Developer's Guide Version 1.0<br />September 2, 2004 So many open source projects. Why not Open your Documents?<br />码一旦编译部署到实际运行环境中，接踵而来的莫名其妙的错误很可能会使得我们摸不找头脑。<br />问题出在哪里？<br />首先，Servlet 运行是多线程的，而应用服务器并不会为每个线程都创建一个Servlet<br />实例，也就是说，TestServlet在应用服务器中只有一个实例（在Tomcat中是这样，其他的<br />应用服务器可能有不同的实现），而这个实例会被许多个线程并发调用，doGet 方法也将被不<br />同的线程反复调用，可想而知，每次调用doGet 方法，这个唯一的TestServlet 实例的<br />session 变量都会被重置，线程A 的运行过程中，其他的线程如果也被执行，那么session<br />的引用将发生改变，之后线程A 再调用session，可能此时的session 与其之前所用的<br />session就不再一致，显然，错误也就不期而至。<br />ThreadLocal的出现，使得这个问题迎刃而解。<br />我们对上面的例子进行一些小小的修改：<br />public class TestServlet extends HttpServlet {<br />private ThreadLocal localSession = new ThreadLocal();<br />public void doGet( HttpServletRequest request,<br />HttpServletResponse response)<br />throws ServletException, IOException {<br />localSession.set(getSession());<br />doSomething();<br />session.flush();<br />}<br />public void doSomething(){<br />Session session = (Session)localSession.get();<br />......//基于session的存取操作<br />}<br />}<br />可以看到，localSession 是一个ThreadLocal 类型的对象，在doGet 方法中，我们<br />通过其set 方法将获取的session 实例保存，而在doSomething 方法中，通过get 方法取<br />出session实例。<br />这也就是ThreadLocal的独特之处，它会为每个线程维护一个私有的变量空间。实际上，<br />其实现原理是在JVM 中维护一个Map，这个Map的key 就是当前的线程对象，而value则是<br />线程通过ThreadLocal.set方法保存的对象实例。当线程调用ThreadLocal.get方法时，<br />ThreadLocal会根据当前线程对象的引用，取出Map中对应的对象返回。<br />这样，ThreadLocal通过以各个线程对象的引用作为区分，从而将不同线程的变量隔离开<br />来。 <br />Hibernate官方开发手册的示例中，提供了一个通过ThreadLocal维护Session的好<br />榜样： <br /><br />public class HibernateUtil {<br />private static SessionFactory sessionFactory;<br />static {<br />try {<br />// Create the SessionFactory<br />sessionFactory = new<br />Configuration().configure().buildSessionFactory();<br />} catch (HibernateException ex) {<br />throw new RuntimeException(<br />"Configuration problem: " + ex.getMessage(),<br />ex<br />);<br />}<br />}<br />public static final ThreadLocal session = new ThreadLocal();<br />public static Session currentSession() throws HibernateException<br />{<br />Session s = (Session) session.get();<br />// Open a new Session, if this Thread has none yet<br />if (s == null) {<br />s = sessionFactory.openSession();<br />session.set(s);<br />}<br />return s;<br />}<br />public static void closeSession() throws HibernateException {<br />Session s = (Session) session.get();<br />session.set(null);<br />if (s != null)<br />s.close();<br />}<br />}<br />在代码中，只要借助上面这个工具类获取Session 实例，我们就可以实现线程范围内的<br />Session 共享，从而避免了在线程中频繁的创建和销毁Session 实例。不过注意在线程结束<br />时关闭Session。<br /><br />同时值得一提的是，新版本的Hibernate在处理Session的时候已经内置了延迟加载机<br />制，只有在真正发生数据库操作的时候，才会从数据库连接池获取数据库连接，我们不必过于担<br />心Session的共享会导致整个线程生命周期内数据库连接被持续占用。<br /><br /><br /><br />对于Web程序<br />而言，我们可以借助Servlet2.3规范中新引入的Filter机制，轻松实现线程生命周期内的<br />Session管理（关于Filter的具体描述，请参考Servlet2.3规范）。<br />Filter的生命周期贯穿了其所覆盖的Servlet（JSP也可以看作是一种特殊的Servlet）<br />及其底层对象。Filter在Servlet被调用之前执行，在Servlet调用结束之后结束。因此，<br />在Filter 中管理Session 对于Web 程序而言就显得水到渠成。下面是一个通过Filter 进<br />行Session管理的典型案例：<br />public class PersistenceFilter implements Filter<br />{<br />protected static ThreadLocal hibernateHolder = new ThreadLocal();<br />public void doFilter(ServletRequest request, ServletResponse<br />response, FilterChain chain)<br />throws IOException, ServletException<br />{<br />hibernateHolder.set(getSession());<br />try<br />{<br />......<br />chain.doFilter(request, response);<br />......<br />}<br />finally<br />{<br />Session sess = (Session)hibernateHolder.get();<br />if (sess != null)<br />{<br />hibernateHolder.set(null);<br />try<br />{<br />sess.close();<br />}<br />catch (HibernateException ex) {<br />throw new ServletException(ex);<br />}<br />}<br />}<br />}<br />......<br />Hibernate Developer's Guide Version 1.0<br />September 2, 2004 So many open source projects. Why not Open your Documents?<br />}<br />通过在doFilter中获取和关闭Session，并在周期内运行的所有对象（Filter链中其<br />余的Filter，及其覆盖的Servlet 和其他对象）对此Session 实例进行重用，保证了一个<br />Http Request处理过程中只占用一个Session，提高了整体性能表现。<br />在实际设计中，Session的重用做到线程级别一般已经足够，企图通过HttpSession实<br />现用户级的Session重用反而可能导致其他的问题。凡事不能过火，Session重用也一样。<br /></span>
				<br /> 转自:http://bbs.tech.ccidnet.com/read.php?tid=147661</font>
<img src ="http://www.blogjava.net/NeonWay/aggbug/68662.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-09-09 10:04 <a href="http://www.blogjava.net/NeonWay/archive/2006/09/09/68662.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>hibernate集合映射inverse和cascade详解 （转载）</title><link>http://www.blogjava.net/NeonWay/archive/2006/09/09/68659.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Sat, 09 Sep 2006 02:00:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/09/09/68659.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/68659.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/09/09/68659.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/68659.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/68659.html</trackback:ping><description><![CDATA[
		<font size="2">﻿4. hibernate如何根据pojo来更新数据库<br /><br />4.0 在commit/flush之前，hibernate不会对pojo对象作神秘的处理。<br />4.0.1 在select查询出pojo时，hibernate根据“字段--属性”的对应关系，用字段的值填充pojo的属性；<br />然后根据“关系标记”生成sql语句从relationTable中查询出满足条件的relationPojo，并把这些relatinPojo<br />放到“关系属性”中。这个过程是机械的。<br /><br />4.0.2 在pojo对象被查出来后，到commit(或flush)之前，它将是一个普通的java对象，hibernate不会做额外的手脚。<br />比如，不会限制你设置一个属性的值为null或其它任何值<br />在集合类Set的add(object)操作时， 不会改变object的值，不会检查参数object是否是一个pojo对象<br />设置mainPojo的一个“桥属性”的值，不会自动设置relationPojo的对应的“桥属性”的值。<br />执行session.delete(pojo)时，pojo本身没有变化，他的属性值也没有变化。<br />执行session.save(pojo)时，如果pojo的id不是hibernate或数据库生成,则它的值没有变化。<br />如果pojo的id是hibernate或数据库生成，则hibernate会把id给pojo设上去。<br /><br />extend: 对lazy=true的set，hibernate在进行set的操作(调用java.util.Set中声明的方法)时<br />会先inialize这个set，仅此而已。而inialize仅仅是从数据库中捞出set的数据。 <br />如果一个set已经被inialize了，那么对它进行的操作就是java.util.Set接口中定义的语义。<br /><br />另外，如果id由hibernate来生成，那么在save(pojo)时，hibernate会改变该pojo，会设置它的id，这<br />可能改变该pojo的hashCode，详细地讨论见帖《》<br /><br />mapping文件中标记的某些属性及pojo对象的操作会对数据库操作产生影响，这些影响都是在commit时才会起作用。<br />而在commit前pojo的状态不受它们的影响。<br /><br />不过，待commit之时，将由hibernate完全掌控，它好像知道pojo对象从创建到commit这中间的所有变化。<br /><br /><br />4.01. 关联更新<br />"关系标记"对应的属性是一个pojo或一个pojo的集合，修改“关系属性”的值能会导致更新mainTable表，也可能会更新relationTable表。<br /><br />这种更新暂叫“关联更新”。<br /><br /><br />4.1.inverse属性的作用（假定没有设置cascade属性） <br />4.1.1 “只有集合标记（set/map/list/array/bag）才有inverse属性”。<br />————不妨以标记set为例，具体为“一个地区（Address表）的学校（School表）” -- address.schoolSet。<br /><br />4.1.2 “set的inverse属性决定是否把对set的改动反映到数据库中去。<br />inverse=false————反映；inverse=true————不反映”<br />inverse属性默认为false<br /><br />对&lt;one-to-many&gt;和&lt;many-to-many&gt;子标记，这两条都适用。<br />不管是对set做什么操作，4.1.2都适用。<br /><br />4.1.3当inverse=false时，hibernate如何将对set的改动反映到数据库中：<br /><br />对set的操作主要有：（1）新增元素 address.getSchoolSet().add(oneSchool);<br />（2）删除元素 address.getSchoolSet().remove(oneSchool);<br />（3）删除set address.setSchoolSet(null);<br />（4）设新set address.setSchoolSet( newSchoolSet);<br />（5）转移set otherSchoolSet = otherAddress.getSchoolSet();<br />otherAddress.setSchoolSet(null);<br />address.setSchoolSet(otherSchoolSet);<br />（6）改变set中元素的属性的值 如果是改变key属性，这会导致异常<br />如果改变的是普通的属性，则hibernate认为set没有变化（在后面可以看出缘由）。<br />所以这种情形不予考虑。<br /><br />改变set后，hibernate对数据库的操作根据是&lt;one-to-many&gt;关系还是&lt;many-to-many&gt;关系而有不同。<br /><br />对one-to-many，对school set的改动，会改变表SCHOOL中的数据:<br />#SCHOOL_ID是school表的主键，SCHOOL_ADDRESS是school表中的地址栏位<br />#表School的外键为SCHOOL_ADDRESS，它对应表Address的主键ADDRESS_ID<br />（11）insert oneSchool———— sqlInsertRowString: <br />update SCHOOL set SCHOOL_ADDRESS=? where SCHOOL_ID=? <br />(仅仅update foreign-key的值。)<br />（22）delete oneSchool———— sqlDeleteRowString: <br />update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ID=?<br />（很奇怪，把foreign-key设置为null不知道有什么实际意义？）<br />（33）delete 属于某一address的所有school ————sqlDeleteString：<br />update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ADDRESS=?<br />（44）update ————sqlUpdateRowString：""， no need<br /><br />对many-to-many，对school set的改动，会改变关系表ADDRESS_SCHOOL中的数据:<br />#“地区————学校”的关系为多对多的关系有点牵强，只是为了方便与上面的one-to-many作比较<br />#假设有一个关系表ADDRESS_SCHOOL，有两个字段ADDRESS_ID, SCHOOL_ID，<br />#这两个字段分别对应ADDRESS和SCHOOL两表的key<br />（11）insert的SQL语句为： insert into ADDRESS_SCHOOL(ADDRESS_ID, SCHOOL_ID) <br />values(?,?)<br />（22）delete的SQL语句为： delete from ADDRESS_SCHOOL <br />where ADDRESS_ID=? AND SCHOOL_ID=?<br />（33）delete all的SQL语句为： delete from ADDRESS_SCHOOL<br />where ADDRESS_ID=?<br />（44）update的sql语句为 ————sqlUpdateRowString：<br />update ADDRESS_SCHOOL set ADDRESS_ID=?<br />where ADDRESS_ID=? AND SCHOOL_ID=?<br /><br />对set的操作(1),hibernate会执行(11)sqlInsertRowString<br />对set的操作(2),hibernate会执行(22)sqlDeleteRowString<br />对set的操作(3),hibernate会执行(33)sqlDeleteString<br />对set的操作(4),老的schoolSet因为没有所属的address,所以被全部delete掉，即先执行(33)sqlDeleteString<br />然后新增新的schoolSet,即再执行sqlInsertRowString<br />对set的操作(5)，实际上就是将set从一个pojo转移到另一pojo：<br />首先，执行sqlDeleteString，删除掉otherAddress所属的school<br />然后，执行sqlDeleteString，删除掉address原先的school<br />最后，执行sqlInsertRowString，将otherSchoolSet新增给address<br /><br />总结：（1）对one-to-many而言，改变set，会让hibernate执行一系列的update语句， 不会delete/insert数据<br />（2）对many-to-many而言，改变set,只修改关系表的数据，不会影响many-to-many的另一方。<br />（3）虽然one-to-many和many-to-many的数据库操作不一样，但目的都是一个：维护数据的一致性。执行的sql都<br />只涉及到“桥字段”，不会考虑或改变其他的字段，所以对set的操作(6)是没有效果地。<br />extend:对list,可能还会维护index字段。<br /><br />4.1.4 “inverse与cascade没有什么关系，互无牵扯。”<br />commit后，这两个属性发挥作用的时机不同，hibernate会根据对pojo对象的改动，及cascade属性的设置，<br />生成一系列的Action，比如UpdateAction,DeleteAction,InsertAction等，每个Action都有execute方法以执行对应的sql语句。<br />待所有这些Action都生成好了后，hibernate再一起执行它们，在执行sql前，inverse属性起作用，<br />当inverse=true时，不执行sql；当inverse=false时，执行sql。<br /><br />4.1.5 inverse的默认值为false，所以inverse属性默认会进行“关联更新”。<br /><br />4.1.6 建议：只对set + many-to-many设置inverse=false，其他的标记不考虑inverse属性。<br />  糟糕的是，不设置inverse属性时，inverse默认为false。<br /><br />4.2. 级联（cascade）属性的作用： <br />4.2.1 只有“关系标记”才有cascade属性：many-to-one，one-to-one ，any, <br />set(map, bag, idbag, list, array) + one-to-many(many-to-many)<br /><br />4.2.2 级联指的是当主控方执行操作时，关联对象（被动方）是否同步执行同一操作。<br />pojo和它的关系属性的关系就是“主控方 -- 被动方”的关系，如果关系属性是一个set，那么被动方就是set中的一个一个元素，。<br />比如：学校（School）有三个属性：地区(Address),校长（TheMaster）和学生(Set， 元素为Student)<br />执行session.delete(school)时，级联决定是否执行session.delete(Address),session.delete(theMaster)，<br />是否对每个aStudent执行session.delete(aStudent)。<br /><br />extend:这点和inverse属性是有区别的。见4.3.<br /><br />4.2.3 一个操作因级联cascade可能触发多个关联操作。前一个操作叫“主控操作”，后一个操作叫“关联操作”。<br />cascade属性的可选值：<br />all : 所有情况下均进行关联操作。<br />none：所有情况下均不进行关联操作。这是默认值。<br />save-update:在执行save/update/saveOrUpdate时进行关联操作。<br />delete：在执行delete时进行关联操作。 <br /><br />具体执行什么“关联操作”是根据“主控操作”来的：<br />“主控操作”       “关联操作”<br />session.saveOrUpdate --&gt; session.saveOrUpdate (执行saveOrUpdate实际上会执行save或者update)<br />session.save ----&gt; session.saveOrUpdate<br />session.udpate --&gt; session.saveOrUpdate<br />session.delete --&gt; session.delete<br /><br />4.2.4 主控操作和关联操作的先后顺序是“先保存one，再保存many；先删除many，再删除one；先update主控方，再update被动方”<br />对于one-to-one，当其属性constrained="false"（默认值）时，它可看作one-to-many关系；<br />  当其属性constrained="true"时，它可看作many-to-one关系；<br />对many-to-many，它可看作one-to-many。<br /><br />比如：学校（School）有三个属性：地区(Address),校长（TheMaster，其constrained="false"）和学生(Set， 元素为Student) <br />当执行session.save(school)时，<br />实际的执行顺序为：session.save(Address);<br />session.save(school);<br />session.save(theMaster);<br />for( 对每一个student ){<br />session.save(aStudent);<br />}<br /><br />当执行session.delete(school)时，<br />实际的执行顺序为：session.delete(theMaster);<br />for( 对每一个student ){<br />session.delete(aStudent);<br />}<br />session.delete(school);<br />session.delete(Address);<br /><br />当执行session.update(school)时，<br />实际的执行顺序为：session.update(school);<br />session.saveOrUpdate(Address);<br />session.saveOrUpdate(theMaster);<br />for( 对每一个student ){<br />session.saveOrUpdate(aStudent);<br />}<br />注意：update操作因级联引发的关联操作为saveOrUpdate操作，而不是update操作。<br />saveOrUpdate与update的区别是：前者根据操作对象是保存了还是没有保存，而决定执行update还是save<br /><br />extends: 实际中，删除学校不会删除地区，即地区的cascade一般设为false<br />另外，many-to-many关系很少设置cascade=true，而是设置inverse=false。这个反映了cascade和inverse的区别。见4.3<br /><br />4.2.6 cascade的默认值为false，所以inverse属性默认会进行“关联更新”。<br /><br />4.2.7 总结：级联（cascade）就是操作一个对象时，对它的属性（其cascade=true）也进行这个操作。<br /><br /><br />4.3 inverse和cascade的比较<br />这两个属性本身互不影响，但起的作用有些类似，都能引发对关系表的更新。<br /><br />4.3.1 inverse只对set+one-to-many(或many-to-many)有效，对many-to-one, one-to-one无效。<br />cascade对关系标记都有效。<br /><br />4.3.2 inverse对集合对象整体起作用，cascade对集合对象中的一个一个元素起作用，如果集合为空，那么cascade不会引发关联操作。<br />比如将集合对象置为null， school.setStudentSet(null)<br />inverse导致hibernate执行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=?<br />cascade则不会执行对STUDENT表的关联更新， 因为集合中没有元素。<br /><br />再比新增一个school, session.save(school)<br />inverse导致hibernate执行：<br />for( 对(school的每一个student ){<br />udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //将学生的school_id改为新的school的id<br />}<br />cascade导致hibernate执行：<br />for( 对school的每一个student ){<br />session.save(aStudent); //对学生执行save操作<br />}<br /><br />extends:如果改变集合中的部分元素（比如新增一个元素），<br />inverse: hibernate先判断哪些元素改变了，对改变的元素执行相应的sql<br />cascade: 它总是对集合中的每个元素执行关联操作。<br />（在关联操作中，hibernate会判断操作的对象是否改变）<br /><br />4.3.2 两个起作用的时机不同：<br />cascade：在对主控方操作时，级联发生。<br />inverse: 在flush时（commit会自动执行flush)，对session中的所有set，hibernate判断每个set是否有变化，<br />对有变化的set执行相应的sql，执行之前，会有个判断：if( inverse == true ) return;<br /><br />可以看出cascade在先，inverse在后。<br /><br />4.3.3 inverse 对set + one-to-many 和 set + many-to-many 起的作用不同。hibernate生成的sql不同。<br />对one-to-many，hibernate对many方的数据库表执行update语句。<br />对many-to-many, hibernate对关系表执行insert/update/delte语句，注意不是对many方的数据库表而是关系表。<br /><br />cascase 对set都是一致的，不管one-to-many还是many-to-many。都简单地把操作传递到set中的每个元素。所以它总是更新many<br />方的数据库表。<br /><br />4.3.4 建议：只对set + many-to-many设置inverse=false，其他的标记不考虑inverse属性，都设为inverse=true。<br />  <br />  对cascade，一般对many-to-one，many-to-many，constrained=true的one-to-one 不设置级联删除。<br /><br />引自：<a href="http://bbs.tech.ccidnet.com/simple/index.php?t144447.html">http://bbs.tech.ccidnet.com/simple/index.php?t144447.html</a></font>
<img src ="http://www.blogjava.net/NeonWay/aggbug/68659.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-09-09 10:00 <a href="http://www.blogjava.net/NeonWay/archive/2006/09/09/68659.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>quartz学习总结：</title><link>http://www.blogjava.net/NeonWay/archive/2006/08/09/62627.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Wed, 09 Aug 2006 10:52:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/08/09/62627.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/62627.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/08/09/62627.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/62627.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/62627.html</trackback:ping><description><![CDATA[
		<p>
				<font size="1">quartz学习总结：<br />一、关于job:<br />   用Quartz的行话讲，作业是一个执行任务的简单Java类。任务可以是任何Java代码。只需你实现org.quartz.Job接口并且在出现严重错误情况下抛出JobExecutionException异常即可。Job接口包含唯一的一个方法execute()，作业从这里开始执行。一旦实现了Job接口和execute()方法，当Quartz确定该是作业运行的时候，它将调用你的作业。Execute()方法内就完全是你要做的事情。要注意，自己实现job时必须有一个public 的无参数的构造方法.对于job，大多数情况下都要依赖于某些具体的条件，这时，就要用到JobDataMap了。JobDataMap是Map的一个子类，获取时很简单，直接用get方法就ok了，基于参数，我们就可以定制不同的job任务了。下面是一个简单的job，用来列举出所有的参数并且获得参数名为name的值.</font>
		</p>
		<p>
				<font size="1">   public class HelloJob implements Job {</font>
		</p>
		<p>
				<font size="1"> private static Log _log = LogFactory.getLog(HelloJob.class);</font>
		</p>
		<p>
				<font size="1"> public HelloJob() {<br /> }</font>
		</p>
		<p>
				<font size="1"> public void execute(JobExecutionContext context)<br />   throws JobExecutionException {</font>
		</p>
		<p>
				<font size="1">  JobDataMap jobDataMap =  context.getJobDetail().getJobDataMap();</font>
		</p>
		<p>
				<font size="1">  _log.info("要执行的参数如下:");<br />  Iterator i = jobDataMap.entrySet().iterator();  <br />  while(i.hasNext()) {<br />  Map.Entry me = (Map.Entry)i.next();<br />  _log.info(me.getKey() + ": "+me.getValue());<br />  }  <br />  _log.info("U Are Welcome:"+jobDataMap.get("name"));<br /> }</font>
		</p>
		<p>
				<font size="1">     }</font>
		</p>
		<p>
				<br />
				<font size="1">二、关于jobdetail:<br />    Quartz并不存储一个真正的Job实例，相反的，它通过jobdetail来定义job,并指定job的name和group,在一个调度器（Scheduler）中,name和group是唯一被定义的，一个触发器(trigger)只能指定一个job,但多个触发器可以指定同一个job.</font>
		</p>
		<p>
				<br />
				<font size="1">三、关于Scheduler</font>
		</p>
		<p>
				<font size="1">    Scheduler的作用就是调用任务，在指定的时间执行指定的任务。主要方法如下:</font>
		</p>
		<p>
				<font size="1">    scheduleJob方法:</font>
		</p>
		<p>
				<font size="1">    scheduleJob(JobDetail jobDetail, Trigger trigger):把jobDetail添加到调度器中，并指定触发器trigger.在这里要注意，在同一个调度器中，jobDetail的name和group是唯一的;Trigger的name和group也必须是唯一的。如果在trigger中指定job的name,则该name必须和jobDetail的name保持一致，否则会抛出异常。<br />    scheduleJob(Trigger trigger):指定的trigger中必须包含jobdetai的name.以便于让quartz知道要执行的任务，如果指定的jobdetail的name不在调度器中的任务列表中，则会抛出JobPersistenceException异常。</font>
		</p>
		<p>
				<font size="1">    deleteJob(String jobName,String groupName)方法:<br />    删除指定的job,并且删除所有相关联的触发器。（Delete the identified Job from the Scheduler - and any associated Triggers.）</font>
		</p>
		<p>
				<font size="1">四、关于作业存储<br />    Quartz提供两种基本作业存储类型。<br />    <br />    第一种类型叫做RAMJobStore，它利用通常的内存来持久化调度程序信息。这种作业存储类型最容易配置、构造和运行。对许多应用来说，这种作业存储已经足够了。然而，因为调度程序信息是存储在被分配给JVM的内存里面，所以，当应用程序停止运行时，所有调度信息将被丢失。<br />    <br />    第二种类型称为JDBC作业存储。Quartz提供两种不同的实现，但两种实现一般都需要JDBC驱动程序和后台数据库来持久化调度程序信息。这两种类型的不同在于你是否想要控制数据库事务或这释放控制给应用服务器例如BEA's WebLogic或Jboss。（这类似于J2EE领域中，Bean管理的事务和和容器管理事务之间的区别）这两种JDBC作业存储是： </font>
		</p>
		<p>
				<font size="1">    · JobStoreTX：当你想要控制事务或工作在非应用服务器环境中是使用 （注：自己控制事务）。</font>
		</p>
		<p>
				<font size="1">    · JobStoreCMT：当你工作在应用服务器环境中和想要容器控制事务时使用 (web服务器控制事务)。</font>
		</p>
		<p>
				<font size="1">五、关于触发器</font>
		</p>
		<p>
				<font size="1">      Quartz中的触发器用来告诉调度程序作业什么时候触发。框架提供了一把触发器类型，但两个最常用的是SimpleTrigger和CronTrigger。SimpleTrigger为需要简单打火调度而设计。 </font>
		</p>
		<p>
				<font size="1">      典型地，如果你需要在给定的时间和重复次数或者两次打火之间等待的秒数打火一个作业，那么SimpleTrigger适合你。<br />      <br />      另一方面，如果你有许多复杂的作业调度，那么或许需要CronTrigger。 <br />     <br />      CronTrigger很强大，使用复杂的时间判断来使用，效果很好。</font>
		</p>
		<p>
				<font size="1">六、关于Quartz中的几个表：<br />    QRTZ_TRIGGERS                   存放Trigger(包括SIMPLE_TRIGGERS和CRON_TRIGGERS)和jobDetail的对应关系<br />    QRTZ_TRIGGER_LISTENERS<br />    QRTZ_SIMPLE_TRIGGERS            存储简单触发器  <br />    QRTZ_SCHEDULER_STATE<br />    QRTZ_PAUSED_TRIGGER_GRPS<br />    QRTZ_LOCKS<br />    QRTZ_JOB_LISTENERS<br />    QRTZ_JOB_DETAILS                 存储jobDetail<br />    QRTZ_FIRED_TRIGGERS<br />    QRTZ_CRON_TRIGGERS               存储复杂触发器CRON_TRIGGERS<br />    QRTZ_CALENDARS<br />    QRTZ_BLOB_TRIGGERS </font>
		</p>
		<p>
				<font size="1">    注：这几个表网上并没有相关说明，等研究一段后补充()。</font>
		</p>
		<p>
				<br />
				<font size="1">七、把quartz集成到web应用中<br />    1、根据quartz中提供的建表文档，建立数据库.<br />    2、把如下quartz.properties文件放置到classes目录下.文件内容如下:<br />    <br /> #============================================================================<br /> # Configure Main Scheduler Properties  <br /> #============================================================================<br />        <br /> #调度器名，无关紧要,名字任意定<br /> org.quartz.scheduler.instanceName = ZXScheduler<br /> org.quartz.scheduler.instanceId = AUTO</font>
		</p>
		<p>
				<font size="1"> #============================================================================<br /> # Configure ThreadPool   配置数据库连接池<br /> #============================================================================</font>
		</p>
		<p>
				<font size="1"> org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool<br /> org.quartz.threadPool.threadCount = 12<br /> org.quartz.threadPool.threadPriority = 5</font>
		</p>
		<p>
				<font size="1"> #============================================================================<br /> # Configure JobStore  配置做业存储方式<br /> #============================================================================<br />        <br /> #相当于扫描频率，如果系统基于秒级，应培植成1000,quartz默认为分级（60000）<br /> org.quartz.jobStore.misfireThreshold = 1000</font>
		</p>
		<p>
				<font size="1"> #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore<br />        <br /> #在这里自己控制事务<br /> org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX</font>
		</p>
		<p>
				<font size="1"> org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate<br /> org.quartz.jobStore.useProperties = false</font>
		</p>
		<p>
				<font size="1"> #配置dataSource名<br /> org.quartz.jobStore.dataSource = myDS<br /> #表前缀<br /> org.quartz.jobStore.tablePrefix = QRTZ_<br /> org.quartz.jobStore.isClustered = false</font>
		</p>
		<p>
				<font size="1"> #============================================================================<br /> # Configure Datasources  配置数据库的连接，不用解释<br /> #============================================================================</font>
		</p>
		<p>
				<font size="1"> org.quartz.dataSource.myDS.driver = com.microsoft.jdbc.sqlserver.SQLServerDriver<br /> org.quartz.dataSource.myDS.URL = jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=quartzTest<br /> org.quartz.dataSource.myDS.user = sa<br /> org.quartz.dataSource.myDS.password = sa<br /> org.quartz.dataSource.myDS.maxConnections = 5</font>
		</p>
		<p>
				<font size="1">    3、配置web.xml,启动quartz的初试化类,添加初始化servlet</font>
		</p>
		<p>
				<font size="1">   &lt;servlet&gt; <br />    &lt;servlet-name&gt;QuartzInitializer&lt;/servlet-name&gt; <br />    &lt;servlet-class&gt;org.quartz.ee.servlet.QuartzInitializerServlet&lt;/servlet-class&gt; <br />    &lt;init-param&gt;<br />      &lt;param-name&gt;shutdown-on-unload&lt;/param-name&gt;<br />      &lt;param-value&gt;true&lt;/param-value&gt;<br />    &lt;/init-param&gt;<br />    &lt;init-param&gt;<br />     &lt;param-name&gt;config-file&lt;/param-name&gt;<br />     &lt;param-value&gt;quartz.properties&lt;/param-value&gt; <br />    &lt;/init-param&gt;<br />    &lt;load-on-startup&gt;2&lt;/load-on-startup&gt;<br />  &lt;/servlet&gt;    <br />  <br />     4、系统配置完毕。</font>
		</p>
		<p>
				<font size="1">八、构造cron触发器(个人翻译，英文不好，莫见笑)</font>
		</p>
		<p>
				<font size="1">    cron 触发器规范:</font>
		</p>
		<p>
				<font size="1">    Seconds Minutes Hours Day-of-month Month Day-of-Week Year<br />    秒       分      时     天           月    周          年   <br />    </font>
		</p>
		<p>
				<font size="1">    Seconds            0-59    , - * / <br />    Minutes            0-59    , - * / <br />    Hours              0-23    , - * / <br />    Day-of-month       1-31    , - * ? / L C <br />    Month              1-12 or JAN-DEC    , - * / <br />    Day-of-Week        1-7 or SUN-SAT    , - * ? / L C # <br />    Year (Optional)    empty, 1970-2099    , - * / </font>
		</p>
		<p>
				<font size="1">    关于字符串的设置(在cron expression中所有字符不区分大小写):</font>
		</p>
		<p>
				<br />
				<font size="1">    The '*' character is used to specify all values. For example, "*" in the minute field means "every minute".<br />    "*"字符被用来指定所有的值，例如，"*"在分钟字段时表示每一分钟</font>
		</p>
		<p>
				<font size="1">    The '?' character is allowed for the day-of-month and day-of-week fields. It is used to specify 'no specific value'. This is useful when you need to specify something in one of the two fileds, but not the other. See the examples below for clarification.<br />    "?"字符在天和周字段中使用。表示没有指定值，天和周字段指定一个，但不能两个都指定为"？"</font>
		</p>
		<p>
				<font size="1">    The '-' character is used to specify ranges For example "10-12" in the hour field means "the hours 10, 11 and 12".<br />    '-'字符被用来设置范围，比如"10-12"在小时字段的意义为"10点,11点，12点"</font>
		</p>
		<p>
				<font size="1">    The ',' character is used to specify additional values. For example "MON,WED,FRI" in the day-of-week field means "the days Monday, Wednesday, and Friday".</font>
		</p>
		<p>
				<font size="1">    ','字符被用来设置添加的值，例如在周字段设置"MON,WED,FRI"的意义即为:在周一、周三、周五激活</font>
		</p>
		<p>
				<font size="1">    The '/' character is used to specify increments. For example "0/15" in the seconds field means "the seconds 0, 15, 30, and 45". And "5/15" in the seconds field means "the seconds 5, 20, 35, and 50". You can also specify '/' after the '*' character - in this case '*' is equivalent to having '0' before the '/'.</font>
		</p>
		<p>
				<font size="1">    '/'字符被用来设置增量。例如秒字段设置"0/15"的意思为从0开始，每15秒触发，即在0、15、30、45秒触发，秒字段设置"5/15"的意思为，从5开始，第15秒触发，即在5、20、35、50秒触发.你还可以在'*'后面使用'/'字符，在这种情况下'*'与字符'0'意义相同。</font>
		</p>
		<p>
				<br />
				<font size="1">    The 'L' character is allowed for the day-of-month and day-of-week fields. This character is short-hand for "last", but it has different meaning in each of the two fields. For example, the value "L" in the day-of-month field means "the last day of the month" - day 31 for January, day 28 for February on non-leap years. If used in the day-of-week field by itself, it simply means "7" or "SAT". <br />    <br />    But if used in the day-of-week field after another value, it means "the last xxx day of the month" - for example "6L" means "the last friday of the month". When using the 'L' option, it is important not to specify lists, or ranges of values, as you'll get confusing results.<br />    'L'在天字段和周字段中使用。'L'字符是'last'的缩写，但在两个字段中，它有不同的意义。例如，'L'在天字段的意思为月的最后一天，在一月为31，非闰月2月为28。如果在'L'用在周字段中，意思为'7'(周六),即周末,一周最后一天。但如果在周字段的另一个值后面，他意味着最后一个星期几在指定月。例如'6L'意味着月的最后一个周五.在使用'L'选项时，指定列表或者范围是非常重要的，不然容易被结果搞乱。</font>
		</p>
		<p>
				<font size="1">    The '#' character is allowed for the day-of-week field. This character is used to specify "the nth" XXX day of the month. For example, the value of "6#3" in the day-of-week field means the third Friday of the month (day 6 = Friday and "#3" = the 3rd one in the month). Other examples: "2#1" = the first Monday of the month and "4#5" = the fifth Wednesday of the month. Note that if you specify "#5" and there is not 5 of the given day-of-week in the month, then no firing will occur that month.<br />    '#'被用在周字段。它用来指定第几个周几中激活。如:"6#3"--&gt;月的第三个周五；"2#1"--&gt;月的第一个周一;"4#5"--&gt;月的第五个周三。要注意，如果要使用#后面跟5,但当月并没有第五周相应的周天，那么job将不被执行（激活）；</font>
		</p>
		<p>
				<font size="1">    The 'C' character is allowed for the day-of-month and day-of-week fields. This character is short-hand for "calendar". This means values are calculated against the associated calendar, if any. If no calendar is associated, then it is equivalent to having an all-inclusive calendar. A value of "5C" in the day-of-month field means "the first day included by the calendar on or after the 5th". A value of "1C" in the day-of-week field means "the first day included by the calendar on or after sunday".</font>
		</p>
		<p>
				<font size="1">    Support for the features described for the 'C' character is not complete<br />    'C'被用在天和周字段中，'C'是'calendar'的缩写.(不太明白，关于日历的支持还不完善)</font>
		</p>
		<p>
				<font size="1">    Support for specifying both a day-of-week and a day-of-month value is not complete (you'll need to use the '?' character in on of these fields).<br />    同时在周、天中使用'?'还不完善，目前只在两者中使用一个。</font>
		</p>
		<p>
				<font size="1">    Pay attention to the effects of '?' and '*' in the day-of-week and day-of-month fields!<br />    要注意'?'和'*'在周和天字段带来的影响。<br />    注意以下例子:</font>
		</p>
		<p>
				<font size="1">    1、"0 15 10 * * 6L 2002-2005"   在2002至2005年的每月每天的10：15触发。<br />    2、"0 15 10 ? * 6L 2002-2005"   在2002至2005年的每月的最后一个周五触发。<br />    1中*表示每天，覆盖了6L(最后一个周五)</font>
		</p>
		<p>
				<font size="1">
				</font> </p>
		<p>
				<font size="1">
				</font> </p>
		<p>
				<font size="1">
				</font> </p>
		<p>
				<font size="1">
				</font> </p>
		<p>
				<font size="1">    </font>
		</p>
<img src ="http://www.blogjava.net/NeonWay/aggbug/62627.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-08-09 18:52 <a href="http://www.blogjava.net/NeonWay/archive/2006/08/09/62627.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>把java本地程序配置在windows服务来运行</title><link>http://www.blogjava.net/NeonWay/archive/2006/07/26/60112.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Wed, 26 Jul 2006 01:51:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/07/26/60112.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/60112.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/07/26/60112.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/60112.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/60112.html</trackback:ping><description><![CDATA[ <font size="2">   步骤如下：<br />       0、把java程序转换为exe文件，下载exe4j来转换：过程比较简单，选择regular mode,excutable tyep 选择GUI Application,选择其他会有问题，不能成功做成服务，按步骤生成yourApp.exe文件。<br />       1、下载instsrv.exe和srvany.exe程序，这是win2000自带的程序，在winxp中因为安全原因被去除，可以在网上下载到。<br />       2、把两个文件放置在与java的bin目录下，运行 x:\bin\instsrv.exe yourApp x:\bin\srvany.exe ,yourApp是要建立的服务名。<br />       3、yourApp服务已经建立起来了，打开注册表，展开"HKEY_LOCAL_MACHINE\SYSTEM\currentcontrolset\services",找到yourApp 项，在该项下面新建一个名为"Parameters"的项目，接着在“Parameters”项下新建一个类型为字符串值的“application”子键，该键值为要执行的exe文件的磁盘全路径。<br />       4、运行中输入"Services.msc"并回车，在列表中找到我们刚添加的yourApp 服务,把它设置为自动运行，并启动之。<br />       5、配置完毕.</font><img src ="http://www.blogjava.net/NeonWay/aggbug/60112.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-07-26 09:51 <a href="http://www.blogjava.net/NeonWay/archive/2006/07/26/60112.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使java程序只运行一次</title><link>http://www.blogjava.net/NeonWay/archive/2006/07/12/57877.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Wed, 12 Jul 2006 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/07/12/57877.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/57877.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/07/12/57877.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/57877.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/57877.html</trackback:ping><description><![CDATA[
		<p>
				<font size="2">使java程序只运行一次，既只允许一个实例保留在内存中<br /><br />主要是利用fileLock类来实现对文件加锁，实现上述目的(多说无益，代码如下):<br /><br />package com.neonway.oneinstance;</font>
		</p>
		<p>
				<font size="2">import java.io.*;</font>
		</p>
		<p>
				<font size="2">import java.nio.channels.*;</font>
		</p>
		<p>
				<br />
				<font size="2">public class OneInstance {<br />    /**<br />     * @param args<br />     */<br />    public static void main(String[] args) {<br />     try {</font>
		</p>
		<p>
				<font size="2">            System.out.println("progran start ...");</font>
		</p>
		<p>
				<font size="2">            String filename = new String("test.txt");</font>
		</p>
		<p>
				<font size="2">            File testFile = new File(filename);<br />            RandomAccessFile raf;<br />            FileChannel fc;<br />            FileLock fl;</font>
		</p>
		<p>
				<font size="2">            testFile.createNewFile();</font>
		</p>
		<p>
				<font size="2">            if (testFile.canWrite()) {<br />                raf = new RandomAccessFile(testFile, "rw");<br />                fc = raf.getChannel();<br />                fl = fc.tryLock();</font>
		</p>
		<p>
				<font size="2">                if ((fl == null) || (fl.isValid() == false)) {<br />                    System.out.println("this is useing by another program!");<br />                } else {<br />                 System.out.println("program running...");<br />                    Thread.sleep(30 * 1000);<br />                    fl.release();<br />                }</font>
		</p>
		<p>
				<font size="2">                raf.close();<br />            }<br />           System.out.println("program end ...");<br />  } catch (Exception e) {<br />   e.printStackTrace();<br />  }<br />    }<br />}<br /></font>
		</p>
<img src ="http://www.blogjava.net/NeonWay/aggbug/57877.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-07-12 22:23 <a href="http://www.blogjava.net/NeonWay/archive/2006/07/12/57877.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>log4j配置详解</title><link>http://www.blogjava.net/NeonWay/archive/2006/05/29/48717.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 29 May 2006 04:15:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/05/29/48717.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/48717.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/05/29/48717.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/48717.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/48717.html</trackback:ping><description><![CDATA[
		<font color="#0000ff" size="2">
				<strong> Log4j日志管理系统简单使用说明<br /></strong>
		</font>
		<font size="2">
				<br />
   
通常，我们都提供一个名为
log4j.properties的文件，在第一次调用到Log4J时，Log4J会在类路径（../web-inf/class/当然也可以放到其它任
何目录，只要该目录被包含到类路径中即可）中定位这个文件，并读入这个文件完成的配置。这个配置文件告
诉Log4J以什么样的格式、把什么样的信息、输出到什么地方。<br />
　　Log4j有三个主要的组件：Loggers(记录器)，Appenders
(输出源)和Layouts(布局)，这里可简单理解为日志类别，日志要输出的地方和日志以何种形式输出。综合使用这三个组件可以轻松的记录信息的类型和
级别，并可以在运行时控制日志输出的样式和位置。下面对三个组件分别进行说明：<br />
　　<br />
　　<strong>1、 Loggers<br /></strong>　
　Loggers组件在此系统中被分为五个级别：DEBUG、INFO、WARN、ERROR和FATAL。这五个级别是有顺序的，DEBUG
&lt; INFO &lt; WARN &lt; ERROR &lt;
FATAL，分别用来指定这条日志信息的重要程度,明白这一点很重要，这里Log4j有一个规则：假设Loggers级别为P，如果在Loggers中发
生了一个级别Q比P高，则可以启动，否则屏蔽掉。<br />
假设你定义的级别是info，那么error和warn的日志可以显示而比他低的debug信息就不显示了。<br />
 　<br />
　　Java程序举例来说：<br />
　　<br />
　　//建立Logger的一个实例，命名为“com.foo”<br />
　　　Logger　logger = Logger.getLogger("com.foo"); //"com.foo"是实例进行命名，也可以任意<br />
　　//设置logger的级别。通常不在程序中设置logger的级别。一般在配置文件中设置。<br />
　　logger.setLevel(Level.INFO);<br />
　　Logger barlogger = Logger.getLogger("com.foo.Bar");<br />
　　//下面这个请求可用，因为WARN &gt;= INFO<br />
　　logger.warn("Low fuel level.");<br />
　　//下面这个请求不可用，因为DEBUG &lt; INFO<br />
　　logger.debug("Starting search for nearest gas station.");<br />
　　//命名为“com.foo.bar”的实例barlogger会继承实例“com.foo”的级别。因此，下面这个请求可用，因为INFO &gt;= INFO<br />
　　barlogger.info("Located nearest gas station.");<br />
　　//下面这个请求不可用，因为DEBUG &lt; INFO<br />
　　barlogger.debug("Exiting gas station search");<br />
　　这里“是否可用”的意思是能否输出Logger信息。<br />
　　　　在对Logger实例进行命名时，没有限制，可以取任意自己感兴趣的名字。一般情况下建议以类的所在位置来命名Logger实例，这是目前来讲比较有效的Logger命名方式。这样可以使得每个类建立自己的日志信息，便于管理。比如：<br />
　　<br />
　　static Logger logger = Logger.getLogger(ClientWithLog4j.class.getName());<br />
　　<br />
　<strong>　2、Appenders<br /></strong>　　禁用与使用日志请求只是Log4j其中的一个小小的地方，Log4j日志系统允许把日志输出到不同的地方，如控制台（Console）、文件（Files）、根据天数或者文件大小产生新的文件、以流的形式发送到其它地方等等。<br />
　　<br />
　　其语法表示为：<br />
　　<br />
　　org.apache.log4j.ConsoleAppender（控制台）<br />
　　org.apache.log4j.FileAppender（文件）<br />
　　org.apache.log4j.DailyRollingFileAppender（每天产生一个日志文件）<br />
    org.apache.log4j.RollingFileAppender（文件大小到达指定尺寸的时候产生一个新的文件）<br />
　　org.apache.log4j.WriterAppender（将日志信息以流格式发送到任意指定的地方）<br />
　　<br />
　　配置时使用方式为：<br />
　　log4j.appender.appenderName = fully.qualified.name.of.appender.class<br />
　　log4j.appender.appenderName.option1 = value1<br />
　　…<br />
    log4j.appender.appenderName.option = valueN<br />
　　这样就为日志的输出提供了相当大的便利。<br />
　　<br /><strong>　　3、Layouts</strong><br />
　
　有时用户希望根据自己的喜好格式化自己的日志输出。Log4j可以在Appenders的后面附加Layouts来完成这个功能。Layouts提供了
四种日志输出样式，如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式等等。<br />
　　<br />
　　其语法表示为：<br />
　　<br />
　　org.apache.log4j.HTMLLayout（以HTML表格形式布局），<br />
　　org.apache.log4j.PatternLayout（可以灵活地指定布局模式），<br />
　　org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），<br />
　　org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息）<br />
　　<br />
　　配置时使用方式为：<br />
　　<br />
　　log4j.appender.appenderName.layout =fully.qualified.name.of.layout.class<br />
　　log4j.appender.appenderName.layout.option1 = value1<br />
　　…<br />
　　log4j.appender.appenderName.layout.option = valueN</font>
		<p>
				<font size="2">
						<strong>
								<font color="#0000ff"> Log4j的配置 </font>
						</strong>
						<br />
　　<br />
　　以上是从原理方面说明Log4j的使用方法，在具体Java编程使用Log4j可以参照以下示例：<br />
　　<br />
　　<strong>1、 建立Logger实例</strong>：<br />
　　语法表示：public static Logger getLogger( String name)<br />
　　实际使用：static Logger logger = Logger.getLogger(ServerWithLog4j.class.getName ()) ;<br />
　　<br />
　　<strong>2、 读取配置文件</strong>：<br />
　　获得了Logger的实例之后，接下来将配置Log4j使用环境：<br />
　　语法表示：<br />
　　BasicConfigurator.configure()：自动快速地使用缺省Log4j环境。<br />
　　PropertyConfigurator.configure(String configFilename)：读取使用Java的特性文件编写的配置文件。<br />
　　DOMConfigurator.configure(String filename)：读取XML形式的配置文件。<br />
　　实际使用：<br />
    PropertyConfigurator.configure("ServerWithLog4j.properties");<br />
　　<br />
　　<strong>3、 插入日志信息</strong><br />
　　完成了以上连个步骤以后，下面就可以按日志的不同级别插入到你要记录日志的任何地方了。<br />
　　语法表示：<br />
　　Logger.debug(Object message);//调试信息<br />
　　Logger.info(Object message);//一般信息<br />
　　Logger.warn(Object message);//警告信息<br />
　　Logger.error(Object message);//错误信息<br />
　　Logger.fatal(Object message);//致命错误信息</font>
		</p>
		<p>
				<font size="2">　　实际使用：logger.info("ServerSocket before accept: " + server);<br />
　　<br /><strong><font color="#0000ff">  配置过程 <br /><br /></font></strong>　在实际编程时，要使Log4j真正在系统中运行事先还要对配置文件进行定义。定义步骤就是对Logger、Appender及Layout的分别使用。<br />
    Log4j支持两种配置文件格式，一种是XML格式的文件，一种是java properties（key=value）【Java特性文件（键=值）】。下面我们介绍使用Java特性文件做为配置文件的方法<br />
   具体如下：<br />
　　<br />
　　<strong>1、配置根Logger，</strong>其语法为：<br />
　　log4j.rootLogger = [ level ] , appenderName1, appenderName2, …<br />
        
level :
是日志记录的优先级，分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别，优
先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别，您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定
义了INFO级别，则应用程序中所有DEBUG级别的日志信息将不被打印出来。<br />
　　     appenderName:就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。<br />
   例如：log4j.rootLogger＝info,A1,B2,C3<br />
　　<br />
　　<strong>2、配置日志信息输出目的地</strong>，其语法为：<br />
　　log4j.appender.appenderName = fully.qualified.name.of.appender.class  //<br />
　　  "fully.qualified.name.of.appender.class" 可以指定下面五个目的地中的一个：<br />
          1.org.apache.log4j.ConsoleAppender（控制台）<br />
          2.org.apache.log4j.FileAppender（文件）<br />
          3.org.apache.log4j.DailyRollingFileAppender（每天产生一个日志文件）<br />
          4.org.apache.log4j.RollingFileAppender（文件大小到达指定尺寸的时候产生一个新的文件）<br />
          5.org.apache.log4j.WriterAppender（将日志信息以流格式发送到任意指定的地方）<br />
             <strong>1.ConsoleAppender选项<br /></strong>                   
Threshold=WARN:指定日志消息的输出最低层次。<br />
                   
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。<br />
                   
Target=System.err：默认情况下是：System.out,指定输出控制台<br />
              <strong>2.FileAppender</strong> 选项<br />
                   
Threshold=WARN:指定日志消息的输出最低层次。<br />
                   
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。<br />
                   
File=mylog.txt:指定消息输出到mylog.txt文件。<br />
                   
Append=false:默认值是true,即将消息增加到指定文件中，false指将消息覆盖指定的文件内容。<br />
            3.<strong>DailyRollingFileAppender</strong> 选项<br />
                   
Threshold=WARN:指定日志消息的输出最低层次。<br />
                   
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。<br />
             
       File=mylog.txt:指定消息输出到
mylog.txt文件。<br />
                   
Append=false:默认值是true,即将消息增加到指定文件中，false指将消息覆盖指定的文件内容。<br />
                   
DatePattern='.'yyyy-ww:每周滚动一次文件，即每周产生一个新的文件。当然也可以指定按月、周、天、时和分。即对应的格式如下：<br />
                   
1)'.'yyyy-MM: 每月<br />
                   
2)'.'yyyy-ww: 每周 <br />
                   
3)'.'yyyy-MM-dd: 每天<br />
                   
4)'.'yyyy-MM-dd-a: 每天两次<br />
                   
5)'.'yyyy-MM-dd-HH: 每小时<br />
                   
6)'.'yyyy-MM-dd-HH-mm: 每分钟<br />
            4.<strong>RollingFileAppender</strong> 选项<br />
             
       Threshold=WARN:指定日志消息的输出最低层次。<br />
             
       ImmediateFlush=true:默认值是true,意
谓着所有的消息都会被立即输出。<br />
                   
File=mylog.txt:指定消息输出到mylog.txt文件。<br />
                   
Append=false:默认值是true,即将消息增加到指定文件中，false指将消息覆盖指定的文件内容。<br />
                   
MaxFileSize=100KB: 后缀可以是KB, MB 或者是 GB.
在日志文件到达该大小时，将会自动滚动，即将原来的内容移到mylog.log.1文件。<br />
                   
MaxBackupIndex=2:指定可以产生的滚动文件的最大数。</font>
		</p>
		<p>
				<font size="2">实际应用：<br />
　　log4j.appender.A1=org.apache.log4j.ConsoleAppender //这里指定了日志输出的第一个位置A1是控制台ConsoleAppender<br />
　　<br />
　　<strong>3、配置日志信息的格式</strong>，其语法为：<br />
　　<strong>A.</strong><strong>log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class<br /></strong>            
 "fully.qualified.name.of.layout.class" 可以指定下面4个格式中的一个：<br />
               1.org.apache.log4j.HTMLLayout（以HTML表格形式布局），<br />
　　       2.org.apache.log4j.PatternLayout（可以灵活地指定布局模式），<br />
　　       3.org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），<br />
　　       4.org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息）<br />
                   1.HTMLLayout 选项<br />
                     
LocationInfo=true:默认值是false,输出java文件名称和行号<br />
                     
Title=my app file: 默认值是 Log4J Log Messages.<br />
                   2.PatternLayout
选项<br />
                     
ConversionPattern=%m%n :指定怎样格式化指定的消息。<br />
                  
3.XMLLayout  选项<br />
                     
LocationInfo=true:默认值是false,输出java文件和行号<br />
   实际应用：<br />
   　　log4j.appender.A1.layout=org.apache.log4j.PatternLayout</font>
		</p>
		<p>
				<font size="2">       <strong>B</strong>.<strong> log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n<br /></strong>           这里需要说明的就是日志信息格式中几个符号所代表的含义：<br />
　　         －X号: X信息输出时左对齐；<br />
                  
%p: 输出日志信息优先级，即DEBUG，INFO，WARN，ERROR，FATAL,<br />
                  
%d: 输出日志时间点的日期或时间，默认格式为ISO8601，也可以在其后指定格式，比如：%d{yyy MMM dd
HH:mm:ss,SSS}，输出类似：2002年10月18日 22：10：28，921<br />
                  
%r: 输出自应用启动到输出该log信息耗费的毫秒数<br />
                  
%c: 输出日志信息所属的类目，通常就是所在类的全名<br />
                   %t: 输出产生该日志事件的线程名<br />
                  
%l:
输出日志事件的发生位置，相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程，以及在代码中的行数。举例：Testlog4.main
(TestLog4.java:10)<br />
                  
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。<br />
                   %%: 输出一个"%"字符<br />
                  
%F: 输出日志消息产生时所在的文件名称<br />
                   %L: 输出代码中的行号<br />
                  
%m: 输出代码中指定的消息,产生的日志具体信息<br />
                  
%n: 输出一个回车换行符，Windows平台为"\r\n"，Unix平台为"\n"输出日志信息换行<br />
            可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如：<br />
                    
1)%20c：指定输出category的名称，最小的宽度是20，如果category的名称小于20的话，默认的情况下右对齐。<br />
                    
2)%-20c:指定输出category的名称，最小的宽度是20，如果category的名称小于20的话，"-"号指定左对齐。<br />
                    
3)%.30c:指定输出category的名称，最大的宽度是30，如果category的名称大于30的话，就会将左边多出的字符截掉，但小于30的
话也不会有空格。<br />
                    
4)%20.30c:如果category的名称小于20就补空格，并且右对齐，如果其名称长于30字符，就从左边交远销出的字符截掉。</font>
		</p>
		<p>
				<font size="2">　　这里上面三个步骤是对前面Log4j组件说明的一个简化；下面给出一个具体配置例子，在程序中可以参照执行：<br />
　　log4j.rootLogger=INFO,A1，B2<br />
　　log4j.appender.A1=org.apache.log4j.ConsoleAppender<br />
　　log4j.appender.A1.layout=org.apache.log4j.PatternLayout<br />
　　log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n<br />
 　　根据上面的日志格式，某一个程序的输出结果如下：<br />
　　0　　INFO　2003-06-13 13:23:46968 ClientWithLog4j Client socket: Socket[addr=localhost/127.0.0.1,port=8002,localport=2014]<br />
         16　 DEBUG 2003-06-13
13:23:46984 ClientWithLog4j Server says: 'Java server with log4j, Fri
Jun 13 13:23:46 CST 2003'<br />
　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j GOOD<br />
　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Command 'HELLO' not understood.'<br />
　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j HELP<br />
　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Vocabulary: HELP QUIT'<br />
　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j QUIT<br />
 <br />
     <strong> 4. # 当输出信息于回滚文件时</strong></font>
		</p>
		<p>
				<font size="2">          log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender  
//指定以文件的方式输出日志<br />
           log4j.appender.ROLLING_FILE.Threshold=ERROR <br />
           log4j.appender.ROLLING_FILE.File=rolling.log 
//文件位置,也可以用变量${java.home}、rolling.log<br />
           log4j.appender.ROLLING_FILE.Append=true <br />
           log4j.appender.ROLLING_FILE.MaxFileSize=10KB  //文件最大尺寸<br />
           log4j.appender.ROLLING_FILE.MaxBackupIndex=1  //备份数<br />
           log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout <br />
           log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework]
%d - %c -%-4r [%t] %-5p %c %x - %m%n  　　</font>
		</p>
		<p>
				<font size="2">××××××××××××××××××××××××××××××××××××××××××××××××<br /><strong><font color="#0000ff"> <br /></font></strong><strong><font color="#0000ff"> Log4j比较全面的配置 <br /></font></strong></font>
		</p>
		<p>
				<font size="2"> LOG4J的配置之简单使它遍及于越来越多的应用中了：Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了， </font>
		</p>
		<p>
				<font size="2"> log4j.rootLogger=DEBUG,CONSOLE,A1,im <br />
 log4j.addivity.org.apache=true </font>
		</p>
		<p>
				<font size="2"> # 应用于控制台 </font>
		</p>
		<p>
				<font size="2"> log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender <br />
 log4j.appender.Threshold=DEBUG <br />
 log4j.appender.CONSOLE.Target=System.out <br />
 log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout <br />
 log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n <br />
 #log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]
n%c[CATEGORY]%n%m[MESSAGE]%n%n </font>
		</p>
		<p>
				<font size="2"> #应用于文件 </font>
		</p>
		<p>
				<font size="2"> log4j.appender.FILE=org.apache.log4j.FileAppender <br />
 log4j.appender.FILE.File=file.log <br />
 log4j.appender.FILE.Append=false <br />
 log4j.appender.FILE.layout=org.apache.log4j.PatternLayout <br />
 log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n <br />
 # Use this layout for LogFactor 5 analysis </font>
		</p>
		<p>
				<font size="2"> # 应用于文件回滚 </font>
		</p>
		<p>
				<font size="2"> log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender <br />
 log4j.appender.ROLLING_FILE.Threshold=ERROR <br />
 log4j.appender.ROLLING_FILE.File=rolling.log  //文件位置,也可以用变量${java.home}、rolling.log<br />
 log4j.appender.ROLLING_FILE.Append=true       //true:添加  false:覆盖<br />
 log4j.appender.ROLLING_FILE.MaxFileSize=10KB   //文件最大尺寸<br />
 log4j.appender.ROLLING_FILE.MaxBackupIndex=1  //备份数<br />
 log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout <br />
 log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n </font>
		</p>
		<p>
				<font size="2">
						<br />
 #应用于socket <br />
 log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender <br />
 log4j.appender.SOCKET.RemoteHost=localhost <br />
 log4j.appender.SOCKET.Port=5001 <br />
 log4j.appender.SOCKET.LocationInfo=true <br />
 # Set up for Log Facter 5 <br />
 log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout <br />
 log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n
</font>
		</p>
		<p>
				<font size="2">
						<br />
 # Log Factor 5 Appender <br />
 log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender <br />
 log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000 </font>
		</p>
		<p>
				<font size="2"> # 发送日志给邮件 </font>
		</p>
		<p>
				<font size="2"> log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender <br />
 log4j.appender.MAIL.Threshold=FATAL <br />
 log4j.appender.MAIL.BufferSize=10 <br />
 <a href="mailto:log4j.appender.MAIL.From=web@www.wuset.com">log4j.appender.MAIL.From=web@www.wuset.com</a><br />
 log4j.appender.MAIL.SMTPHost=www.wusetu.com <br />
 log4j.appender.MAIL.Subject=Log4J Message <br />
 <a href="mailto:log4j.appender.MAIL.To=web@www.wusetu.com">log4j.appender.MAIL.To=web@www.wusetu.com</a><br />
 log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout <br />
 log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n </font>
		</p>
		<p>
				<font size="2"> # 用于数据库 <br />
 log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender <br />
 log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test <br />
 log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver <br />
 log4j.appender.DATABASE.user=root <br />
 log4j.appender.DATABASE.password= <br />
 log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n') <br />
 log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout <br />
 log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n </font>
		</p>
		<p>
				<font size="2">
						<br />
 log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender <br />
 log4j.appender.A1.File=SampleMessages.log4j <br />
 log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j' <br />
 log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout </font>
		</p>
		<p>
				<font size="2"> #自定义Appender </font>
		</p>
		<p>
				<font size="2"> log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender </font>
		</p>
		<p>
				<font size="2"> log4j.appender.im.host = mail.cybercorlin.net <br />
 log4j.appender.im.username = username <br />
 log4j.appender.im.password = password <br />
 log4j.appender.im.recipient = <a href="mailto:corlin@cybercorlin.net">corlin@cybercorlin.net</a></font>
		</p>
		<p>
				<font size="2"> log4j.appender.im.layout=org.apache.log4j.PatternLayout <br />
 log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n</font>
		</p>
		<p>
				<font size="2">摘自:http://blog.csdn.net/eako/archive/2005/09/23/488099.aspx<br /></font>
		</p>
<img src ="http://www.blogjava.net/NeonWay/aggbug/48717.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-05-29 12:15 <a href="http://www.blogjava.net/NeonWay/archive/2006/05/29/48717.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CVSNT在win2000下的权限分配图</title><link>http://www.blogjava.net/NeonWay/archive/2006/05/26/48316.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Fri, 26 May 2006 05:53:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/05/26/48316.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/48316.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/05/26/48316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/48316.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/48316.html</trackback:ping><description><![CDATA[
		<p>
				<a href="/images/blogjava_net/neonway/11542/o_cvs.jpg">
						<img height="64" alt="t_cvs.jpg" src="http://www.blogjava.net/images/blogjava_net/neonway/11542/t_cvs.jpg" width="120" border="0" />
				</a>
				<br />
		</p>
<img src ="http://www.blogjava.net/NeonWay/aggbug/48316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-05-26 13:53 <a href="http://www.blogjava.net/NeonWay/archive/2006/05/26/48316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用.bat文件在Windows下运行Java程序时怎样避免DOS窗口 </title><link>http://www.blogjava.net/NeonWay/archive/2006/04/24/42817.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 24 Apr 2006 04:51:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/04/24/42817.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/42817.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/04/24/42817.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/42817.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/42817.html</trackback:ping><description><![CDATA[
		<span id="LblContent">我们在启动脚本中一般使用类似如下的命令执行一个Java程序：<br />    java xxx.xxx.MailClass.class<br />或：<br />    java -jar xxx.jar<br />这样在Java程序启动时，加载的DOS窗口一直出现，直到你关闭了Java程序才一起关闭。<br /><br />很多朋友询问怎样才能避免这个DOS窗口，你只需要把.bat中的启动命名修改为：<br />    start javaw xxx.xxx.MailClass.class<br />或:<br />    start javaw -jar xxx.jar<br />即可。<br /><br /><br />注：DOS窗口还是会一闪而过，因为javaw是一个需要dos加载的命令。 <br /><br />转自：<a href="http://affair.gzgo.gov.cn/java/display/8119.htm">http://affair.gzgo.gov.cn/java/display/8119.htm</a></span>
<img src ="http://www.blogjava.net/NeonWay/aggbug/42817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-04-24 12:51 <a href="http://www.blogjava.net/NeonWay/archive/2006/04/24/42817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>