﻿<?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-Sealyu-随笔分类-Spring</title><link>http://www.blogjava.net/sealyu/category/30667.html</link><description>--- The devil's in the Details</description><language>zh-cn</language><lastBuildDate>Tue, 23 Feb 2010 08:17:26 GMT</lastBuildDate><pubDate>Tue, 23 Feb 2010 08:17:26 GMT</pubDate><ttl>60</ttl><item><title>getHibernateTemplate()和getSession()的区别（转）</title><link>http://www.blogjava.net/sealyu/archive/2010/02/22/313642.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 22 Feb 2010 08:43:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/02/22/313642.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/313642.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/02/22/313642.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/313642.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/313642.html</trackback:ping><description><![CDATA[<div>
<p>在前一篇日志中自动生成hibernate配置文件的时候，会在dao层用到getSession()方法来操作数据库记录，但是他还有个方法getHibernateTemplate()，这两个方法究竟有什么区别呢？</p>
<p>1.使用getSession()方法你只要继承sessionFactory，而使用getHibernateTemplate()方法必须继承
HibernateDaoSupport当然包括sessionFactory，这点区别都不是特别重要的，下面这些区别就很重要了</p>
<p>2.getSession()方法是没有经过spring包装的，spring会把最原始的session给你，在使用完之后必须自己调用相应的
close方法，而且也不会对声明式事务进行相应的管理，一旦没有及时关闭连接，就会导致数据库连接池的连接数溢
出，getHibernateTemplate()方法是经过spring封装的，例如添加相应的声明式事务管理，由spring管理相应的连接。</p>
<p>在实际的使用过程中发现的确getHibernateTemplate()比getSession()方法要好很多，但是有些方法在getHibernateTemplate()并没有提供，这时我们用HibernateCallback 回调的方法管理数据库.</p>
<p>例如如下代码：</p>
<p>/**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 使用 hql 语句进行操作<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param hql HSQL 查询语句（使用回调函数访问外部变量，必须是final的）<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param offset 开始取数据的下标<br />
&nbsp;&nbsp;&nbsp; * @param length 读取数据记录数<br />
&nbsp;&nbsp;&nbsp; * @return List 结果集<br />
*/<br />
public List getListForPage ( final String hql , final int offset , final int length ) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List list = getHibernateTemplate().executeFind ( new HibernateCallback ( ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Object doInHibernate ( Session session ) throws HibernateException, SQLException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Query query = session.createQuery ( hql ) ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query.setFirstResult ( offset ) ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;query.setMaxResults ( length ) ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List list = query.list ( ) ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return list ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }) ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return list ;<br />
}</p>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/313642.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-02-22 16:43 <a href="http://www.blogjava.net/sealyu/archive/2010/02/22/313642.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 的 HibernateDaoSupport 类的 getSession() 导致的连接泄露问题（转）</title><link>http://www.blogjava.net/sealyu/archive/2010/02/22/313643.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 22 Feb 2010 08:43:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/02/22/313643.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/313643.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/02/22/313643.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/313643.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/313643.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Spring+Hibernate做项目, 发现有member在不加事务的情况下就去调用 getSession() 方法,结果导致数据库连接不能释放, 也无法正常的提交事务(只能做查询, 不能做save(), update()). 如果配合连接池使用的话,不出几分钟就会导致连接池无法拿到新连接的情况.不过, 只要给DAO或者Service加入了事务, 就不会出现连接泄漏的问题.谈谈解...&nbsp;&nbsp;<a href='http://www.blogjava.net/sealyu/archive/2010/02/22/313643.html'>阅读全文</a><img src ="http://www.blogjava.net/sealyu/aggbug/313643.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-02-22 16:43 <a href="http://www.blogjava.net/sealyu/archive/2010/02/22/313643.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring的DriverManagerDataSource与apache的BasicDataSource（转）</title><link>http://www.blogjava.net/sealyu/archive/2010/02/02/311728.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 02 Feb 2010 14:04:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/02/02/311728.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/311728.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/02/02/311728.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/311728.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/311728.html</trackback:ping><description><![CDATA[情况是这样的。。。
<br />
<br />
2008-3-18 1:08:26 org.apache.tomcat.util.threads.ThreadPool logFull
<br />
严重: All threads (150) are currently busy, waiting. Increase maxThreads (150) or check the servlet status
<br />
<br />
重新启动服务器之后，问题依然存在。
<br />
分析得出以下可能情况
<br />
1.连接数达到了150的最大上限
<br />
2.服务器端响应用户请求的时间过长导致
<br />
3.连接池的配置数出了问题
<br />
<br />
分析：
<br />
1.1个用户访问是OK的，当用2个用户对localhost进行访问，所以根本不可能达到并发访问线程的150的上限，所以应该不是数据库的原因,排除了第一种可能
<br />
2.之前访问的JSP已经经过多次访问，所以不可能是第一次访问编译，运行而导致的请求时间过长的原因，第二情况也否定。
<br />
<br />
<strong>到此，经过分析可以确定的是连接池出了问题...</strong>
<br />
<br />
首先有2个知识点需要再弄的更清楚一些
<br />
<br />
<strong>DriverManager与DataSource&nbsp; 连接数据库有何区别?</strong>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DriverManager传统的jdbc连接，通过Class.forName("XXX"),的办法注册之后，就可以DriverManager.getConnection()获得连接了。
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataSource是建立在JNDI服务基础上的,需要application
server配置datasource.首先需要注册一个DataSource(一般在/META-INF/context.xml下)然后在
web.xml文件中引用这个DataSource,就可以DataSource.getConnection()获得连接,具体操作参考(tomcat
目录里的JNDI Resources小节)
<br />
<br />
<strong>DataSource 与 DBCP pool(连接池) 的区别？</strong>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; J2EE
服务器启动时会建立一定数量的池连接，并一直维持不少于此数目的池连接。客户端程序需要连接时，池驱动程序会返回一个未使用的池连接并将其表记为忙。如果
当前没有空闲连接，池驱动程序就新建一定数量的连接，新建连接的数量有配置参数决定。当使用的池连接调用完成后，池驱动程序将此连接表记为空闲，其他调用
就可以使用这个连接。
<br />
相当于是优化了DataSource的一种工具
<br />
<br />
跟数据库连接的部分是通过Spring的DataSource JDBC连接的,配置的XML内容如下:
<br />
<div>
<div>
<div>Xml代码 <embed src="http://jueforever.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%3Cbean%20id%3D%22propertyConfigurer%22%20%0Aclass%3D%22org.springframework.beans.factory.config.PropertyPlaceholderConfigurer%22%3E%0A%3C!--PropertyPlaceholderConfigurer%E7%B1%BB%E6%9D%A5%E8%AF%BB%E5%8F%96xxx.properties%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%BF%A1%E6%81%AF%2C%E4%BB%A5key%E5%92%8Cvalue%E7%9A%84%E5%BD%A2%E5%BC%8F--%3E%0A%3Cproperty%20name%3D%22locations%22%3E%0A%20%20%20%20%3Clist%3E%0A%20%20%20%20%20%20%20%20%3Cvalue%3E%0A%20%20%20%20%20%20%20%20%20%20%2FWEB-INF%2Fclasses%2Fconfig%2Fpkm%2Fenvironment%2Fjdbc.properties%0A%20%20%20%20%20%20%20%20%3C%2Fvalue%3E%0A%20%20%20%20%20%20%20%20%3Cvalue%3E%0A%20%20%20%20%20%20%20%20%20%20%3C!--%E5%A4%9A%E4%B8%AAxxx.properties%E6%96%87%E4%BB%B6--%3E%0A%20%20%20%20%20%20%20%20%3C%2Fvalue%3E%0A%20%20%20%20%3C%2Flist%3E%0A%3C%2Fproperty%3E%0A%3C%2Fbean%3E%0A%0A%3C!--%E4%BA%8B%E5%AE%9E%E4%B8%8A%E6%98%AF%E5%9B%A0%E4%B8%BADriverManagerDataSource%E5%BB%BA%E7%AB%8B%E8%BF%9E%E6%8E%A5%E6%98%AF%E5%8F%AA%E8%A6%81%E6%9C%89%E8%BF%9E%E6%8E%A5%E5%B0%B1%E6%96%B0%E5%BB%BA%E4%B8%80%E4%B8%AAconnection%2C%E6%A0%B9%E6%9C%AC%E6%B2%A1%E6%9C%89%E8%BF%9E%E6%8E%A5%E6%B1%A0%E7%9A%84%E4%BD%9C%E7%94%A8--%3E%0A%3C!--%E4%B8%A4%E7%A7%8D%E4%B8%8D%E5%90%8C%E7%9A%84DataSource--%3E%0A%3C!--%E5%8D%95%E7%BA%AF%E7%9A%84DataSource--%3E%0A%3Cbean%20id%3D%22pkmDataSource%22%20%0Aclass%3D%22org.springframework.jdbc.datasource.DriverManagerDataSource%22%3E%0A%20%20%20%20%3Cproperty%20name%3D%22driverClassName%22%3E%0A%20%20%20%20%20%20%20%20%3Cvalue%3E%24%7Bpkm.jdbc.driverClassName%7D%3C%2Fvalue%3E%0A%20%20%20%20%20%20%20%20%3C!--%24%7Bpkm.jdbc.driverClassName%7D%E6%98%AFjdbc.properties%E6%96%87%E4%BB%B6%20%E4%B8%AD%E7%9A%84key--%3E%0A%20%20%20%20%3C%2Fproperty%3E%0A%20%20%20%20%3Cproperty%20name%3D%22url%22%3E%0A%20%20%20%20%20%20%20%20%3Cvalue%3E%24%7Bpkm.jdbc.url%7D%3C%2Fvalue%3E%0A%20%20%20%20%3C%2Fproperty%3E%0A%20%20%20%20%3Cproperty%20name%3D%22username%22%3E%0A%20%20%20%20%20%20%20%20%3Cvalue%3E%24%7Bpkm.jdbc.username%7D%3C%2Fvalue%3E%0A%20%20%20%20%3C%2Fproperty%3E%0A%20%20%20%20%3Cproperty%20name%3D%22password%22%3E%0A%20%20%20%20%20%20%20%20%3Cvalue%3E%24%7Bpkm.jdbc.password%7D%3C%2Fvalue%3E%0A%20%20%20%20%3C%2Fproperty%3E%0A%3C%2Fbean%3E%0A%0A%3C!--%E8%BF%9E%E6%8E%A5%E6%B1%A0--%3E%0A%3Cbean%20id%3D%22pkmDataSource%22%20%0Aclass%3D%22org.apache.commons.dbcp.BasicDataSource%22%20destroy-method%3D%22close%22%20lazy-init%3D%22false%22%3E%0A%20%20%20%20%3Cproperty%20name%3D%22driverClassName%22%20value%3D%22%24%7Bpkm.jdbc.driverClassName%7D%22%2F%3E%0A%20%20%20%20%3Cproperty%20name%3D%22url%22%20value%3D%22%24%7Bpkm.jdbc.url%7D%22%2F%3E%0A%20%20%20%20%3Cproperty%20name%3D%22username%22%20value%3D%22%24%7Bpkm.jdbc.username%7D%22%2F%3E%0A%20%20%20%20%3Cproperty%20name%3D%22password%22%20value%3D%22%24%7Bpkm.jdbc.password%7D%22%2F%3E%0A%20%20%20%20%3Cproperty%20name%3D%22initialSize%22%20value%3D%225%22%2F%3E%0A%20%20%20%20%3Cproperty%20name%3D%22maxActive%22%20value%3D%2210%22%2F%3E%0A%20%20%20%20%3Cproperty%20name%3D%22maxWait%22%20value%3D%2260000%22%2F%3E%0A%20%20%20%20%3Cproperty%20name%3D%22poolPreparedStatements%22%20value%3D%22true%22%2F%3E%20%20%0A%3C%2Fbean%3E" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol start="1">
    <li>&lt;bean&nbsp;id="propertyConfigurer"&nbsp;&nbsp;&nbsp;</li>
    <li>class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;&nbsp;&nbsp;</li>
    <li>&lt;!--PropertyPlaceholderConfigurer类来读取xxx.properties配置文件信息,以key和value的形式--&gt;&nbsp;&nbsp;</li>
    <li>&lt;property&nbsp;name="locations"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/WEB-INF/classes/config/pkm/environment/jdbc.properties&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/value&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--多个xxx.properties文件--&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/value&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt;&nbsp;&nbsp;</li>
    <li>&lt;/property&gt;&nbsp;&nbsp;</li>
    <li>&lt;/bean&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;!--事实上是因为DriverManagerDataSource建立连接是只要有连接就新建一个connection,根本没有连接池的作用--&gt;&nbsp;&nbsp;</li>
    <li>&lt;!--两种不同的DataSource--&gt;&nbsp;&nbsp;</li>
    <li>&lt;!--单纯的DataSource--&gt;&nbsp;&nbsp;</li>
    <li>&lt;bean&nbsp;id="pkmDataSource"&nbsp;&nbsp;&nbsp;</li>
    <li>class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="driverClassName"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;${pkm.jdbc.driverClassName}&lt;/value&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--${pkm.jdbc.driverClassName}是jdbc.properties文件&nbsp;中的key--&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="url"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;${pkm.jdbc.url}&lt;/value&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="username"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;${pkm.jdbc.username}&lt;/value&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="password"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;${pkm.jdbc.password}&lt;/value&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;&nbsp;&nbsp;</li>
    <li>&lt;/bean&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;!--连接池--&gt;&nbsp;&nbsp;</li>
    <li>&lt;bean&nbsp;id="pkmDataSource"&nbsp;&nbsp;&nbsp;</li>
    <li>class="org.apache.commons.dbcp.BasicDataSource"&nbsp;destroy-method="close"&nbsp;lazy-init="false"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="driverClassName"&nbsp;value="${pkm.jdbc.driverClassName}"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="url"&nbsp;value="${pkm.jdbc.url}"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="username"&nbsp;value="${pkm.jdbc.username}"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="password"&nbsp;value="${pkm.jdbc.password}"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="initialSize"&nbsp;value="5"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="maxActive"&nbsp;value="10"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="maxWait"&nbsp;value="60000"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="poolPreparedStatements"&nbsp;value="true"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&lt;/bean&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="xml">&lt;bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;
&lt;!--PropertyPlaceholderConfigurer类来读取xxx.properties配置文件信息,以key和value的形式--&gt;
&lt;property name="locations"&gt;
&lt;list&gt;
&lt;value&gt;
/WEB-INF/classes/config/pkm/environment/jdbc.properties
&lt;/value&gt;
&lt;value&gt;
&lt;!--多个xxx.properties文件--&gt;
&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;!--事实上是因为DriverManagerDataSource建立连接是只要有连接就新建一个connection,根本没有连接池的作用--&gt;
&lt;!--两种不同的DataSource--&gt;
&lt;!--单纯的DataSource--&gt;
&lt;bean id="pkmDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
&lt;property name="driverClassName"&gt;
&lt;value&gt;${pkm.jdbc.driverClassName}&lt;/value&gt;
&lt;!--${pkm.jdbc.driverClassName}是jdbc.properties文件 中的key--&gt;
&lt;/property&gt;
&lt;property name="url"&gt;
&lt;value&gt;${pkm.jdbc.url}&lt;/value&gt;
&lt;/property&gt;
&lt;property name="username"&gt;
&lt;value&gt;${pkm.jdbc.username}&lt;/value&gt;
&lt;/property&gt;
&lt;property name="password"&gt;
&lt;value&gt;${pkm.jdbc.password}&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;!--连接池--&gt;
&lt;bean id="pkmDataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" lazy-init="false"&gt;
&lt;property name="driverClassName" value="${pkm.jdbc.driverClassName}"/&gt;
&lt;property name="url" value="${pkm.jdbc.url}"/&gt;
&lt;property name="username" value="${pkm.jdbc.username}"/&gt;
&lt;property name="password" value="${pkm.jdbc.password}"/&gt;
&lt;property name="initialSize" value="5"/&gt;
&lt;property name="maxActive" value="10"/&gt;
&lt;property name="maxWait" value="60000"/&gt;
&lt;property name="poolPreparedStatements" value="true"/&gt;
&lt;/bean&gt;</pre>
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以问题就出在这里,当使用BasicDataSource后问题不在出现,所以是连接数量造成的,在访问数量大,并发的情况下,毫无疑问是要选择连接池的,因为有连接池的功能,无论是效率还是在资源利用率上都优于DriverManagerDataSource
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从而从根本上了解了spring的DriverManagerDataSource与apacheBasicDataSource之间的区别.
<br />
<br />
有时候可能需要配置Jndi服务
<br />
<div>
<div>
<div>Xml代码 <embed src="http://jueforever.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%3Cbean%20id%3D%22pkmDataSource%22%20%0Aclass%3D%22org.springframework.jndi.JndiObjectFactoryBean%22%3E%0A%20%20%20%20%3Cproperty%20name%3D%22jndiName%22%20value%3D%22pkmDataSource%22%2F%3E%20%0A%3C%2Fbean%3E%0A%3C!--%0A%E8%BF%99%E6%A0%B7%E7%9A%84%E8%AF%9D%E9%83%A8%E7%BD%B2%E7%9A%84%E6%97%B6%E5%80%99%EF%BC%8C%E9%9C%80%E8%A6%81%E5%9C%A8%E5%AE%B9%E5%99%A8%E4%B8%AD%EF%BC%88tomcat%2Cweblogic%EF%BC%89%E9%85%8D%E7%BD%AEJDBC%20Connection%20Pool(DBCP)%E8%BF%9E%E6%8E%A5%E6%B1%A0%0A--%3E%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol start="1">
    <li>&lt;bean&nbsp;id="pkmDataSource"&nbsp;&nbsp;&nbsp;</li>
    <li>class="org.springframework.jndi.JndiObjectFactoryBean"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="jndiName"&nbsp;value="pkmDataSource"/&gt;&nbsp;&nbsp;&nbsp;</li>
    <li>&lt;/bean&gt;&nbsp;&nbsp;</li>
    <li>&lt;!--&nbsp;</li>
    <li>这样的话部署的时候，需要在容器中（tomcat,weblogic）配置JDBC&nbsp;Connection&nbsp;Pool(DBCP)连接池&nbsp;</li>
    <li>--&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="xml">&lt;bean id="pkmDataSource"
class="org.springframework.jndi.JndiObjectFactoryBean"&gt;
&lt;property name="jndiName" value="pkmDataSource"/&gt;
&lt;/bean&gt;
&lt;!--
这样的话部署的时候，需要在容器中（tomcat,weblogic）配置JDBC Connection Pool(DBCP)连接池
--&gt;
</pre>
<br />
<br />
<strong>这三种连接方式常常使用，也容易混淆，选择其中的某一种，就需要看具体环境来配置了。</strong>
<br />
<br />
<span style="font-size: medium;">PS：jdbc.properties文件中的配置如下</span>
<br />
<div>
<div>
<div>Properties代码 <embed src="http://jueforever.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=pkm.jdbc.driverClassName%3Doracle.jdbc.OracleDriver%0Apkm.jdbc.url%3Djdbc%5C%3Aoracle%5C%3Athin%5C%3A%40109.52.20.31%5C%3A1521%5C%3Aorcl%3C!--%E6%8A%8A%E7%AC%A6%E5%8F%B7%E5%81%9A%E8%BD%AC%E8%AF%91--%3E%0Apkm.jdbc.name%3Dpkmuser%0Apkm.jdbc.password%3Ddbl0gin%0Apkm.jdbc.dataSource%3DpkmDataSource" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol start="1">
    <li>pkm.jdbc.driverClassName=oracle.jdbc.OracleDriver&nbsp;&nbsp;</li>
    <li>pkm.jdbc.url=jdbc":oracle":thin":@109.52.20.31":1521":orcl&lt;!--把符号做转译--&gt;&nbsp;&nbsp;</li>
    <li>pkm.jdbc.name=pkmuser&nbsp;&nbsp;</li>
    <li>pkm.jdbc.password=dbl0gin&nbsp;&nbsp;</li>
    <li>pkm.jdbc.dataSource=pkmDataSource&nbsp;&nbsp;</li>
</ol>
<br />
<h1>                    关于Spring整合发现的一些问题。
<cite><a href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(saveit=window.open('http://wz.csdn.net/storeit.aspx?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t),'saveit','scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes'));saveit.focus();" class="fav_csdnstylebykimi" title="收藏到我的网摘中，并分享给我的朋友">收藏</a> </cite>
</h1>
<div>
<script type="text/javascript">
document.body.oncopy = function() {
if (window.clipboardData) {
setTimeout(function() {
var text = clipboardData.getData("text");
if (text && text.length > 300) {
text = text + ""r"n"n本文来自CSDN博客，转载请标明出处：" + location.href;
clipboardData.setData("text", text);
}
}, 100);
}
}
</script>
<script type="text/javascript">                        function StorePage() { d = document; t = d.selection ? (d.selection.type != 'None' ? d.selection.createRange().text : '') : (d.getSelection ? d.getSelection() : ''); void (keyit = window.open('http://www.365key.com/storeit.aspx?t=' + escape(d.title) + '&u=' + escape(d.location.href) + '&c=' + escape(t), 'keyit', 'scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes')); keyit.focus(); }</script>
<p>Spring提供了两个这样的数据源（都位于org.springframework.jdbc.datasource程序包里）：<br />
DriverManagerDataSource：在每个连接请求时都新建一个连接。与DBCP的BasicDataSource不同，DriverManagerDataSource提供的连接没有进行池管理。<br />
SingleConnectionDataSource：在每个连接请求时都返回同一个连接。虽然它不同严格意义上的池管理数据源，但我们可以把它看作只有一个连接的池。<br />
对两个数据源的配置都类似于配置DBCP的BasicDataSource<br />
区别在于由于DriverManagerDataSource和SingleConnectionDataSource都没有提供连接池，所以在此没有设置池配置属性。<br />
虽然这两个数据源都对于小程序来说是很不错的，而且还在不断发展，但把它们用于生产程序还是需要认真考虑的。</p>
<p>SingleConnectionDataSource只使用一个数据库连接，所以不适合用于多线程程序。而
DriverMangerDataSource虽然能够支持多线程，但它会在每次连接请求时都新建一个连接，这是以性能为代价的。由于这些限制，我们强烈
建议应该使用数据源池。</p>
<p>在通过数据源与数据库建立连接之后，我们就要实际访问数据库了，而最基本的方式就是使用JDBC，现在我们就来看一看Spring如何让使用简单的JDBC更加简便。<br />
&nbsp;Spring在第三方依赖包中包含了两个数据源的实现类包，其一是Apache的DBCP，其二是 C3P0。可以在Spring配置文件中利用这两者中任何一个配置数据源。</p>
<p>DBCP数据源 <br />
&nbsp;&nbsp;&nbsp;&nbsp; DBCP 类包位于
/lib/jakarta-commons/commons-dbcp.jar，DBCP是一个依赖 Jakarta commons-
pool对象池机制的数据库连接池，所以在类路径下还必须包括/lib/jakarta-
commons/commons-pool.jar。下面是使 用DBCP配置MySql数据源的配置片断：</p>
<p>xml 代码<br />
&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destroy-method="close"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="driverClassName" value="com.mysql.jdbc.Driver" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="url" value="jdbc:mysql://localhost:3309/sampledb" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="username" value="root" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="password" value="1234" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
bean&gt;&nbsp;&nbsp; </p>
<p>BasicDataSource提供了close()方法关闭数据源，所以必须设定destroy-method=&#8221;close&#8221;属性， 以便Spring容器关闭时，数据源能够正常关闭。除以上必须的数据源属性外，还有一些常用的属性： <br />
&nbsp;&nbsp;&nbsp;&nbsp; defaultAutoCommit：设置从数据源中返回的连接是否采用自动提交机制，默认值为 true； <br />
&nbsp;&nbsp;&nbsp;&nbsp; defaultReadOnly：设置数据源是否仅能执行只读操作， 默认值为 false； <br />
&nbsp;&nbsp;&nbsp;&nbsp; maxActive：最大连接数据库连接数，设置为0时，表示没有限制； <br />
&nbsp;&nbsp;&nbsp;&nbsp; maxIdle：最大等待连接中的数量，设置为0时，表示没有限制； <br />
&nbsp;&nbsp;&nbsp;&nbsp; maxWait：最大等待秒数，单位为毫秒， 超过时间会报出错误信息； <br />
&nbsp;&nbsp;&nbsp;&nbsp; validationQuery：用于验证连接是否成功的查询SQL语句，SQL语句必须至少要返回一行数据， 如你可以简单地设置为：&#8220;select count(*) from user&#8221;； <br />
&nbsp;&nbsp;&nbsp;&nbsp; removeAbandoned：是否自我中断，默认是 false ； <br />
&nbsp;&nbsp;&nbsp;&nbsp; removeAbandonedTimeout：几秒后数据连接会自动断开，在removeAbandoned为true，提供该值； <br />
&nbsp;&nbsp;&nbsp;&nbsp; logAbandoned：是否记录中断事件， 默认为 false； </p>
<p>C3P0数据源 <br />
&nbsp;&nbsp;&nbsp;&nbsp; C3P0
是一个开放源代码的JDBC数据源实现项目，它在lib目录中与Hibernate一起发布，实现了JDBC3和JDBC2扩展规范说明的
Connection 和Statement 池。C3P0类包位于/lib/c3p0/c3p0-0.9.0.4.jar。下面是使用C3P0配置一
个 oracle数据源：</p>
<p>xml 代码<br />
&lt;bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destroy-method="close"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="driverClass" value=" oracle.jdbc.driver.OracleDriver "/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="jdbcUrl" value=" jdbc:oracle:thin:@localhost:1521:ora9i "/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="user" value="admin"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="password" value="1234"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
bean&gt;&nbsp;&nbsp; </p>
<p>ComboPooledDataSource和BasicDataSource一样提供了一个用于关闭数据源的close()方法，这样我们就可以保证Spring容器关闭时数据源能够成功释放。 <br />
&nbsp;&nbsp;&nbsp;&nbsp; C3P0拥有比DBCP更丰富的配置属性，通过这些属性，可以对数据源进行各种有效的控制： <br />
&nbsp;&nbsp;&nbsp;&nbsp; acquireIncrement：当连接池中的连接用完时，C3P0一次性创建新连接的数目； <br />
&nbsp;&nbsp;&nbsp;&nbsp; acquireRetryAttempts：定义在从数据库获取新连接失败后重复尝试获取的次数，默认为30； <br />
&nbsp;&nbsp;&nbsp;&nbsp; acquireRetryDelay：两次连接中间隔时间，单位毫秒，默认为1000； <br />
&nbsp;&nbsp;&nbsp;&nbsp; autoCommitOnClose：连接关闭时默认将所有未提交的操作回滚。默认为false； <br />
&nbsp;&nbsp;&nbsp;&nbsp;
automaticTestTable：
C3P0将建一张名为Test的空表，并使用其自带的查询语句进行测试。如果定义了这个参数，那么属性preferredTestQuery将被忽略。
你 不能在这张Test表上进行任何操作，它将中为C3P0测试所用，默认为null； <br />
&nbsp;&nbsp;&nbsp;&nbsp;
breakAfterAcquireFailure： 获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源仍有效保留，并在下次调&nbsp;&nbsp;&nbsp;
用getConnection()的时候继续尝试获取连 接。如果设为true，那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为
false； <br />
&nbsp;&nbsp;&nbsp;&nbsp; checkoutTimeout：当连接池用完时客户端调用getConnection()后等待获取新连接的时间，超时后将抛出SQLException，如设为0则无限期等待。单位毫秒，默认为0； <br />
&nbsp;&nbsp;&nbsp;&nbsp;
connectionTesterClassName：
通过实现ConnectionTester或QueryConnectionTester的类来测试连接，类名需设置为全限定名。默认为
com.mchange.v2.C3P0.impl.DefaultConnectionTester； <br />
&nbsp;&nbsp;&nbsp;&nbsp; idleConnectionTestPeriod：隔多少秒检查所有连接池中的空闲连接，默认为0表示不检查； <br />
&nbsp;&nbsp;&nbsp;&nbsp; initialPoolSize：初始化时创建的连接数，应在minPoolSize与maxPoolSize之间取值。默认为3； <br />
&nbsp;&nbsp;&nbsp;&nbsp; maxIdleTime：最大空闲时间，超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0； <br />
&nbsp;&nbsp;&nbsp;&nbsp; maxPoolSize：连接池中保留的最大连接数。默认为15； <br />
&nbsp;&nbsp;&nbsp;&nbsp;
maxStatements：
JDBC的标准参数，用以控制数据源内加载的PreparedStatement数量。但由于预缓存的Statement属
于单个Connection 而不是整个连接池。所以设置这个参数需要考虑到多方面的因素，如果maxStatements与
maxStatementsPerConnection 均为0，则缓存被关闭。默认为0； <br />
&nbsp;&nbsp;&nbsp;&nbsp; maxStatementsPerConnection：连接池内单个连接所拥有的最大缓存Statement数。默认为0； <br />
&nbsp;&nbsp;&nbsp;&nbsp; numHelperThreads：C3P0是异步操作的，缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能，通过多线程实现多个操作同时被执行。默认为3； <br />
&nbsp;&nbsp;&nbsp;&nbsp; preferredTestQuery：定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个参数能显著提高测试速度。测试的表必须在初始数据源的时候就存在。默认为null； <br />
&nbsp;&nbsp;&nbsp;&nbsp; propertyCycle： 用户修改系统配置参数执行前最多等待的秒数。默认为300； <br />
&nbsp;&nbsp;&nbsp;&nbsp;
testConnectionOnCheckout：
因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的时候都 将校验其有效性。建议使用
idleConnectionTestPeriod或automaticTestTable <br />
等方法来提升连接测试的性能。默认为false； <br />
&nbsp;&nbsp;&nbsp;&nbsp; testConnectionOnCheckin：如果设为true那么在取得连接的同时将校验连接的有效性。默认为false。 </p>
<p>读配置文件的方式引用属性： </p>
<p>&lt;bean id="propertyConfigurer"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="location" value="/WEB-INF/jdbc.properties"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
bean&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destroy-method="close"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="driverClassName" value="${jdbc.driverClassName}" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="url" value="${jdbc.url}" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="username" value="${jdbc.username}" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="password" value="${jdbc.password}" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
bean&gt;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 在jdbc.properties属性文件中定义属性值： <br />
&nbsp;&nbsp;&nbsp;&nbsp; jdbc.driverClassName= com.mysql.jdbc.Driver <br />
&nbsp;&nbsp;&nbsp;&nbsp; jdbc.url= jdbc:mysql://localhost:3309/sampledb <br />
&nbsp;&nbsp;&nbsp;&nbsp; jdbc.username=root <br />
&nbsp;&nbsp;&nbsp;&nbsp; jdbc.password=1234 <br />
&nbsp;&nbsp;&nbsp;&nbsp; 提示 经常有开发者在${xxx}的前后不小心键入一些空格，这些空格字符将和变量合并后作为属性的值。如： 的属性配置项，在前后都有空格，被解析后，username的值为&#8220; 1234 &#8221;，这将造成最终的错误，因此需要特别小心。</p>
<p>获取JNDI数据源 <br />
&nbsp;&nbsp;&nbsp;&nbsp;
如果应用配置在高性能的应用服务器（如WebLogic或Websphere等）上，我们可能更希望使用应用服务器本身提供的数据源。应用服务器的数据源
使用JNDI开放调用者使用，Spring为此专门提供引用JNDI资源的JndiObjectFactoryBean类。下面是一个简单的配置：</p>
<p>xml 代码<br />
&lt;bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="jndiName" value="java:comp/env/jdbc/bbt"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
bean&gt;&nbsp;&nbsp; </p>
<p>通过jndiName指定引用的JNDI数据源名称。 <br />
&nbsp;&nbsp;&nbsp;&nbsp; Spring 2.0为获取J2EE资源提供了一个jee命名空间，通过jee命名空间，可以有效地简化J2EE资源的引用。下面是使用jee命名空间引用JNDI数据源的配置： </p>
<p>xml 代码<br />
&lt;beans xmlns=http://www.springframework.org/schema/beans&nbsp;&nbsp;&nbsp;&nbsp; <br />
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance&nbsp;&nbsp;&nbsp;&nbsp; <br />
xmlns:jee=http://www.springframework.org/schema/jee&nbsp;&nbsp;&nbsp;&nbsp; <br />
xsi:schemaLocation="<a href="http://www.springframework.org/schema/beans">http://www.springframework.org/schema/beans</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
<a href="http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">http://www.springframework.org/schema/beans/spring-beans-2.0.xsd</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
<a href="http://www.springframework.org/schema/jee">http://www.springframework.org/schema/jee</a>&nbsp;&nbsp;&nbsp;&nbsp; <br />
<a href="http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">http://www.springframework.org/schema/jee/spring-jee-2.0.xsd</a>"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&lt;jee:jndi-lookup id="dataSource" jndi-name=" java:comp/env/jdbc/bbt"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
beans&gt;&nbsp;&nbsp; </p>
<p>Spring的数据源实现类 <br />
&nbsp;&nbsp;&nbsp;&nbsp; Spring
本身也提供了一个简单的数据源实现类DriverManagerDataSource ，它位于
org.springframework.jdbc.datasource包中。这个类实现了javax.sql.DataSource接口，但
它并没
有提供池化连接的机制，每次调用getConnection()获取新连接时，只是简单地创建一个新的连接。因此，这个数据源类比较适合在单元测试
或简 单的独立应用中使用，因为它不需要额外的依赖类。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面，我们来看一下DriverManagerDataSource的简单使用：当然，我们也可以通过配置的方式直接使用DriverManagerDataSource。</p>
<p>java 代码<br />
DriverManagerDataSource ds = new DriverManagerDataSource ();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
ds.setDriverClassName("com.mysql.jdbc.Driver");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
ds.setUrl("jdbc:mysql://localhost:3309/sampledb");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
ds.setUsername("root");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
ds.setPassword("1234");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
Connection actualCon = ds.getConnection();&nbsp;&nbsp; </p>
<p>小结 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;
不管采用何种持久化技术，都需要定义数据源。Spring附带了两个数据源的实现类包，你可以自行选择进行定义。在实际部署时，我们可能会直接采用应用服
务器本身提供的数据源，这时，则可以通过JndiObjectFactoryBean或jee命名空间引用JNDI中的数据源。 </p>
<p>DBCP与C3PO配置的区别：</p>
<p>C3PO ：</p>
<p>xml 代码</p>
<p>&lt;bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="driverClass"&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;oracle.jdbc.driver.OracleDrivervalue&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; property&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="jdbcUrl"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;jdbc:oracle:thin:@10.10.10.6:1521:DataBaseNamevalue&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; property&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="user"&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;testAdminvalue&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; property&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="password"&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;123456value&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; property&gt;&nbsp;&nbsp; <br />
bean&gt;&nbsp;&nbsp; </p>
<p>DBCP：</p>
<p>xml 代码<br />
&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="driverClassName"&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;oracle.jdbc.driver.OracleDrivervalue&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; property&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="url"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;jdbc:oracle:thin:@10.10.10.6:1521:DataBaseNamevalue&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; property&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="username"&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;testAdminvalue&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; property&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="password"&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;123456value&gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; property&gt;&nbsp;&nbsp; <br />
bean&gt;</p>
</div>
<br />
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/311728.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-02-02 22:04 <a href="http://www.blogjava.net/sealyu/archive/2010/02/02/311728.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring为ApplicationContext提供的3种实现</title><link>http://www.blogjava.net/sealyu/archive/2010/02/02/311670.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 02 Feb 2010 06:28:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/02/02/311670.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/311670.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/02/02/311670.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/311670.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/311670.html</trackback:ping><description><![CDATA[spring为ApplicationContext提供的3种实现分别为：ClassPathXmlApplicationContext，
FileSystemXmlApplicationContext和XmlWebApplicationContext，其中
XmlWebApplicationContext是专为Web工程定制的。使用举例如下： <br />
&nbsp;&nbsp; 1. FileSystemXmlApplicationContext <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<div>
<div>
<div>Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://kantery.javaeye.com/blog/333376#"><img alt="复制代码" src="http://kantery.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol>
    <li>eg1. &nbsp;&nbsp;</li>
    <li>pplicationContext&nbsp;ctx&nbsp;=&nbsp;new&nbsp;FileSystemXmlApplicationContext("bean.xml");&nbsp;//加载单个配置文件&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code"> eg1.
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml"); //加载单个配置文件</pre>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<div>
<div>
<div>Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://kantery.javaeye.com/blog/333376#"><img alt="复制代码" src="http://kantery.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol>
    <li>eg2. &nbsp;&nbsp;</li>
    <li>String[]&nbsp;locations&nbsp;=&nbsp;{"bean1.xml",&nbsp;"bean2.xml",&nbsp;"bean3.xml"}; &nbsp;&nbsp;</li>
    <li>ApplicationContext&nbsp;ctx&nbsp;=&nbsp;new&nbsp;FileSystemXmlApplicationContext(locations&nbsp;);&nbsp;//加载多个配置文件&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code">eg2.
String[] locations = {"bean1.xml", "bean2.xml", "bean3.xml"};
ApplicationContext ctx = new FileSystemXmlApplicationContext(locations ); //加载多个配置文件</pre>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<div>
<div>
<div>Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://kantery.javaeye.com/blog/333376#"><img alt="复制代码" src="http://kantery.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol>
    <li>eg3.&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</li>
    <li>ApplicationContext&nbsp;ctx&nbsp;=new&nbsp;FileSystemXmlApplicationContext("D:roject/bean.xml");//根据具体路径加载文件&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code">eg3.
ApplicationContext ctx =new FileSystemXmlApplicationContext("D:roject/bean.xml");//根据具体路径加载文件</pre>
<br />
&nbsp; 2. ClassPathXmlApplicationContext <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<div>
<div>
<div>Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://kantery.javaeye.com/blog/333376#"><img alt="复制代码" src="http://kantery.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol>
    <li>eg1.&nbsp;&nbsp; &nbsp;&nbsp;</li>
    <li>pplicationContext&nbsp;ctx&nbsp;=&nbsp;new&nbsp;ClassPathXmlApplicationContext("bean.xml");&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code"> eg1.
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");</pre>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<div>
<div>
<div>Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://kantery.javaeye.com/blog/333376#"><img alt="复制代码" src="http://kantery.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol>
    <li>&nbsp;eg2. &nbsp;&nbsp;</li>
    <li>String[]&nbsp;locations&nbsp;=&nbsp;{"bean1.xml",&nbsp;"bean2.xml",&nbsp;"bean3.xml"}; &nbsp;&nbsp;</li>
    <li>ApplicationContext&nbsp;ctx&nbsp;=&nbsp;new&nbsp;ClassPathXmlApplication(locations); &nbsp;&nbsp;</li>
    <li>注：其中FileSystemXmlApplicationContext和ClassPathXmlApplicationContext与BeanFactory的xml文件定位方式一样是基于路径的。&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code"> eg2.
String[] locations = {"bean1.xml", "bean2.xml", "bean3.xml"};
ApplicationContext ctx = new ClassPathXmlApplication(locations);
注：其中FileSystemXmlApplicationContext和ClassPathXmlApplicationContext与BeanFactory的xml文件定位方式一样是基于路径的。</pre>
<br />
3. XmlWebApplicationContext <br />
<div>
<div>
<div>Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://kantery.javaeye.com/blog/333376#"><img alt="复制代码" src="http://kantery.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol>
    <li>eg1. &nbsp;&nbsp;</li>
    <li>ServletContext&nbsp;servletContext&nbsp;=&nbsp;request.getSession().getServletContext();&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</li>
    <li>ApplicationContext&nbsp;ctx&nbsp;=&nbsp;WebApplicationContextUtils.getWebApplicationContext(servletContext);&nbsp; <br />
    </li>
</ol>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/311670.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-02-02 14:28 <a href="http://www.blogjava.net/sealyu/archive/2010/02/02/311670.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring-基于注释（Annotation）的配置(转)</title><link>http://www.blogjava.net/sealyu/archive/2010/01/16/309801.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Sat, 16 Jan 2010 15:30:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/01/16/309801.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/309801.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/01/16/309801.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/309801.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/309801.html</trackback:ping><description><![CDATA[基于注释（Annotation）的配置有越来越流行的趋势，Spring 2.5 顺应这种趋势，提供了完全基于注释配置 Bean、装配 Bean 的功能，您可以
<br />
使用基于注释的 Spring IoC 替换原来基于 XML 的配置。本文通过实例详细讲述了 Spring 2.5 基于注释 IoC 功能的使用。
<br />
概述
<br />
注释配置相对于 XML 配置具有很多的优势：
<br />
<br />
它可以充分利用 Java 的反射机制获取类结构信息，这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时，我们就不需要指定
<br />
PO 的属性名、类型等信息，如果关系表字段和 PO 属性名、类型都一致，您甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java
<br />
反射机制获取。
<br />
注释和 Java 代码位于一个文件中，而 XML 配置采用独立的配置文件，大多数配置信息在程序开发完成后都不会调整，如果配置信息和 Java 代码
<br />
放在一起，有助于增强程序的内聚性。而采用独立的 XML 配置文件，程序员在编写一个功能时，往往需要在程序文件和配置文件中不停切换，这种思维
<br />
上的不连贯会降低开发效率。
<br />
因此在很多情况下，注释配置比 XML 配置更受欢迎，注释配置有进一步流行的趋势。Spring 2.5 的一大增强就是引入了很多注释类，现在您已经可以
<br />
使用注释配置完成大部分 XML 配置的功能。在这篇文章里，我们将向您讲述使用注释进行 Bean 定义和依赖注入的内容。
<br />
原来我们是怎么做的
<br />
<br />
在使用注释配置之前，先来回顾一下传统上是如何配置 Bean 并完成 Bean 之间依赖关系的建立。下面是 3 个类，它们分别是 Office、Car 和
<br />
Boss，这 3 个类需要在 Spring 容器中配置为 Bean：
<br />
<br />
Office 仅有一个属性：
<br />
<br />
<br />
清单 1. Office.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
public class Office {
<br />
&nbsp;&nbsp;&nbsp; private String officeNo =&#8221;001&#8221;;
<br />
<br />
&nbsp;&nbsp;&nbsp; //省略 get/setter
<br />
<br />
&nbsp;&nbsp;&nbsp; @Override
<br />
&nbsp;&nbsp;&nbsp; public String toString() {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "officeNo:" + officeNo;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
<br />
<br />
Car 拥有两个属性：
<br />
<br />
<br />
清单 2. Car.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
public class Car {
<br />
&nbsp;&nbsp;&nbsp; private String brand;
<br />
&nbsp;&nbsp;&nbsp; private double price;
<br />
<br />
&nbsp;&nbsp;&nbsp; // 省略 get/setter
<br />
<br />
&nbsp;&nbsp;&nbsp; @Override
<br />
&nbsp;&nbsp;&nbsp; public String toString() {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "brand:" + brand + "," + "price:" + price;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
<br />
Boss 拥有 Office 和 Car 类型的两个属性：
<br />
<br />
<br />
清单 3. Boss.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
<br />
&nbsp;&nbsp;&nbsp; // 省略 get/setter
<br />
<br />
&nbsp;&nbsp;&nbsp; @Override
<br />
&nbsp;&nbsp;&nbsp; public String toString() {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "car:" + car + ""n" + "office:" + office;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
<br />
<br />
<br />
我们在 Spring 容器中将 Office 和 Car 声明为 Bean，并注入到 Boss Bean 中：下面是使用传统 XML 完成这个工作的配置文件 beans.xml：
<br />
<br />
清单 4. beans.xml 将以上三个类配置成 Bean
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans
<br />
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="boss" class="com.baobaotao.Boss"&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="car" ref="car"/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="office" ref="office" /&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="office" class="com.baobaotao.Office"&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="officeNo" value="002"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="car" class="com.baobaotao.Car" scope="singleton"&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="brand" value=" 红旗 CA72"/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="price" value="2000"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<br />
&lt;/beans&gt;
<br />
<br />
<br />
当我们运行以下代码时，控制台将正确打出 boss 的信息：
<br />
<br />
<br />
清单 5. 测试类：AnnoIoCTest.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
import org.springframework.context.ApplicationContext;
<br />
import org.springframework.context.support.ClassPathXmlApplicationContext;
<br />
public class AnnoIoCTest {
<br />
<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] locations = {"beans.xml"};
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ApplicationContext ctx =
<br />
&nbsp;&nbsp;&nbsp; new ClassPathXmlApplicationContext(locations);
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Boss boss = (Boss) ctx.getBean("boss");
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(boss);
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
<br />
<br />
<br />
这说明 Spring 容器已经正确完成了 Bean 创建和装配的工作。
<br />
<br />
使用 @Autowired 注释
<br />
<br />
Spring 2.5 引入了 @Autowired 注释，它可以对类成员变量、方法及构造函数进行标注，完成自动装配的工作。来看一下使用 @Autowired 进行
<br />
成员变量自动注入的代码：
<br />
<br />
<br />
清单 6. 使用 @Autowired 注释的 Boss.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
import org.springframework.beans.factory.annotation.Autowired;
<br />
<br />
public class Boss {
<br />
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
Spring 通过一个 BeanPostProcessor 对 @Autowired 进行解析，所以要让 @Autowired 起作用必须事先在 Spring 容器中声明
<br />
AutowiredAnnotationBeanPostProcessor Bean。
<br />
<br />
<br />
清单 7. 让 @Autowired 注释工作起来
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
<br />
&nbsp;&nbsp;&nbsp; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<br />
&nbsp;&nbsp;&nbsp; xsi:schemaLocation="http://www.springframework.org/schema/beans
<br />
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;!-- 该 BeanPostProcessor 将自动起作用，对标注 @Autowired 的 Bean 进行自动注入 --&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.beans.factory.annotation.
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AutowiredAnnotationBeanPostProcessor"/&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;!-- 移除 boss Bean 的属性注入配置的信息 --&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="boss" class="com.baobaotao.Boss"/&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="office" class="com.baobaotao.Office"&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="officeNo" value="001"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="car" class="com.baobaotao.Car" scope="singleton"&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="brand" value=" 红旗 CA72"/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="price" value="2000"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<br />
&lt;/beans&gt;
<br />
<br />
<br />
这样，当 Spring 容器启动时，AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean，当发现 Bean 中拥有
<br />
@Autowired 注释时就找到和其匹配（默认按类型匹配）的 Bean，并注入到对应的地方中去。
<br />
<br />
按照上面的配置，Spring 将直接采用 Java 反射机制对 Boss 中的 car 和 office 这两个私有成员变量进行自动注入。所以对成员变量使用
<br />
@Autowired 后，您大可将它们的 setter 方法（setCar() 和 setOffice()）从 Boss 中删除。
<br />
<br />
当然，您也可以通过 @Autowired 对方法或构造函数进行标注，来看下面的代码：
<br />
<br />
<br />
清单 8. 将 @Autowired 注释标注在 Setter 方法上
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; public void setCar(Car car) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.car = car;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; public void setOffice(Office office) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.office = office;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
这时，@Autowired 将查找被标注的方法的入参类型的 Bean，并调用方法自动注入这些 Bean。而下面的使用方法则对构造函数进行标注：
<br />
<br />
<br />
清单 9. 将 @Autowired 注释标注在构造函数上
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; public Boss(Car car ,Office office){
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.car = car;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.office = office ;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
由于 Boss() 构造函数有两个入参，分别是 car 和 office，@Autowired 将分别寻找和它们类型匹配的 Bean，将它们作为
<br />
Boss(Car car ,Office office) 的入参来创建 Boss Bean。
<br />
<br />
<br />
当候选 Bean 数目不为 1 时的应对方法
<br />
<br />
在默认情况下使用 @Autowired 注释进行自动注入时，Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时，
<br />
Spring 容器将抛出 BeanCreationException 异常，并指出必须至少拥有一个匹配的 Bean。我们可以来做一个实验：
<br />
<br />
<br />
清单 10. 候选 Bean 数目为 0 时
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
<br />
&nbsp;&nbsp;&nbsp; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<br />
&nbsp;&nbsp;&nbsp;&nbsp; xsi:schemaLocation="http://www.springframework.org/schema/beans
<br />
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd "&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.beans.factory.annotation.
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AutowiredAnnotationBeanPostProcessor"/&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="boss" class="com.baobaotao.Boss"/&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;!-- 将 office Bean 注释掉 --&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;!-- &lt;bean id="office" class="com.baobaotao.Office"&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;property name="officeNo" value="001"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;--&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="car" class="com.baobaotao.Car" scope="singleton"&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="brand" value=" 红旗 CA72"/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="price" value="2000"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<br />
&lt;/beans&gt;
<br />
<br />
<br />
<br />
由于 office Bean 被注释掉了，所以 Spring 容器中将没有类型为 Office 的 Bean 了，而 Boss 的 office 属性标注了 @Autowired，
<br />
当启动 Spring 容器时，异常就产生了。
<br />
<br />
当不能确定 Spring 容器中一定拥有某个类的 Bean 时，可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false)，
<br />
这等于告诉 Spring：在找不到匹配 Bean 时也不报错。来看一下具体的例子：
<br />
<br />
<br />
清单 11. 使用 @Autowired(required = false)
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
import org.springframework.beans.factory.annotation.Autowired;
<br />
import org.springframework.beans.factory.annotation.Required;
<br />
<br />
public class Boss {
<br />
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; public void setCar(Car car) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.car = car;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
&nbsp;&nbsp;&nbsp; @Autowired(required = false)
<br />
&nbsp;&nbsp;&nbsp; public void setOffice(Office office) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.office = office;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
<br />
当然，一般情况下，使用 @Autowired 的地方都是需要注入 Bean 的，使用了自动注入而又允许不注入的情况一般仅会在开发期或测试期碰到
<br />
（如为了快速启动 Spring 容器，仅引入一些模块的 Spring 配置文件），所以 @Autowired(required = false) 会很少用到。
<br />
<br />
和找不到一个类型匹配 Bean 相反的一个错误是：如果 Spring 容器中拥有多个候选 Bean，Spring 容器在启动时也会抛出
<br />
BeanCreationException 异常。来看下面的例子：
<br />
<br />
<br />
清单 12. 在 beans.xml 中配置两个 Office 类型的 Bean
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
&#8230;
<br />
&lt;bean id="office" class="com.baobaotao.Office"&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;property name="officeNo" value="001"/&gt;
<br />
&lt;/bean&gt;
<br />
&lt;bean id="office2" class="com.baobaotao.Office"&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;property name="officeNo" value="001"/&gt;
<br />
&lt;/bean&gt;
<br />
&#8230;
<br />
<br />
<br />
<br />
我们在 Spring 容器中配置了两个类型为 Office 类型的 Bean，当对 Boss 的 office 成员变量进行自动注入时，Spring 容器将无法确定到
<br />
底要用哪一个 Bean，因此异常发生了。
<br />
<br />
Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称，这样歧义就消除了，可以通过下面的方法解决异常：
<br />
<br />
<br />
清单 13. 使用 @Qualifier 注释指定注入 Bean 的名称
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
@Autowired
<br />
public void setOffice(@Qualifier("office")Office office) {
<br />
&nbsp;&nbsp;&nbsp; this.office = office;
<br />
}
<br />
<br />
<br />
<br />
@Qualifier("office") 中的 office 是 Bean 的名称，所以 @Autowired 和 @Qualifier 结合使用时，自动注入的策略就从 byType
<br />
转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释，而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入
<br />
参。正是由于注释对象的不同，所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。下面是对成员变量和构造函数入参进行注释的
<br />
代码：
<br />
<br />
对成员变量进行注释：
<br />
<br />
<br />
清单 14. 对成员变量使用 @Qualifier 注释
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; @Qualifier("office")
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
<br />
对构造函数入参进行注释：
<br />
<br />
清单 15. 对构造函数变量使用 @Qualifier 注释
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; public Boss(Car car , @Qualifier("office")Office office){
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.car = car;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.office = office ;
<br />
}
<br />
}
<br />
<br />
<br />
@Qualifier 只能和 @Autowired 结合使用，是对 @Autowired 有益的补充。一般来讲，@Qualifier 对方法签名中入参进行注释会降低代码的
<br />
可读性，而对成员变量注释则相对好一些。
<br />
<br />
使用 JSR-250 的注释
<br />
<br />
Spring 不但支持自己定义的 @Autowired 的注释，还支持几个由 JSR-250 规范定义的注释，它们分别是 @Resource、@PostConstruct 以及
<br />
@PreDestroy。
<br />
<br />
@Resource
<br />
<br />
@Resource 的作用相当于 @Autowired，只不过 @Autowired 按 byType 自动注入，面 @Resource 默认按 byName 自动注入罢了。
<br />
@Resource 有两个属性是比较重要的，分别是 name 和 type，Spring 将 @Resource 注释的 name 属性解析为 Bean 的名字，而 type
<br />
属性则解析为 Bean 的类型。所以如果使用 name 属性，则使用 byName 的自动注入策略，而使用 type 属性时则使用 byType 自动注入策略。
<br />
如果既不指定 name 也不指定 type 属性，这时将通过反射机制使用 byName 自动注入策略。
<br />
<br />
Resource 注释类位于 Spring 发布包的 lib/j2ee/common-annotations.jar 类包中，因此在使用之前必须将其加入到项目的类库中。
<br />
来看一个使用 @Resource 的例子：
<br />
<br />
<br />
清单 16. 使用 @Resource 注释的 Boss.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
import javax.annotation.Resource;
<br />
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; // 自动注入类型为 Car 的 Bean
<br />
&nbsp;&nbsp;&nbsp; @Resource
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
<br />
&nbsp;&nbsp;&nbsp; // 自动注入 bean 名称为 office 的 Bean
<br />
&nbsp;&nbsp;&nbsp; @Resource(name = "office")
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
}
<br />
<br />
<br />
<br />
一般情况下，我们无需使用类似于 @Resource(type=Car.class) 的注释方式，因为 Bean 的类型信息可以通过 Java 反射从代码中获取。
<br />
<br />
要让 JSR-250 的注释生效，除了在 Bean 类中标注这些注释外，还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor：
<br />
<br />
&lt;bean
<br />
&nbsp; class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/&gt;
<br />
<br />
<br />
<br />
CommonAnnotationBeanPostProcessor 实现了 BeanPostProcessor 接口，它负责扫描使用了 JSR-250 注释的 Bean，并对它们进行相应的操作。
<br />
<br />
@PostConstruct 和 @PreDestroy
<br />
<br />
Spring 容器中的 Bean 是有生命周期的，Spring
允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作，您既可以通过实现
InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法，也可以通过
&lt;bean&gt; 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。关于 Spring 的生命周期，笔者在《精通 Spring 2.x—企业应用开发精解》第 3 章进行了详细的描述，有兴趣的读者可以查阅。
<br />
<br />
JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类，分别是 @PostConstruct 和
@PreDestroy，这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用，而标注了
@PreDestroy 的方法将在类销毁之前调用。
<br />
<br />
<br />
清单 17. 使用 @PostConstruct 和 @PreDestroy 注释的 Boss.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
import javax.annotation.Resource;
<br />
import javax.annotation.PostConstruct;
<br />
import javax.annotation.PreDestroy;
<br />
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; @Resource
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
<br />
&nbsp;&nbsp;&nbsp; @Resource(name = "office")
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
<br />
&nbsp;&nbsp;&nbsp; @PostConstruct
<br />
&nbsp;&nbsp;&nbsp; public void postConstruct1(){
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("postConstruct1");
<br />
&nbsp;&nbsp;&nbsp; }
<br />
<br />
&nbsp;&nbsp;&nbsp; @PreDestroy
<br />
&nbsp;&nbsp;&nbsp; public void preDestroy1(){
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("preDestroy1");
<br />
&nbsp;&nbsp;&nbsp; }
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
您只需要在方法前标注 @PostConstruct 或 @PreDestroy，这些方法就会在 Bean 初始化后或销毁之前被 Spring 容器执行了。
<br />
<br />
我们知道，不管是通过实现 InitializingBean/DisposableBean 接口，还是通过 &lt;bean&gt;
元素的 init-method/destroy-method 属性进行配置，都只能为 Bean 指定一个初始化 / 销毁的方法。但是使用
@PostConstruct 和 @PreDestroy 注释却可以指定多个初始化 / 销毁方法，那些被标注 @PostConstruct 或
@PreDestroy 注释的方法都会在初始化 / 销毁时被执行。
<br />
<br />
通过以下的测试代码，您将可以看到 Bean 的初始化 / 销毁方法是如何被执行的：
<br />
<br />
<br />
清单 18. 测试类代码
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
import org.springframework.context.support.ClassPathXmlApplicationContext;
<br />
<br />
public class AnnoIoCTest {
<br />
<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] locations = {"beans.xml"};
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClassPathXmlApplicationContext ctx =
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new ClassPathXmlApplicationContext(locations);
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Boss boss = (Boss) ctx.getBean("boss");
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(boss);
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ctx.destroy();// 关闭 Spring 容器，以触发 Bean 销毁方法的执行
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
<br />
<br />
<br />
这时，您将看到标注了 @PostConstruct 的 postConstruct1() 方法将在 Spring 容器启动时，创建 Boss Bean 的时候被触发执行，而标注了 @PreDestroy 注释的 preDestroy1() 方法将在 Spring 容器关闭前销毁 Boss Bean 的时候被触发执行。
<br />
<br />
使用 &lt;context:annotation-config/&gt; 简化配置
<br />
<br />
Spring 2.1 添加了一个新的 context 的 Schema
命名空间，该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的，它仅提供元数据信息。要使元数
据信息真正起作用，必须让负责处理这些元数据的处理器工作起来。 <br />
<br />
而我们前面所介绍的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是处理这些注释元数据的处理器。但是直接在 Spring 配置文件中定义这些 Bean 显得比较笨拙。Spring 为我们提供了一种方便的注册这些 BeanPostProcessor 的方式，这就是 &lt;context:annotation-config/&gt;。请看下面的配置：
<br />
<br />
<br />
清单 19. 调整 beans.xml 配置文件
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
<br />
&nbsp;&nbsp;&nbsp; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<br />
&nbsp;&nbsp;&nbsp;&nbsp; xmlns:context="http://www.springframework.org/schema/context"
<br />
&nbsp;&nbsp;&nbsp;&nbsp; xsi:schemaLocation="http://www.springframework.org/schema/beans
<br />
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
<br />
http://www.springframework.org/schema/context
<br />
http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;context:annotation-config/&gt;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="boss" class="com.baobaotao.Boss"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="office" class="com.baobaotao.Office"&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="officeNo" value="001"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="car" class="com.baobaotao.Car" scope="singleton"&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="brand" value=" 红旗 CA72"/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="price" value="2000"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<br />
&lt;/beans&gt;
<br />
<br />
<br />
<br />
&lt;context:annotationconfig/&gt; 将隐式地向 Spring
容器注册
AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、
PersistenceAnnotationBeanPostProcessor 以及
equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。
<br />
<br />
在配置文件中使用 context 命名空间之前，必须在 &lt;beans&gt; 元素中声明 context 命名空间。
<br />
<br />
使用 @Component
<br />
<br />
虽然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能，但是 Bean 还是在 XML 文件中通过 &lt;bean&gt; 进行定义 ——
<br />
也就是说，在 XML 配置文件中定义 Bean，通过 @Autowired 或 @Resource 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能
<br />
。能否也通过注释定义 Bean，从 XML 配置文件中完全移除 Bean 定义的配置呢？答案是肯定的，我们通过 Spring 2.5 提供的 @Component
<br />
注释就可以达到这个目标了。
<br />
<br />
下面，我们完全使用注释定义 Bean 并完成 Bean 之间装配：
<br />
<br />
<br />
清单 20. 使用 @Component 注释的 Car.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
import org.springframework.stereotype.Component;
<br />
<br />
@Component
<br />
public class Car {
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
<br />
仅需要在类定义处，使用 @Component 注释就可以将一个类定义了 Spring 容器中的 Bean。下面的代码将 Office 定义为一个 Bean：
<br />
<br />
<br />
清单 21. 使用 @Component 注释的 Office.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
import org.springframework.stereotype.Component;
<br />
<br />
@Component
<br />
public class Office {
<br />
&nbsp;&nbsp;&nbsp; private String officeNo = "001";
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
<br />
这样，我们就可以在 Boss 类中通过 @Autowired 注入前面定义的 Car 和 Office Bean 了。
<br />
<br />
<br />
清单 22. 使用 @Component 注释的 Boss.java
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
<br />
import org.springframework.beans.factory.annotation.Autowired;
<br />
import org.springframework.beans.factory.annotation.Required;
<br />
import org.springframework.beans.factory.annotation.Qualifier;
<br />
import org.springframework.stereotype.Component;
<br />
<br />
@Component("boss")
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; private Car car;
<br />
<br />
&nbsp;&nbsp;&nbsp; @Autowired
<br />
&nbsp;&nbsp;&nbsp; private Office office;
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
<br />
@Component 有一个可选的入参，用于指定 Bean 的名称，在 Boss 中，我们就将 Bean 名称定义为&#8220;boss&#8221;。一般情况下，
<br />
Bean 都是 singleton 的，需要注入 Bean 的地方仅需要通过 byType 策略就可以自动注入了，所以大可不必指定 Bean 的名称。
<br />
<br />
<br />
在使用 @Component 注释后，Spring 容器必须启用类扫描机制以启用注释驱动 Bean 定义和注释驱动 Bean 自动注入的策略。
<br />
Spring 2.5 对 context 命名空间进行了扩展，提供了这一功能，请看下面的配置：
<br />
<br />
清单 23. 简化版的 beans.xml
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
<br />
&nbsp;&nbsp;&nbsp; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<br />
&nbsp;&nbsp;&nbsp; xmlns:context="http://www.springframework.org/schema/context"
<br />
&nbsp;&nbsp;&nbsp; xsi:schemaLocation="http://www.springframework.org/schema/beans
<br />
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
<br />
http://www.springframework.org/schema/context
<br />
http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;context:component-scan base-package="com.baobaotao"/&gt;
<br />
&lt;/beans&gt;
<br />
<br />
<br />
这里，所有通过 &lt;bean&gt; 元素定义 Bean 的配置内容已经被移除，仅需要添加一行 &lt;context:component-scan/&gt; 配置就解决所有问题了
<br />
——Spring XML 配置文件得到了极致的简化（当然配置元数据还是需要的，只不过以注释形式存在罢了）。&lt;context:component-scan/&gt;
<br />
的 base-package 属性指定了需要扫描的类包，类包及其递归子包中所有的类都会被处理。
<br />
<br />
&lt;context:component-scan/&gt; 还允许定义过滤器将基包下的某些类纳入或排除。Spring 支持以下 4 种类型的过滤方式，通过下表说明：
<br />
<br />
表 1. 扫描过滤方式
<br />
过滤器类型 说明
<br />
注释 假如 com.baobaotao.SomeAnnotation 是一个注释类，我们可以将使用该注释的类过滤出来。
<br />
类名指定 通过全限定类名进行过滤，如您可以指定将 com.baobaotao.Boss 纳入扫描，而将 com.baobaotao.Car 排除在外。
<br />
正则表达式 通过正则表达式定义过滤的类，如下所示： com".baobaotao".Default.*
<br />
AspectJ 表达式 通过 AspectJ 表达式定义过滤的类，如下所示： com. baobaotao..*Service+
<br />
<br />
下面是一个简单的例子：
<br />
<br />
&lt;context:component-scan base-package="com.baobaotao"&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;context:include-filter type="regex"
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expression="com".baobaotao".service"..*"/&gt;
<br />
&nbsp;&nbsp;&nbsp; &lt;context:exclude-filter type="aspectj"
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expression="com.baobaotao.util..*"/&gt;
<br />
&lt;/context:component-scan&gt;
<br />
<br />
<br />
值得注意的是 &lt;context:component-scan/&gt; 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能，同时还启用了注释驱动自动注入
<br />
的功能（即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor），因此当使用
<br />
&lt;context:component-scan/&gt; 后，就可以将 &lt;context:annotation-config/&gt; 移除了。
<br />
<br />
默认情况下通过 @Component 定义的 Bean 都是 singleton 的，如果需要使用其它作用范围的 Bean，可以通过 @Scope 注释来达到目标，如以
<br />
下代码所示：
<br />
<br />
<br />
清单 24. 通过 @Scope 指定 Bean 的作用范围
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
package com.baobaotao;
<br />
import org.springframework.context.annotation.Scope;
<br />
&#8230;
<br />
@Scope("prototype")
<br />
@Component("boss")
<br />
public class Boss {
<br />
&nbsp;&nbsp;&nbsp; &#8230;
<br />
}
<br />
<br />
<br />
<br />
这样，当从 Spring 容器中获取 boss Bean 时，每次返回的都是新的实例了。
<br />
<br />
<br />
采用具有特殊语义的注释
<br />
<br />
Spring 2.5 中除了提供 @Component 注释外，还定义了几个拥有特殊语义的注释，它们分别是：@Repository、@Service
<br />
和 @Controller。在目前的 Spring 版本中，这 3 个注释和 @Component 是等效的，但是从注释类的命名上，很容易看出这
<br />
3 个注释分别和持久层、业务层和控制层（Web 层）相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意，
<br />
但 Spring 将在以后的版本中为它们添加特殊的功能。所以，如果 Web 应用程序采用了经典的三层分层结构的话，最好在持久层、业务层和控制层
<br />
分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释，而用 @Component 对那些比较中立的类进行注释。
<br />
<br />
如果 Bean 不是自己编写的类（如 JdbcTemplate、SessionFactoryBean 等），注释配置将无法实施，此时 XML 配置是唯一可用的方式。
<br />
注释配置往往是类级别的，而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction 事务注释，使用 aop/tx 命名空间的事务配置更加灵活
<br />
和简单。
<br />
所以在实现应用中，我们往往需要同时使用注释配置和 XML 配置，对于类级别且不会发生变动的配置可以优先考虑注释配置；而对于那些第三方类以及
<br />
容易发生调整的配置则应优先考虑使用 XML 配置。Spring 会在具体实施 Bean 创建和 Bean 注入之前将这两种配置方式的元信息融合在一起。
<br />
<br />
小结
<br />
<br />
Spring 在 2.1 以后对注释配置提供了强力的支持，注释配置功能成为 Spring 2.5 的最大的亮点之一。合理地使用 Spring 2.5 的注释配置，
<br />
可以有效减少配置的工作量，提高程序的内聚性。但是这并不意味着传统 XML 配置将走向消亡，在第三方类 Bean 的配置，以及那些诸如数据源、
<br />
缓存池、持久层操作模板类、事务管理等内容的配置上，XML 配置依然拥有不可替代的地位。
<br />
spring 事务管理（Transaction） 例子
<br />
&nbsp;
<br />
&nbsp;
<br />
##Transaction） 例子&nbsp;&nbsp;
<br />
传统上， J2EE开发者有两种事务管理选择，全局和本地事务。Spring框架对事务管理的支持极大地改变传统上认为J2EE应用需要应用服务器。这种
<br />
改变不单是仅仅为了通过EJB来使用生命式事务而使用应用服务器。事实上，即使你的应用服务器有强大的JTA功能，Spring框架的声明式事务提供了
<br />
比EJB CMT（声明式事务）更强大，更高效的编程模型。一般来说，只有支持多个事务资源，才会需要应用服务器的JTA功能，而大多数应用不需要能够
<br />
处理跨多种资源。最重要的一点，使用Spring，你可以选择何时把你的应用迁移到全功能的应用服务器。使用Spring不需要像以前一样用编码实现本地
<br />
事务代替EJB CMT或JTA，现在只需要改配置问价，而不必改代码。
<br />
一. Spring事务管理
<br />
<br />
&nbsp; 1. Spring 编程式事务管理（programmatic transaction management）
<br />
&nbsp;&nbsp; DefaultTransactionDefinition def = new DefaultTransactionDefinition();
<br />
TransactionStatus status = transactionManager.getTransaction(def);
<br />
try {
<br />
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
<br />
<br />
jdbcTemplate.update("INSERT INTO USER VALUES('Spring008', 'caterpillar', 'M', 29)");
<br />
<br />
jdbcTemplate.update("INSERT INTO USER VALUES('Spring009', 'momor', 'F', 26)");
<br />
jdbcTemplate.update("INSERT INTO USER VALUES('Spring010, 'beckyday', 'F', 35)");
<br />
<br />
} catch (DataAccessException ex) {
<br />
<br />
transactionManager.rollback(status); // 也可以執行status.setRollbackOnly();
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw ex;
<br />
}
<br />
transactionManager.commit(status);
<br />
<br />
见：http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=2398
<br />
<br />
&nbsp; 2. Spring宣告式事务管理（declarative transaction management）主要是在spring的配置文件中设置
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;
<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;
<br />
<br />
<br />
<br />
&lt;property name="transactionManager"&gt;
<br />
<br />
<br />
<br />
&lt;ref bean="transactionManager"/&gt;
<br />
<br />
<br />
<br />
&lt;/property&gt;
<br />
<br />
<br />
<br />
&lt;property name="target"&gt;
<br />
<br />
<br />
<br />
&lt;ref bean="userDAO"/&gt;
<br />
<br />
<br />
<br />
&lt;/property&gt;
<br />
<br />
<br />
<br />
&lt;property name="transactionAttributes"&gt;
<br />
<br />
<br />
<br />
&lt;props&gt;
<br />
<br />
<br />
<br />
&lt;prop key="insert*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
<br />
<br />
<br />
<br />
&lt;/props&gt;
<br />
<br />
<br />
<br />
&lt;/property&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
<br />
<br />
<br />
&lt;/bean&gt;
<br />
<br />
见：http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=2402
<br />
二. Spring的@Transcation的例子
<br />
<br />
1. class 中
<br />
<br />
@Component //注释就可以将该类定义了 Spring 容器中的 Bean
<br />
<br />
public Class UserDaoImpl implements IUserDao {
<br />
@Override
<br />
//标志updateUsers（）为Transactional&nbsp;&nbsp;&nbsp;&nbsp;
<br />
@Transactional(
<br />
propagation = Propagation.REQUIRED,
<br />
isolation = Isolation.DEFAULT,
<br />
rollbackFor = Exception.class
<br />
)
<br />
public void updateUsers(List&lt;User&gt; users){
<br />
for（User user ：users）{
<br />
//逻辑
<br />
}
<br />
}
<br />
}
<br />
注意： 事务的属性和基本概念
<br />
<br />
<br />
<br />
Required : 如果在一个事务中调用，就将该方法加到此事务中，如果没有启动事务，就创建新事务
<br />
<br />
<br />
<br />
RequiredNew ： 不管当前有没有事务，都启动新事务，如果有，会被挂起，直到此方法结束
<br />
<br />
<br />
<br />
NotSupported ： 不能在事务中执行此方法，如果有事务，会被挂起，直到此方法结束&nbsp;
<br />
<br />
<br />
<br />
Supports ： 如果有当前事务，此方法回加到当前事务，如果没有，容器不会启动新事务
<br />
<br />
<br />
<br />
Mandatory ： 必须在事务中执行此方法，否则会抛出异常 ： TransactionRequiredException
<br />
<br />
<br />
<br />
Never ： 必须不在事务中调用此方法，否则抛出RemoteException(远程调用)或EJBException(本地调用)
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.spring配置文件加入：
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .....................
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tx:annotation-driven/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .....................
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意： webSerivces 是不支持事务的，如果使用事务，必须在下层的Object中实现，例如在Service层或者是Dao层实现。
<br />
<br />
1. 使用Spring注解来注入属性
<br />
1.1. 使用注解以前我们是怎样注入属性的
<br />
类的实现：
<br />
Java代码
<br />
public class UserManagerImpl implements UserManager {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; private UserDao userDao;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; public void setUserDao(UserDao userDao) {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.userDao = userDao;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;
<br />
}&nbsp;
<br />
<br />
public class UserManagerImpl implements UserManager {
<br />
private UserDao userDao;
<br />
public void setUserDao(UserDao userDao) {
<br />
this.userDao = userDao;
<br />
}
<br />
...
<br />
}
<br />
<br />
配置文件：
<br />
Java代码
<br />
&lt;bean id="userManagerImpl" class="com.kedacom.spring.annotation.service.UserManagerImpl"&gt;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; &lt;property name="userDao" ref="userDao" /&gt;&nbsp;&nbsp;
<br />
&lt;/bean&gt;&nbsp;&nbsp;
<br />
&lt;bean id="userDao" class="com.kedacom.spring.annotation.persistence.UserDaoImpl"&gt;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory" ref="mySessionFactory" /&gt;&nbsp;&nbsp;
<br />
&lt;/bean&gt;&nbsp;
<br />
<br />
&lt;bean id="userManagerImpl" class="com.kedacom.spring.annotation.service.UserManagerImpl"&gt;
<br />
&lt;property name="userDao" ref="userDao" /&gt;
<br />
&lt;/bean&gt;
<br />
&lt;bean id="userDao" class="com.kedacom.spring.annotation.persistence.UserDaoImpl"&gt;
<br />
&lt;property name="sessionFactory" ref="mySessionFactory" /&gt;
<br />
&lt;/bean&gt;
<br />
<br />
<br />
1.2. 引入@Autowired注解（不推荐使用，建议使用@Resource）
<br />
类的实现（对成员变量进行标注）
<br />
Java代码
<br />
public class UserManagerImpl implements UserManager {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; @Autowired&nbsp;
<br />
&nbsp;&nbsp;&nbsp; private UserDao userDao;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;
<br />
}&nbsp;
<br />
<br />
public class UserManagerImpl implements UserManager {
<br />
@Autowired
<br />
private UserDao userDao;
<br />
...
<br />
}
<br />
<br />
或者（对方法进行标注）
<br />
Java代码
<br />
public class UserManagerImpl implements UserManager {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; private UserDao userDao;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; @Autowired&nbsp;
<br />
&nbsp;&nbsp;&nbsp; public void setUserDao(UserDao userDao) {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.userDao = userDao;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;
<br />
}&nbsp;
<br />
<br />
public class UserManagerImpl implements UserManager {
<br />
private UserDao userDao;
<br />
@Autowired
<br />
public void setUserDao(UserDao userDao) {
<br />
this.userDao = userDao;
<br />
}
<br />
...
<br />
}
<br />
配置文件
<br />
Java代码
<br />
&lt;bean id="userManagerImpl" class="com.kedacom.spring.annotation.service.UserManagerImpl" /&gt;&nbsp;&nbsp;
<br />
&lt;bean id="userDao" class="com.kedacom.spring.annotation.persistence.UserDaoImpl"&gt;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory" ref="mySessionFactory" /&gt;&nbsp;&nbsp;
<br />
&lt;/bean&gt;&nbsp;
<br />
<br />
&lt;bean id="userManagerImpl" class="com.kedacom.spring.annotation.service.UserManagerImpl" /&gt;
<br />
&lt;bean id="userDao" class="com.kedacom.spring.annotation.persistence.UserDaoImpl"&gt;
<br />
&lt;property name="sessionFactory" ref="mySessionFactory" /&gt;
<br />
&lt;/bean&gt;
<br />
<br />
@Autowired可以对成员变量、方法和构造函数进行标注，来完成自动装配的工作。以上两种不同实现方式中，@Autowired的标注位置不同，它们都会在Spring在初始化userManagerImpl这个bean时，自动装配userDao这个属性，区别是：第一种实现中，Spring会直接将UserDao类型的唯一一个bean赋值给userDao这个成员变量；第二种实现中，Spring会调用setUserDao方法来将UserDao类型的唯一一个bean装配到userDao这个属性。
<br />
<br />
1.3. 让@Autowired工作起来
<br />
要使@Autowired能够工作，还需要在配置文件中加入以下代码
<br />
Java代码
<br />
&lt;bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" /&gt;&nbsp;
<br />
<br />
&lt;bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" /&gt;
<br />
<br />
<br />
1.4. @Qualifier
<br />
@Autowired是根据类型进行自动装配的。在上面的例子中，如果当Spring上下文中存在不止一个UserDao类型的bean时，就会抛出BeanCreationException异常；如果Spring上下文中不存在UserDao类型的bean，也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题。
<br />
1. 可能存在多个UserDao实例
<br />
Java代码
<br />
@Autowired&nbsp;
<br />
public void setUserDao(@Qualifier("userDao") UserDao userDao) {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; this.userDao = userDao;&nbsp;&nbsp;
<br />
}&nbsp;
<br />
<br />
@Autowired
<br />
public void setUserDao(@Qualifier("userDao") UserDao userDao) {
<br />
this.userDao = userDao;
<br />
}
<br />
<br />
这样，Spring会找到id为userDao的bean进行装配。
<br />
2. 可能不存在UserDao实例
<br />
<br />
Java代码
<br />
@Autowired(required = false)&nbsp;&nbsp;
<br />
public void setUserDao(UserDao userDao) {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; this.userDao = userDao;&nbsp;&nbsp;
<br />
}&nbsp;
<br />
<br />
@Autowired(required = false)
<br />
public void setUserDao(UserDao userDao) {
<br />
this.userDao = userDao;
<br />
}
<br />
<br />
<br />
1.5. @Resource（JSR-250标准注解，推荐使用它来代替Spring专有的@Autowired注解）
<br />
Spring 不但支持自己定义的@Autowired注解，还支持几个由JSR-250规范定义的注解，它们分别是@Resource、@PostConstruct以及@PreDestroy。
<br />
@Resource的作用相当于@Autowired，只不过@Autowired按byType自动注入，而@Resource默认按byName自动注入罢了。@Resource有两个属性是比较重要的，分别是name和type，Spring将
@Resource注解的name属性解析为bean的名字，而type属性则解析为bean的类型。所以如果使用name属性，则使用byName的自
动注入策略，而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性，这时将通过反射机制使用byName自动
注入策略。 <br />
@Resource装配顺序
<br />
<br />
如果同时指定了name和type，则从Spring上下文中找到唯一匹配的bean进行装配，找不到则抛出异常
<br />
如果指定了name，则从上下文中查找名称（id）匹配的bean进行装配，找不到则抛出异常
<br />
如果指定了type，则从上下文中找到类型匹配的唯一bean进行装配，找不到或者找到多个，都会抛出异常
<br />
如果既没有指定name，又没有指定type，则自动按照byName方式进行装配（见2）；如果没有匹配，则回退为一个原始类型（UserDao）进行匹配，如果匹配则自动装配；
<br />
<br />
<br />
1.6. @PostConstruct（JSR-250）
<br />
在方法上加上注解@PostConstruct，这个方法就会在Bean初始化之后被Spring容器执行（注：Bean初始化包括，实例化Bean，并装配Bean的属性（依赖注入））。
<br />
它的一个典型的应用场景是，当你需要往Bean里注入一个其父类中定义的属性，而你又无法复写父类的属性或属性的setter方法时，如：
<br />
Java代码
<br />
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; private SessionFactory mySessionFacotry;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; @Resource&nbsp;
<br />
&nbsp;&nbsp;&nbsp; public void setMySessionFacotry(SessionFactory sessionFacotry) {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.mySessionFacotry = sessionFacotry;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; @PostConstruct&nbsp;
<br />
&nbsp;&nbsp;&nbsp; public void injectSessionFactory() {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.setSessionFactory(mySessionFacotry);&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;
<br />
}&nbsp;
<br />
<br />
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
<br />
private SessionFactory mySessionFacotry;
<br />
@Resource
<br />
public void setMySessionFacotry(SessionFactory sessionFacotry) {
<br />
this.mySessionFacotry = sessionFacotry;
<br />
}
<br />
@PostConstruct
<br />
public void injectSessionFactory() {
<br />
super.setSessionFactory(mySessionFacotry);
<br />
}
<br />
...
<br />
}
<br />
<br />
这里通过@PostConstruct，为UserDaoImpl的父类里定义的一个sessionFactory私有属性，注入了我们自己定义
的sessionFactory（父类的setSessionFactory方法为final，不可复写），之后我们就可以通过调用
super.getSessionFactory()来访问该属性了。 <br />
<br />
1.7. @PreDestroy（JSR-250）
<br />
在方法上加上注解@PreDestroy，这个方法就会在Bean初始化之后被Spring容器执行。由于我们当前还没有需要用到它的场景，这里不不去演示。其用法同@PostConstruct。
<br />
<br />
1.8. 使用&lt;context:annotation-config /&gt;简化配置
<br />
Spring2.1添加了一个新的context的Schema命名空间，该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配
置。我们知道注释本身是不会做任何事情的，它仅提供元数据信息。要使元数据信息真正起作用，必须让负责处理这些元数据的处理器工作起来。 <br />
AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor就是处理这些注释元数据的处理器。但是直接在Spring配置文件中定义这些Bean显得比较笨拙。Spring为我们提供了一种方便的注册这些BeanPostProcessor的方式，这就是&lt;context:annotation-config /&gt;：
<br />
Java代码
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"&nbsp; <br />
&nbsp;&nbsp;&nbsp; xsi:schemaLocation="http://www.springframework.org/schema/beans&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; http://www.springframework.org/schema/context&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; &lt;context:annotation-config /&gt;&nbsp;&nbsp;
<br />
&lt;/beans&gt;&nbsp;
<br />
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans
<br />
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
<br />
http://www.springframework.org/schema/context
<br />
http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;
<br />
&lt;context:annotation-config /&gt;
<br />
&lt;/beans&gt;
<br />
<br />
&lt;context:annotationconfig /&gt;将隐式地向Spring容
器注册AutowiredAnnotationBeanPostProcessor、
CommonAnnotationBeanPostProcessor、
PersistenceAnnotationBeanPostProcessor以及
RequiredAnnotationBeanPostProcessor这4个BeanPostProcessor。 <br />
<br />
2. 使用Spring注解完成Bean的定义
<br />
以上我们介绍了通过@Autowired或@Resource来实现在Bean中自动注入的功能，下面我们将介绍如何注解Bean，从而从XML配置文件中完全移除Bean定义的配置。
<br />
<br />
2.1. @Component（不推荐使用）、@Repository、@Service、@Controller
<br />
只需要在对应的类上加上一个@Component注解，就将该类定义为一个Bean了：
<br />
Java代码
<br />
@Component&nbsp;
<br />
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;
<br />
}&nbsp;
<br />
<br />
@Component
<br />
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
<br />
...
<br />
}
<br />
<br />
使用@Component注解定义的Bean，默认的名称（id）是小写开头的非限定类名。如这里定义的Bean名称就是userDaoImpl。你也可以指定Bean的名称：
<br />
@Component("userDao")
<br />
@Component是所有受Spring管理组件的通用形式，Spring还提供了更加细化的注解形式：@Repository、@Service、@Controller，它们分别对应存储层Bean，业务层Bean，和展示层Bean。目前版本（2.5）中，这些注解与@Component的语义是一样的，完全通用，在Spring以后的版本中可能会给它们追加更多的语义。所以，我们推荐使用@Repository、@Service、@Controller来替代@Component。
<br />
<br />
2.2. 使用&lt;context:component-scan /&gt;让Bean定义注解工作起来
<br />
Java代码
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"&nbsp; <br />
&nbsp;&nbsp;&nbsp; xsi:schemaLocation="http://www.springframework.org/schema/beans&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; http://www.springframework.org/schema/context&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; &lt;context:component-scan base-package="com.kedacom.ksoa" /&gt;&nbsp;&nbsp;
<br />
&lt;/beans&gt;&nbsp;
<br />
<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans
<br />
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
<br />
http://www.springframework.org/schema/context
<br />
http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;
<br />
&lt;context:component-scan base-package="com.kedacom.ksoa" /&gt;
<br />
&lt;/beans&gt;
<br />
<br />
这里，所有通过&lt;bean&gt;元素定义Bean的配置内容已经被移除，仅需要添加一行&lt;context:component-scan /&gt;配置就解决所有问题了——Spring XML配置文件得到了极致的简化（当然配置元数据还是需要的，只不过以注释形式存在罢了）。&lt;context:component-scan /&gt;的base-package属性指定了需要扫描的类包，类包及其递归子包中所有的类都会被处理。
<br />
&lt;context:component-scan /&gt;还允许定义过滤器将基包下的某些类纳入或排除。Spring支持以下4种类型的过滤方式：
<br />
<br />
过滤器类型 表达式范例 说明
<br />
注解 org.example.SomeAnnotation 将所有使用SomeAnnotation注解的类过滤出来
<br />
类名指定 org.example.SomeClass 过滤指定的类
<br />
正则表达式 com".kedacom".spring".annotation".web"..* 通过正则表达式过滤一些类
<br />
AspectJ表达式 org.example..*Service+ 通过AspectJ表达式过滤一些类
<br />
<br />
以正则表达式为例，我列举一个应用实例：
<br />
Java代码
<br />
&lt;context:component-scan base-package="com.casheen.spring.annotation"&gt;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; &lt;context:exclude-filter type="regex" expression="com".casheen".spring".annotation".web"..*" /&gt;&nbsp;&nbsp;
<br />
&lt;/context:component-scan&gt;&nbsp;
<br />
<br />
&lt;context:component-scan base-package="com.casheen.spring.annotation"&gt;
<br />
&lt;context:exclude-filter type="regex" expression="com".casheen".spring".annotation".web"..*" /&gt;
<br />
&lt;/context:component-scan&gt;
<br />
<br />
值得注意的是&lt;context:component-scan
/&gt;配置项不但启用了对类包进行扫描以实施注释驱动Bean定义的功能，同时还启用了注释驱动自动注入的功能（即还隐式地在内部注册了
AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor），
因此当使用&lt;context:component-scan /&gt;后，就可以将&lt;context:annotation-config /&gt;移除了。
<br />
<br />
2.3. 使用@Scope来定义Bean的作用范围
<br />
在使用XML定义Bean时，我们可能还需要通过bean的scope属性来定义一个Bean的作用范围，我们同样可以通过@Scope注解来完成这项工作：
<br />
Java代码
<br />
@Scope("session")&nbsp;&nbsp;
<br />
@Component()&nbsp;&nbsp;
<br />
public class UserSessionBean implements Serializable {&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;
<br />
}&nbsp;
<br />
<br />
@Scope("session")
<br />
@Component()
<br />
public class UserSessionBean implements Serializable {
<br />
...
<br />
}
<br />
<br />
<br />
3. 参考
<br />
http://kingtai168.javaeye.com/blog/244002
<br />
http://www.javaeye.com/topic/244153
<br />
http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config
<br />
http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-classpath-scanning
<img src ="http://www.blogjava.net/sealyu/aggbug/309801.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-01-16 23:30 <a href="http://www.blogjava.net/sealyu/archive/2010/01/16/309801.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Debug Google Web Toolkit with Struts/Tiles</title><link>http://www.blogjava.net/sealyu/archive/2010/01/15/309666.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 15 Jan 2010 04:05:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/01/15/309666.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/309666.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/01/15/309666.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/309666.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/309666.html</trackback:ping><description><![CDATA[<p>Ok,
your fresh new GWT user interface is ready, it works great with GWTShell's internal Tomcat and with server-side stubs,
but as you compile and deploy it on your web-app there are problems.</p>
<p>Of course you can debug with a JavaScript debugger (Venkman or Microsoft's Script Debugger),
but generated JavaScript is really complex, for people and for those unstable debuggers.</p>
<p>But, wait, we have a debugger, is our state-of-the-art debugger,
integrated in our IDE and GWT can use it, if only you could run client
into the Shell and server side into your preferred development
envinroment !!!</p>
<p>We could deploy our web-app into the integrated Tomcat, but it's not easy to do (and an hell to mantain)
and also can be that you don't want (or you can't) use Tomcat for your Web Application.</p>
<p>I think that everyone should do his work, so let's Google Shell do
the client, and your application server run the server side code. It's
not difficult, you only need to follow these few steps.</p>
<p>First thing do to it's a little (I promise really little and only
one) change to your GWT code, so that the client will always use
absolute urls for your web-app, allowing us to use the ContextPath
without problems, whatever it's the relative position of your Google
client inside your root context.</p>
<p>To do that we have to register this way the end-point of the remote service:</p>
<pre>	endpoint.setServiceEntryPoint( GWT.getModuleBaseURL()+"/myRemoteService" );</pre>
<p>now we must also remember to change our servlet path inside the GWT module file (*.gwt.xml)
putting the complete service name at the top, in this case <strong>/mypackage.google.GWTClient</strong>:	</p>
<pre>  	&lt;servlet path="/mypackage.google.GWTClient/myRemoteService" class="mypackage.google.server.MyRemoteServiceImpl"/&gt;</pre>
<p>No other change to your client side code, I promise again.
</p>
<p>Now let's start to modify the web-app to run our services. The first thing is to include in our <em>WEB-INF/lib</em> the <strong>gwt-user.jar</strong>.</p>
<p>Unfortunately the one supplied by Google doesn't work, because it
includes the javax.servlet.* classes to simplify the automatic
generation of development projects.</p>
<p>We must strip that out (with an ANT Task obviously):</p>
<textarea readonly="true" cols="100" rows="16">	&lt;property name="original.gwt.jar.lib" value="&lt;path-to&gt;"gwt-user.jar" /&gt;
&lt;property name="temp.dir" value="./tempDir"/&gt;
&lt;target name="stripGWT-lib"&gt;
&lt;mkdir dir="${temp.dir}"/&gt;
&lt;unjar src="${original.gwt.jar.lib}" dest="${temp.dir}"&gt;
&lt;patternset&gt;
&lt;exclude name="javax/**/*.*"/&gt;
&lt;/patternset&gt;
&lt;/unjar&gt;
&lt;jar destfile="${web-inf.lib.dir}/gwt-user.jar" basedir="${temp.dir}"/&gt;
&lt;delete dir="${temp.dir}"/&gt;
&lt;/target&gt;</textarea>
<p>Now we can also add that to our IDE build-path.</p>
<p>Everything is ready to begin the configuration of our web-app,
beginning with the registration of the remote service inside web.xml,
remembering the module name we used in the *.gwt.xml file:</p>
<textarea readonly="true" cols="100" rows="10">	&lt;servlet&gt;
&lt;servlet-name&gt;MyRemoteService&lt;/servlet-name&gt;
&lt;servlet-class&gt;mypackage.google.server.MyRemoteServiceImpl&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;MyRemoteService&lt;/servlet-name&gt;
&lt;url-pattern&gt;/mypackage.google.GWTClient/myRemoteService&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</textarea>
<p>We don't have yet finished, we must also "simulate" one of the call
implemented by GWTServlet, registering its path into web.xml and with
the help of a JSP::</p>
<textarea readonly="true" rows="10" cols="100">	&lt;servlet&gt;
&lt;servlet-name&gt;gwt-hosted&lt;/servlet-name&gt;
&lt;jsp-file&gt;/gwt-hosted.jsp&lt;/jsp-file&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;gwt-hosted&lt;/servlet-name&gt;
&lt;url-pattern&gt;/mypackage.google.GWTClient/gwt-hosted.html&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</textarea>
<p>the
<strong>gwt-hosted.jsp</strong> JSP it's only a simple script with a row of scriptlet that export our web-app context to the GWT client::</p>
<textarea readonly="true" rows="18" cols="100">	&lt;html&gt;
&lt;body&gt;
&lt;script&gt;
var $wnd = parent;
var $doc = $wnd.document;
var q = location.search.substring(1);
var i = q.lastIndexOf("=");
var $moduleName = q;
var $moduleBaseURL = "&lt;%= request.getContextPath()%&gt;/" + $moduleName;
if (i != -1) {
$moduleBaseURL = q.substring(0, i);
$moduleName = q.substring(i+1);
}
parent.__gwt_initHostedModeModule(this, $moduleName);
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</textarea>
<p>We did it !!!!The good thing is that we don't even have to "Compile" our
JavaScript in the destination context during debug. Infact the GWT Shell
callback will always call our Java classes and not the JavaScript code.</p>
<p>So we have together maximum flexibility on client-side project for our debugging purposes,
and only at the very finish of the development cycle we will produce the JavaScript code.</p>
<p>Could we be in a better position as web developer ?</p>
<p>Now some tips about GWT's Shell:</p>
<p>Are we in debug mode or not ? The answer is yes, so we should
configure the Shell log to a more verbose level (if you have used
GWT.log in your code):</p>
<pre>	-logLevel ERROR|WARN|INFO|TRACE|DEBUG|SPAM|ALL</pre>
<p>We should also disable the integrated Tomcat:</p>
<pre>	-noserver</pre>
<p>and configure the Shell to generate JavaScript code directly inside our web-app project:</p>
<pre>	-out &lt;web-app-project-dir&gt;</pre>
<p>Also can be useful to open directly the Shell browser to our web-app
at startup, simply putting the http url as the last parameter of the
command line that start the Shell.</p>
<p>But what if all this it's not enough to solve our problems, may be because the generated JavaScript is not working fine ?
Well, together with much luck, we can try to switch the generation code to DETAILED:</p>
<pre> -style DETAILED</pre>
<p>and use traditional JavaScript debugging tecnique.</p>
<h1>Integrate Google Web Toolkit with Struts/Tiles</h1>
<p>Ok, now everything it's integrated with your web-application and
you're in love with GWT. You did really a great work, a prototype that
it's working fine and looks pretty. But when time comes that you must
put it into your old fashioned Struts/Tiles web-application it stops
working and sure you can't write everything from scratch again, but do
a step after another and start with a single functionality done with
GWT.</p>
<p>Well, I don't know you, but I suffered from this problem, but at the
end, thank to some little tips, I was able to let Struts and GWT not
only to live together, but to cooperate to make my application look
better.</p>
<p>It was like having an old fashioned B&amp;W TV Set and a new HDMI on the same bench, side by side !!!</p>
<p>Well, to achieve this, we have to do some simple steps:</p>
<p> - In <em>web.xml</em> add another Struts <em>action</em> mapping, this time it must be extension based, for instance I choosed *.gwt:</p>
<textarea readonly="true" cols="80" rows="6">		&lt;servlet-mapping&gt;
&lt;servlet-name&gt;action&lt;/servlet-name&gt;
&lt;url-pattern&gt;*.gwt&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
</textarea>
<p> - In <em>struts-config.xml</em> write the new Struts <em>action</em>
that will render the GWT page, taking care to put at the top of the
path the complete GWT's module name, so that the script will be able to
find internal files with no problems:</p>
<textarea readonly="true" cols="80" rows="5">        &lt;action path="/mypackage.google.GWTClient/GWTClient"
forward=".my.gwt.application"/&gt;
</textarea>
<p> - (only if you use Tiles as view)Add a <em>Tiles</em> definition that will include GWT's (in my example into the body of the layout JSP):</p>
<textarea readonly="true" cols="80" rows="8">    &lt;definition name=".my.gwt.application" extends=".mainLayout"&gt;
&lt;put name="title"  value="Google Web Toolkit in Tiles" /&gt;
&lt;put name="body"
value="/mypackage.google.GWTClient/GWTClient.html" /&gt;
&lt;put name="header"   value="/WEB-INF/jsp/header.jsp" /&gt;
&lt;/definition&gt;
</textarea>
<p> - (if you used filters for Struts Action mapping)Update
your filters so that they will be able to do their work also with the
new GWT module:</p>
<textarea readonly="true" cols="80" rows="5">	&lt;filter-mapping&gt;
&lt;filter-name&gt;AuthenticationFilter&lt;/filter-name&gt;
&lt;url-pattern&gt;*.gwt&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;
</textarea>
<p>That's all, the game is made.</p>
<br />
<strong>P.S.:</strong>the same applies if you used Spring MVC.
<h1>Integrating it also with Spring/Hibernate</h1>
<p>The life-cycle of a GWT's Service is managed by your
servlet-container, because as a design choice Google opted for the
portabily and simplicity of the <strong>Servlet</strong> model.<br />
In our code this mean that our service implementations will all inherit (indirectly) from <em>HttpServlet</em>. This means also that to integrate them in our Spring managed container we have to play dirty. I decided to use <strong>static</strong> property inside our Google Service implementation, so that Spring will be able to assign it at startup time.<br />
Pratically, if we want to inject something in GWT's service <em>mypackage.google.server.MyRemoteServiceImpl</em>'s  <em>myProperty</em> property, it's enough to declare it static:</p>
<textarea rows="3" cols="100" readonly="true">	private static MyProperty myProperty;
</textarea>
<p>Then to let Spring works, we also need an instance setter:</p>
<textarea rows="5" cols="120" readonly="true">	public void setMyProperty(MyProperty mp) // remember, myProperty is static, you can't use this
{
myProperty = mp;
}
</textarea>
<p>In our <em>applicationContext.xml</em> we can inject the <em>myPropertyBean</em> Bean simply with a dummy bean declaration of the GWT implementation class:</p>
<textarea rows="4" cols="100" readonly="true">	&lt;bean id="myGWTService" class="mypackage.google.server.MyRemoteServiceImpl"&gt;
&lt;property name="myProperty"&gt;&lt;ref local="myPropertyBean"/&gt;&lt;/property&gt;
&lt;/bean&gt;
</textarea>
<p>This works because at startup Spring will instantiate an
instance of the MyRemoteServiceImpl class, this instance will never be
used by the servlet-container, but the bean property is contained into
a static variable, so <strong>every</strong> instance will have it !!!<br />
Now, if your business bean <em>myPropertyBean</em> uses <strong>Hibernate</strong> and you usually use Spring's <strong>OpenSessionInView</strong><em>Filter/Interceptor</em>
to manage your Session, you need a further step and use an AOP
Interceptor. But we are lucky in this, because all is done by Spring
out-of-the-box only with few lines of configuration::</p>
<textarea rows="40" cols="120" readonly="true">	&lt;!-- Our Propagation policy --&gt;
&lt;bean id="matchAllWithPropReq"
class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource"&gt;
&lt;property name="transactionAttribute"&gt;&lt;value&gt;PROPAGATION_REQUIRED&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;!-- Match them all interceptor --&gt;
&lt;bean id="matchAllTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"&gt;
&lt;property name="transactionManager"&gt;&lt;ref bean="transactionManager"/&gt;&lt;/property&gt;
&lt;property name="transactionAttributeSource"&gt;&lt;ref bean="matchAllWithPropReq"/&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;!-- This AutoProxyCreator bean will let us declaratively decide which beans will have the transaction
managed with PROPAGATION_REQUIRED --&gt;
&lt;bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;idref local="matchAllTxInterceptor"/&gt;
&lt;idref bean="myHibernateInterceptor"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="beanNames"&gt;
&lt;list&gt;
&lt;idref local="myPropertyBean"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;!--  This is the bean that, if it doesn't exist, create and then close Hibernate Session --&gt;
&lt;bean id="myHibernateInterceptor" class="org.springframework.orm.hibernate3.HibernateInterceptor"&gt;
&lt;property name="sessionFactory"&gt;&lt;ref local="sessionFactory"/&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;
&lt;property name="sessionFactory"&gt;&lt;ref local="sessionFactory"/&gt;&lt;/property&gt;
&lt;/bean&gt;
</textarea>
<p>With this configuration, if <em>myPropertyBean</em> is used
outside a transaction scope, a new Hibernate transaction will be
created and assigned to it. In our case this is also the span of the
Hibernate's Session (thanks to HibernateTransactionManager). The <em>myHibernateInterceptor</em> will also take charge of eventually close the Session at the end of the business method.</p>
<p>
<a href="http://www.jroller.com/page/masini?entry=template_project_for_gwt_with">continue here...</a>
</p>
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/309666.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-01-15 12:05 <a href="http://www.blogjava.net/sealyu/archive/2010/01/15/309666.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Integrating GWT with Spring Security</title><link>http://www.blogjava.net/sealyu/archive/2010/01/15/309637.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 15 Jan 2010 01:39:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/01/15/309637.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/309637.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/01/15/309637.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/309637.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/309637.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Yesterday, I wrote about How to do cross-domain GWT RPC with a ProxyServlet. Today I'll be discussinghow to modify the ProxyServlet to authenticate with Spring Security. For the application I'm work...&nbsp;&nbsp;<a href='http://www.blogjava.net/sealyu/archive/2010/01/15/309637.html'>阅读全文</a><img src ="http://www.blogjava.net/sealyu/aggbug/309637.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-01-15 09:39 <a href="http://www.blogjava.net/sealyu/archive/2010/01/15/309637.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring Security 2 中动态角色实现的讨论（转）</title><link>http://www.blogjava.net/sealyu/archive/2010/01/11/309070.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 11 Jan 2010 14:03:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/01/11/309070.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/309070.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/01/11/309070.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/309070.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/309070.html</trackback:ping><description><![CDATA[安全框架的主体包括两部分即验权和授权。Spring
Security2可以很好的实现这两个过程。Spring
Security2对其前身acegi最大的改进是提供了自定义的配置标签，通过Security的命名空间定义了http和
authentication-provider等标签，这样做的好处是极大地简化了框架的配置，并很好地隐藏了框架实现的细节，在配置的表述上也更清
晰，总体上提高了框架的易用性。
<br />
<br />
然而，该框架默认的权限配置方式在xml中，又因为新版本隐藏了实现细节，在动态权限的扩展上，能力变小了。在验权过程中，遇到的问题不多。但在
授权时，如果是acegi，人们可以通过继承AbstractFilterInvocationDefinitionSource类实现在授权（即资源角
色和用户角色的匹配）前，针对资源的角色的获取。而新版本因为用新标签进行了整合，这个过程被默认的类实现隐藏掉了，包括过滤器，资源获取和角色定义等过
程都由框架来实现，于是很多人在使用Spring
Security2时也想通过改动DefaultFilterInvocationDefinitionSource对资源的获取来实现数据库或文件中的
动态的角色。不过这样的改动侵入性比较高，而且还保留了acegi的痕迹，也违背了开闭的原则。
<br />
<br />
其实，我们完全可以通过Spring Security2 accessManager提供的自定义投票机制来解决这个问题，这样既不影响现有的基于URL的配置，还可以加入自己的动态的权限配置。
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实现策略如下：
<br />
<br />
1 定义类DynamicRoleVoter实现AccessDecisionVoter，注入实现接口DynamicRoleProvider（用来定义获取角色的方法）的提供动态角色的类
<br />
<br />
2 在两个supports方法中返回true
<br />
<br />
3 在vote方法中，有三个参数(Authentication authentication, Object
object,ConfigAttributeDefinition config)
通过第一个获取用户的权限集合，第二个可以获取到资源对象，进而通过DynamicRoleProvider获取到角色集合进行匹配。
<br />
<br />
4 在配置文件中加入DynamicRoleVoter，如下：
<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:bean&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="accessDecisionManager"</span><span style="color: #ff0000;">&nbsp;class</span><span style="color: #0000ff;">="org.springframework.security.vote.AffirmativeBased"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="decisionVoters"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:list</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:bean&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="org.springframework.security.vote.RoleVoter"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:bean&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="org.springframework.security.vote.AuthenticatedVoter"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:bean&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="DynamicRoleVoter"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="dynamicRoleProvider"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:ref&nbsp;</span><span style="color: #ff0000;">local</span><span style="color: #0000ff;">="dynamicRoleProvider"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">beans:property</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">beans:bean</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">beans:list</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">beans:property</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">beans:bean</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">beans:bean&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">=&#8221;&nbsp;</span><span style="color: #ff0000;">dynamicRoleProvider&#8221;&nbsp;class</span><span style="color: #0000ff;">=&#8221;&#8230;&#8221;&gt;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #ff0000;">&#8230;&#8230;&nbsp;&nbsp;<br />
&nbsp;&lt;/beans:bean</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">&nbsp;&nbsp;<br />
<br />
<br />
<br />
</span>http://hszdz.javaeye.com/blog/337652<br />
</div>
<pre style="display: none;" name="code" class="xml">&lt;beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased"&gt;
&lt;beans:property name="decisionVoters"&gt;
&lt;beans:list&gt;
&lt;beans:bean class="org.springframework.security.vote.RoleVoter" /&gt;
&lt;beans:bean class="org.springframework.security.vote.AuthenticatedVoter" /&gt;
&lt;beans:bean class="DynamicRoleVoter"&gt;
&lt;beans:property name="dynamicRoleProvider"&gt;
&lt;beans:ref local="dynamicRoleProvider"/&gt;
&lt;/beans:property&gt;
&lt;/beans:bean&gt;
&lt;/beans:list&gt;
&lt;/beans:property&gt;
&lt;/beans:bean&gt;
&lt;beans:bean id=&#8221; dynamicRoleProvider&#8221; class=&#8221;&#8230;&#8221;&gt;
&#8230;&#8230;
&lt;/beans:bean&gt;
</pre>
<img src ="http://www.blogjava.net/sealyu/aggbug/309070.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-01-11 22:03 <a href="http://www.blogjava.net/sealyu/archive/2010/01/11/309070.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SpringSide 3 中的安全框架</title><link>http://www.blogjava.net/sealyu/archive/2010/01/08/308743.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 08 Jan 2010 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/01/08/308743.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/308743.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/01/08/308743.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/308743.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/308743.html</trackback:ping><description><![CDATA[<div>
<p>在SpringSide
3的官方文档中，说安全框架使用的是Spring Security
2.0。乍一看，吓了我一跳，以为Acegi这么快就被淘汰了呢。上搜索引擎一搜，发现原来Spring Security 2.0就是Acegi
2.0。悬着的心放下来了。虽然SpringSide 3中关于Acegi的配置文件看起来很不熟悉，但是读了Acegi
2.0的官方文档后，一切都释然了。<br />
<br />
先来谈一谈Acegi的基础知识，Acegi的架构比较复杂，但是我希望我下面的只言片语能够把它说
清楚。大家都知道，如果要对Web资源进行保护，最好的办法莫过于Filter，要想对方法调用进行保护，最好的办法莫过于AOP。Acegi对Web资
源的保护，就是靠Filter实现的。如下图：<br />
<img alt="001.PNG" src="../../images/blogjava_net/youxia/SpringSide14/001.PNG" width="484" border="0" height="185" /><br />
<br />
一
般来说，我们的Filter都是配置在web.xml中，但是Acegi不一样，它在web.xml中配置的只是一个代理，而真正起作用的Filter是
作为Bean配置在Spring中的。web.xml中的代理依次调用这些Bean，就实现了对Web资源的保护，同时这些Filter作为Bean被
Spring管理，所以实现AOP也很简单，真的是一举两得啊。<br />
<br />
Acegi中提供的Filter不少，有十多个，一个一个学起来比较复杂。但是对于我们Web开发者来说，常用的就那么几个，如下图中的被红圈圈标记出来的：<br />
<img alt="002.PNG" src="../../images/blogjava_net/youxia/SpringSide14/002.PNG" width="496" border="0" height="479" /><br />
<br />
从上到下，它们实现的功能依次是1、制定必须为https连接；2、从Session中提取用户的认证信息；3、退出登录；4、登录；5、记住用户；6、所有的应用必须配置这个Filter。<br />
<br />
一
般来说，我们写Web应用只需要熟悉这几个Filter就可以了，如果不需要https连接，连第一个也不用熟悉。但是有人肯定会想，这些Filter怎
么和我的数据库联系起来呢？不用着急，这些Filter并不直接处理用户的认证，也不直接处理用户的授权，而是把它们交给了认证管理器和决策管理器。如下
图：<br />
<img alt="003.PNG" src="../../images/blogjava_net/youxia/SpringSide14/003.PNG" width="532" border="0" height="374" /></p>
<p>对于这两种管理器，那也是不需要我们写代码的，Acegi也提供了现成的类。那么大家又奇怪了：又是现成的，那怎么和我的数据库关联起来呢？别着急，其实这两个管理器自己也不做事，认证管理器把任务交给了Provider，而决策管理器则把任务交给了Voter，如下图：<br />
<img alt="004.PNG" src="../../images/blogjava_net/youxia/SpringSide14/004.PNG" width="504" border="0" height="505" /></p>
<p>现
在我要告诉你们，这里的Provider和Voter也是不需要我们写代码的。不要崩溃，快到目标了。Acegi提供了多个Provider的实现类，如
果我们想用数据库来储存用户的认证数据，那么我们就选择DaoAuthenticationProvider。对于Voter，我们一般选择
RoleVoter就够用了，它会根据我们配置文件中的设置来决定是否允许某一个用户访问制定的Web资源。<br />
<br />
而DaoAuthenticationProvider也是不直接操作数据库的，它把任务委托给了UserDetailService，如下图：<br />
<img alt="005.PNG" src="../../images/blogjava_net/youxia/SpringSide14/005.PNG" width="482" border="0" height="600" /><br />
<br />
而
我们要做的，就是实现这个UserDetailService。图画得不好，大家不要见笑，但是说了这么多总算是引出了我们开发中的关键，那就是我们要实
现自己的UserDetailService，它就是连接我们的数据库和Acegi的桥梁。UserDetailService的要求也很简单，只需要一
个返回org.springframework.security.userdetails.User对象的
loadUserByUsername(String
userName)方法。因此，怎么设计数据库都可以，不管我们是用一个表还是两个表还是三个表，也不管我们是用户-授权，还是用户-角色-授权，还是用
户-用户组-角色-授权，这些具体的东西Acegi统统不关心，它只关心返回的那个User对象，至于怎么从数据库中读取数据，那就是我们自己的事了。<br />
<br />
反
过来再看看上面的过程，我们发现，即使我们要做的只是实现自己的UserDetailService类，但是我们不得不在Spring中配置那一大堆的
Bean，包括几个Filter，几个Manager，几个Provider和Voter，而这些配置往往都是重复的无谓的。好在Acegi
2.0也认识到了这个问题，所以，它设计了一个&lt;http&gt;标签，让Acegi的配置得到了简化。下面是SpringSide
3中的配置的截图，大家可以看看：<br />
<img alt="006.PNG" src="../../images/blogjava_net/youxia/SpringSide14/006.PNG" width="881" border="0" height="534" /><br />
<br />
下图是官方文章中的传统Filter设置和&lt;http&gt;元素之间的对应关系：<br />
<img alt="007.PNG" src="../../images/blogjava_net/youxia/SpringSide14/007.PNG" width="1000" border="0" height="474" /><br />
<br />
下
面的代码是SpringSide 3中实现UserDetailService的范例，在SpringSide
3的范例中，白衣使用了三个表User、Role、Authority。但是Acegi不关心你用了几个表，它只关心UserDetails对象。而决定
用户能否访问指定Web资源的，是RoleVoter类，无需任何修改它可以工作得很好，唯一的缺点是它只认ROLE_前缀，所以搞得白衣的
Authority看起来都象角色，不伦不类。</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;">
<span style="color: #0000ff;">package</span>
<span style="color: #000000;">&nbsp;personal.youxia.service.security;<br />
<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;java.util.ArrayList;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;java.util.List;<br />
<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;org.springframework.beans.factory.annotation.Required;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;org.springframework.dao.DataAccessException;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;org.springframework.security.GrantedAuthority;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;org.springframework.security.GrantedAuthorityImpl;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;org.springframework.security.userdetails.UserDetails;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;org.springframework.security.userdetails.UserDetailsService;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;org.springframework.security.userdetails.UsernameNotFoundException;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;personal.youxia.entity.user.Authority;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;personal.youxia.entity.user.Role;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;personal.youxia.entity.user.User;<br />
</span>
<span style="color: #0000ff;">import</span>
<span style="color: #000000;">&nbsp;personal.youxia.service.user.UserManager;<br />
<br />
</span>
<span style="color: #008000;">/**</span>
<span style="color: #008000;">
<br />
&nbsp;*&nbsp;实现SpringSecurity的UserDetailsService接口,获取用户Detail信息.<br />
&nbsp;*&nbsp;<br />
&nbsp;*&nbsp;</span>
<span style="color: #808080;">@author</span>
<span style="color: #008000;">&nbsp;calvin<br />
&nbsp;</span>
<span style="color: #008000;">*/</span>
<span style="color: #000000;">
<br />
</span>
<span style="color: #0000ff;">public</span>
&nbsp;
<span style="color: #0000ff;">class</span>
<span style="color: #000000;">&nbsp;UserDetailServiceImpl&nbsp;</span>
<span style="color: #0000ff;">implements</span>
<span style="color: #000000;">&nbsp;UserDetailsService&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">private</span>
<span style="color: #000000;">&nbsp;UserManager&nbsp;userManager;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">public</span>
<span style="color: #000000;">&nbsp;UserDetails&nbsp;loadUserByUsername(String&nbsp;userName)&nbsp;</span>
<span style="color: #0000ff;">throws</span>
<span style="color: #000000;">&nbsp;UsernameNotFoundException,&nbsp;DataAccessException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User&nbsp;user&nbsp;</span>
<span style="color: #000000;">=</span>
<span style="color: #000000;">&nbsp;userManager.getUserByLoginName(userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">if</span>
<span style="color: #000000;">&nbsp;(user&nbsp;</span>
<span style="color: #000000;">==</span>
&nbsp;
<span style="color: #0000ff;">null</span>
<span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">throw</span>
&nbsp;
<span style="color: #0000ff;">new</span>
<span style="color: #000000;">&nbsp;UsernameNotFoundException(userName&nbsp;</span>
<span style="color: #000000;">+</span>
&nbsp;
<span style="color: #000000;">"</span>
<span style="color: #000000;">&nbsp;不存在</span>
<span style="color: #000000;">"</span>
<span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span>
<span style="color: #000000;">&lt;</span>
<span style="color: #000000;">GrantedAuthority</span>
<span style="color: #000000;">&gt;</span>
<span style="color: #000000;">&nbsp;authsList&nbsp;</span>
<span style="color: #000000;">=</span>
&nbsp;
<span style="color: #0000ff;">new</span>
<span style="color: #000000;">&nbsp;ArrayList</span>
<span style="color: #000000;">&lt;</span>
<span style="color: #000000;">GrantedAuthority</span>
<span style="color: #000000;">&gt;</span>
<span style="color: #000000;">();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">for</span>
<span style="color: #000000;">&nbsp;(Role&nbsp;role&nbsp;:&nbsp;user.getRoles())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">for</span>
<span style="color: #000000;">&nbsp;(Authority&nbsp;authority&nbsp;:&nbsp;role.getAuths())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;authsList.add(</span>
<span style="color: #0000ff;">new</span>
<span style="color: #000000;">&nbsp;GrantedAuthorityImpl(authority.getName()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #008000;">//</span>
<span style="color: #008000;">&nbsp;目前在MultiDatabaseExample的User类中没有enabled,&nbsp;accountNonExpired,credentialsNonExpired,&nbsp;accountNonLocked等属性<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #008000;">//</span>
<span style="color: #008000;">&nbsp;暂时全部设为true,在需要时才添加这些属性.</span>
<span style="color: #008000;">
<br />
</span>
<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.springframework.security.userdetails.User&nbsp;userdetail&nbsp;</span>
<span style="color: #000000;">=</span>
&nbsp;
<span style="color: #0000ff;">new</span>
<span style="color: #000000;">&nbsp;org.springframework.security.userdetails.User(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.getLoginName(),&nbsp;user.getPassword(),&nbsp;</span>
<span style="color: #0000ff;">true</span>
<span style="color: #000000;">,&nbsp;</span>
<span style="color: #0000ff;">true</span>
<span style="color: #000000;">,&nbsp;</span>
<span style="color: #0000ff;">true</span>
<span style="color: #000000;">,&nbsp;</span>
<span style="color: #0000ff;">true</span>
<span style="color: #000000;">,&nbsp;authsList<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.toArray(</span>
<span style="color: #0000ff;">new</span>
<span style="color: #000000;">&nbsp;GrantedAuthority[authsList.size()]));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">return</span>
<span style="color: #000000;">&nbsp;userdetail;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Required<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">public</span>
&nbsp;
<span style="color: #0000ff;">void</span>
<span style="color: #000000;">&nbsp;setUserManager(UserManager&nbsp;userManager)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #0000ff;">this</span>
<span style="color: #000000;">.userManager&nbsp;</span>
<span style="color: #000000;">=</span>
<span style="color: #000000;">&nbsp;userManager;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span>
</div>
<br />
<br />
最
后再来说说这个命名的问题，我对Authentication和Authority这两个单词比较反感，两个原因，一是因为它们太生僻了，二是因为它们长
得太像了，明明一个是认证，一个是授权，意思相差很远，外貌却如此相似，确实很烦人。如果让我来选择，我喜欢Privilege这个单词，在我刚使用
MySQL的时候就跟它很熟了，所以在我的项目中，我可能会用Privilege来代替Authority。如果我们只使用User-Role两级关系，
使用RoleVoter默认的ROLE_前缀当然没有关系，如果是像白衣这样是用三层关系，最好还是把这个前缀改一改，以免混淆。</div>
<script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('AjaxHolder$scriptmanager1', document.getElementById('Form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tAjaxHolder$UpdatePanel1'], [], [], 90);
//]]&gt;
</script>
<img src ="http://www.blogjava.net/sealyu/aggbug/308743.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-01-08 17:01 <a href="http://www.blogjava.net/sealyu/archive/2010/01/08/308743.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring Security 2 配置精讲(转)</title><link>http://www.blogjava.net/sealyu/archive/2010/01/08/308741.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 08 Jan 2010 08:58:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/01/08/308741.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/308741.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/01/08/308741.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/308741.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/308741.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 论坛上看了不少Spring Security的相关文章。这些文章基本上都还是基于Acegi-1.X的配置方式，而主要的配置示例也来自于SpringSide的贡献。众所周知，SpringSecurity针对Acegi的一个重大的改进就在于其配置方式大大简化了。所以如果配置还是基于Acegi-1.X这样比较繁琐的配置方式的话，那么我们还不如直接使用Acegi而不要去升级了。所以在这...&nbsp;&nbsp;<a href='http://www.blogjava.net/sealyu/archive/2010/01/08/308741.html'>阅读全文</a><img src ="http://www.blogjava.net/sealyu/aggbug/308741.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-01-08 16:58 <a href="http://www.blogjava.net/sealyu/archive/2010/01/08/308741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Spring MVC I18N 的配置问题(转)</title><link>http://www.blogjava.net/sealyu/archive/2010/01/07/308556.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 07 Jan 2010 07:18:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/01/07/308556.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/308556.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/01/07/308556.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/308556.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/308556.html</trackback:ping><description><![CDATA[<font color="LimeGreen"><strong>《Spring MVC I18N 配置说明》</strong></font> <br />
<br />
<font color="Orange">一、基于浏览器语言的国际化配置</font> <br />
<br />
使用Spring的MVC，并且配置中有配置Resource文件 <br />
<br />
<table bgcolor="#999999" border="0" cellpadding="3" cellspacing="1">
    <tbody>
        <tr>
            <td align="left" bgcolor="#dddddd" valign="top" width="1"><font color="#555555">
            <pre>1<br />
            2<br />
            3<br />
            4<br />
            5<br />
            </pre>
            </font></td>
            <td align="left" bgcolor="#ffffff" valign="top">
            <pre>	&lt;!-- 资源文件绑定器 --&gt;<br />
            &lt;bean id=<font color="red">"messageSource"</font> class=<font color="red">"org.springframework.context.support.ResourceBundleMessageSource"</font>&gt;<br />
            &lt;property name=<font color="red">"basename"</font> value=<font color="red">"message-info"</font> /&gt;<br />
            &lt;property name=<font color="red">"useCodeAsDefaultMessage"</font> value=<font color="red">"true"</font> /&gt;<br />
            &lt;/bean&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
其中，<font color="Red">message-info</font>是你的properties文件的通用名。如：我的配置文件叫message-info.properties,message-info_zh_CN.properties等等 <br />
<br />
只要有了这个配置，然后配置JSP渲染器为JSTL支持的，那么在你的JSP文件中使用fmt标记就可以实现客户浏览器语言国际化了。 <br />
如：&lt;fmt:message key="info.login.title" /&gt; <br />
其中的info.login.title和你的资源文件对应 <br />
<br />
<font color="Orange">二、基于动态加载的国际化配置</font> <br />
<br />
<font color="Teal">1、基于请求的国际化配置</font> <br />
<br />
基于请求的国际化配置是指，在当前请求内，国际化配置生效，否则自动以浏览器为主。 <br />
<br />
配置方式如下： <br />
首先配置拦截器 <br />
<table bgcolor="#999999" border="0" cellpadding="3" cellspacing="1">
    <tbody>
        <tr>
            <td align="left" bgcolor="#dddddd" valign="top" width="1"><font color="#555555">
            <pre>1<br />
            2<br />
            3<br />
            </pre>
            </font></td>
            <td align="left" bgcolor="#ffffff" valign="top">
            <pre>	&lt;!-- 国际化操作 拦截器 必需配置，可以和其它国际化方式通用 --&gt;<br />
            &lt;bean id=<font color="red">"<font color="Red">localeChangeInterceptor</font>"</font> class=<font color="red">"org.springframework.web.servlet.i18n.LocaleChangeInterceptor"</font> /&gt;<br />
            &nbsp;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
这个配置呢，是不论请求级别的国际化，还是Cookie级别的国际化，再或者Session级别的国际化，都<font color="Red">必需有配置这个拦截器</font>，否则会不能使用。 <br />
<br />
配好上面的拦截器之后，就将拦截器注入到你的UrlHandlerMapping中，例如: <br />
<table bgcolor="#999999" border="0" cellpadding="3" cellspacing="1">
    <tbody>
        <tr>
            <td align="left" bgcolor="#dddddd" valign="top" width="1"><font color="#555555">
            <pre>1<br />
            2<br />
            3<br />
            4<br />
            5<br />
            6<br />
            </pre>
            </font></td>
            <td align="left" bgcolor="#ffffff" valign="top">
            <pre>	&lt;bean id=<font color="red">"defaultUrlMapping"</font> class=<font color="red">"org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"</font>&gt;<br />
            &lt;property name=<font color="red">"<font color="Red">interceptors</font>"</font> ref=<font color="red">"<font color="Red">localeChangeInterceptor</font>"</font> /&gt;<br />
            &lt;property name=<font color="red">"order"</font>&gt;<br />
            &lt;value&gt;1&lt;/value&gt;<br />
            &lt;/property&gt;<br />
            &lt;/bean&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
这个时候，但凡有了符合UrlMapping的请求，就会被拦截，并且开始配置国际化参数 <br />
<table bgcolor="#999999" border="0" cellpadding="3" cellspacing="1">
    <tbody>
        <tr>
            <td align="left" bgcolor="#dddddd" valign="top" width="1"><font color="#555555">
            <pre>1<br />
            2<br />
            </pre>
            </font></td>
            <td align="left" bgcolor="#ffffff" valign="top">
            <pre>	&lt;bean id=<font color="red">"localeResolver"</font> class=<font color="red">"org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"</font>&gt;<br />
            &lt;/bean&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
默认的参数名为<font color="Red">locale</font>主意大小写。里面放的就是你的提交参数。如:en_US，zh_CN之类的，这个时候，你在页面上加一句
<table bgcolor="#999999" border="0" cellpadding="3" cellspacing="1">
    <tbody>
        <tr>
            <td align="left" bgcolor="#dddddd" valign="top" width="1"><font color="#555555">
            <pre>1<br />
            </pre>
            </font></td>
            <td align="left" bgcolor="#ffffff" valign="top">
            <pre>&lt;a href=<font color="red">"?locale=zh_CN"</font>&gt;简体中文&lt;/a&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
如果你的资源中，饱含建议中文的配置，那么就会变成你确定的简体中文拉。 <br />
<br />
<font color="Teal">2、基于Session的国际化配置</font> <br />
<br />
拦截器和基于请求的相同 <br />
<br />
Session的配置如下： <br />
<table bgcolor="#999999" border="0" cellpadding="3" cellspacing="1">
    <tbody>
        <tr>
            <td align="left" bgcolor="#dddddd" valign="top" width="1"><font color="#555555">
            <pre>1<br />
            2<br />
            3<br />
            </pre>
            </font></td>
            <td align="left" bgcolor="#ffffff" valign="top">
            <pre>	&lt;bean id=<font color="red">"<font color="Red">localeResolver</font>"</font> class=<font color="red">"org.springframework.web.servlet.i18n.SessionLocaleResolver"</font>&gt;<br />
            &lt;/bean&gt;<br />
            <br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
在你的处理的Controller中，将提交上来的locale字段信息生成真正的Locale对象，然后将对象保存在Session中，默认保存的ID是
<table bgcolor="#999999" border="0" cellpadding="3" cellspacing="1">
    <tbody>
        <tr>
            <td align="left" bgcolor="#dddddd" valign="top" width="1"><font color="#555555">
            <pre>1<br />
            </pre>
            </font></td>
            <td align="left" bgcolor="#ffffff" valign="top">
            <pre>SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
这样，当你的Session不过期，那么语言种类始终保持正确的说。我一直是这样子用的，我觉得还是Session的好，老外们用了很满意。 <br />
<br />
<font color="Teal">3、基于Cookie的国际化配置</font> <br />
<br />
这个我就不说了，反正用的不多，至少我做的项目能不用Cookie就不用Cookie，所以，基于Cookie的国际化配置我就不细说了，如果想知道怎么配置，那么下载一个Spring，其中的例子程序就是用Cookie配置的，你自己读代码就OK了。 <br />
<br />
<font color="Orange">三、注意事项</font> <br />
<br />
如果不用默认的浏览器语言国际化方式，那么拦截器一定要配置，如果你有多个UrlMapping，那么就每个都配上拦截器。 <br />
至于配置的LocaleResolver的名字，一定要用上面的配置中的名字<font color="Red">localeResolver</font>当然了，这个是默认的名字来的，自己设置成别的也可以，但是就是麻烦，反正我用默认的就感觉不错。 <br />
<br />
好啦，说了这么多，应该大家会配置了吧，表说你不知道Locale对象怎么生成哦～
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/308556.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-01-07 15:18 <a href="http://www.blogjava.net/sealyu/archive/2010/01/07/308556.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Simple web application with Spring Security</title><link>http://www.blogjava.net/sealyu/archive/2009/12/24/307183.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 24 Dec 2009 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/24/307183.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/307183.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/24/307183.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/307183.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/307183.html</trackback:ping><description><![CDATA[<div>
<div>
<p>After given a
demo to the customer, they wished to modify the specification so that
after login, the page always shows the name of the user logged in. Also
they would prefer if the navigation was customized to the type of user
that has logged in. e.g. standard users with ROLE_USER should not see
the link to the admin page on the common navigation.</p>
<p>The specification has been updated as follows:</p>
<blockquote>
<p>User Story 7: Create common navigation that all secure pages will contain.<br />
Note: There will be links to home, admin pages and a logout link.<br />
Note: only admin users should see the admin link on the common navigation.</p>
</blockquote>
<blockquote>
<p>User Story 10: A common information bar should exist on all secure pages that displays whether the user is logged in or not.</p>
</blockquote>
<h3>The solution</h3>
<p>To customize the common navigation per user role type and display
the logged in username, we are going to use spring security&#8217;s tag libs,
specifically the <strong>authorize</strong> and <strong>authentication</strong> tags.</p>
<h3>Add spring security taglibs as dependency</h3>
<p>Add <strong>spring-security-taglibs-2.0.4.jar</strong> to our WAR projects lib folder.</p>
<h3>The implementation</h3>
<p>The first step is to update our acceptance tests that verify behavior on the common navigation:</p>
<pre>@Test<br />
public void shouldNotBeAbleToSeeAdminLinkOnCommonNavigationWhenNotLoggedInAsStandardUser() {<br />
<br />
driver.get("http://localhost:8080/springsecuritywebapp/home.htm");<br />
login(driver);<br />
<br />
// verify<br />
assertThat(driver.getTitle(),<br />
is("Home: Spring Security Web Application"));<br />
<br />
try {<br />
driver.findElement(By.linkText("Admin"));<br />
fail("should not be able to see a link to admin page when logged in as standard user");<br />
} catch (final NoSuchElementException e) {<br />
assertNotNull(e);<br />
}<br />
<br />
}<br />
<br />
@Test<br />
public void shouldBeAbleToViewUsernameOfUserOnAdminPageWhenSuccessfullyAuthenticated() {<br />
loginAsUser(driver, withAdminRole());<br />
<br />
// state verification<br />
assertThat(driver.getTitle(),<br />
is("Admin: Spring Security Web Application"));<br />
assertThat(driver.findElement(By.id("loginstatus")).getText(),<br />
containsString("Logged in as: admin"));<br />
<br />
}<br />
<br />
@Test<br />
public void shouldBeAbleToViewUsernameOfUserOnHomePageWhenSuccessfullyAuthenticated() {<br />
login(driver);<br />
<br />
// state verification<br />
assertThat(driver.getTitle(),<br />
is("Home: Spring Security Web Application"));<br />
assertThat(driver.findElement(By.id("loginstatus")).getText(),<br />
containsString("Logged in as: username"));<br />
<br />
}<br />
</pre>
<p>Next step is to create a userinfobar.jsp file that will be included in each secure page:</p>
<pre>&lt;span id="loginstatus"&gt;Logged in as: &lt;security:authentication property="principal.username"/&gt;<br />
&lt;/span&gt;<br />
<br />
&lt;br /&gt;</pre>
<p>Things to note:</p>
<ol>
    <li>we are using the authentication tag from spring security&#8217;s tag libs
    (which will be included at top of each jsp that includes this file)</li>
</ol>
<p>Next this should be included in the home.jsp and admin.jsp pages. Here is home.jsp:</p>
<pre>&lt;%@ page session="true"%&gt;<br />
&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;<br />
&lt;%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %&gt;<br />
&lt;%@ taglib prefix='security' uri='http://www.springframework.org/security/tags' %&gt;<br />
&lt;html&gt;<br />
<br />
&lt;head&gt;<br />
&lt;title&gt;Home: Spring Security Web Application&lt;/title&gt;<br />
<br />
&lt;/head&gt;<br />
<br />
&lt;body&gt;<br />
<br />
&lt;%@ include file="/WEB-INF/jsp/navigation.jsp" %&gt;<br />
&lt;%@ include file="/WEB-INF/jsp/userinfobar.jsp"%&gt;<br />
<br />
home page: only logged in users should see this page.<br />
<br />
&lt;/body&gt;<br />
<br />
&lt;/html&gt;</pre>
<p>Things to note:</p>
<ol>
    <li>The spring security tablib is included at top of page</li>
    <li>The userinfobar.jsp file is included so will display username of logged in users.</li>
</ol>
<p>Build, deploy and run all acceptance tests.</p>
<h3>Getting the code</h3>
<p>The code for this part is tagged and available for viewing online at: <a href="http://code.google.com/p/spring-security-series/source/browse/#svn/tags/SpringSecuritySeriesWAR-Part8">http://code.google.com/p/spring-security-series/source/browse/#svn/tags/SpringSecuritySeriesWAR-Part8<br />
</a></p>
<p>SVN Url: https://spring-security-series.googlecode.com/svn/tags/SpringSecuritySeriesWAR-Part8</p>
</div>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/307183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-24 17:01 <a href="http://www.blogjava.net/sealyu/archive/2009/12/24/307183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Springing Around with ExtJS</title><link>http://www.blogjava.net/sealyu/archive/2009/11/19/302934.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 19 Nov 2009 07:21:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/11/19/302934.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/302934.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/11/19/302934.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/302934.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/302934.html</trackback:ping><description><![CDATA[<p>While playing around more with ExtJS and Spring, I ran in to one of
my favorite annoyances — setting up a new project. I can create a new
webapp in IDEA (or Eclipse) and add some dependencies, but it is still
pretty empty. Maven can go a bit farther, but I don&#8217;t like how it
handles transitive dependencies. None of these will really give me a
good starting point out of the box without either copying a bunch of
stuff over from other projects or writing a lot from scratch.</p>
<p>To finally scratch that itch, and move further along the Spring
&amp; ExtJS path, I turned my demo project into a basic template. The
zip archive that you can grab at the bottom is a fully-configured
Spring web application, including Tiles, Spring Security, Spring MVC,
custom JSON view, Transactions and a Datasource.</p>
<p>To get started, grab the file, extract it to some directory. Open a
shell, navigate to the Template directory and run the ant command <code>ant dist</code>.
This will compile the whole project and create a Template.war file in
the dist directory. Note, I use Java 6 for all development, so if
you&#8217;re not at least at Java 5, you won&#8217;t be able to use this.</p>
<p>Before you drop the war file into Tomcat&#8217;s webapp directory, you&#8217;ll need to setup the database. First, copy the jar files in <code>lib/tomcat</code> into Tomcat&#8217;s <code>lib</code>
directory. This is the MySQL JDBC driver and the JTA files for
transactions. You&#8217;ll then need to create a new database on your local
MySQL instance called <code>tomcat</code>. For simplicity in
development, create a user with all rights to the tomcat database.
Here&#8217;s the code to run from a MySQL shell:</p>
<p><code>create database tomcat;<br />
use tomcat;<br />
grant all on tomcat.* to tomcat@localhost identified by 'apache';<br />
</code><br />
Your other option is to just put in your MySQL root username and
password. To do that, or change the connection string completely, edit <code>context.xml</code> under <code>web/META-INF</code>. If you don&#8217;t use MySQL, you will have to edit this file and also put the correct driver in Tomcat&#8217;s <code>lib</code> directory.</p>
<p>Once you have the database setup, drop the Template.war file into your Tomcat <code>webapps</code> directory and startup Tomcat. Assuming Tomcat is configured to listen on port 8080, you can open the sample by browsing to <code>http://localhost:8080/Template</code></p>
<p>The application only has a couple pages. First, the login page:</p>
<p><a href="http://www.sporcic.org/wp-content/uploads/2009/01/sample-login.png"><img wp-image-107="" alignnone="" title="sample-login" src="http://www.sporcic.org/wp-content/uploads/2009/01/sample-login-300x255.png" alt="Login Page" width="300" height="255" /></a></p>
<p>Spring Security is configured to route unauthenticated requests
through this page for login. You can take a look at the
applicationContext-security.xml file in WEB-INF to see how this was
done. There are two users configured for now. User <code>scott</code> with a password of <code>tiger</code> and user <code>bob</code> with a password of <code>password</code>. Yes, not very clever, but it works. Scott is in both the ROLE_ADMIN and ROLE_USER roles, while Bob is only in ROLE_USER.</p>
<p>If you login with scott, you&#8217;ll be taken to the index page, which looks like this:</p>
<p><a href="http://www.sporcic.org/wp-content/uploads/2009/01/sample-echo.png"><img wp-image-108="" alignnone="" title="sample-echo" src="http://www.sporcic.org/wp-content/uploads/2009/01/sample-echo-300x257.png" alt="Home Page" width="300" height="257" /></a></p>
<p>The index page simply contains a text box with a button. Entering a
message and pressing the button results in an Ajax call to the server
which echos the message back to the page. An HTML element in the middle
of the page is updated with the result via JavaScript.</p>
<p>You&#8217;ll notice in the footer of the page you can see the currently
logged in user on the left, and a link to log out on the right.
Clicking the log out link takes you to the logout page, which looks
like this:</p>
<p><a href="http://www.sporcic.org/wp-content/uploads/2009/01/sample-logout.png"><img wp-image-109="" alignnone="" title="sample-logout" src="http://www.sporcic.org/wp-content/uploads/2009/01/sample-logout-300x255.png" alt="Logout Page" width="300" height="255" /></a></p>
<p>Again, nothing fancy. Just a message saying you have logged out and
a link to login again. Use the login link to login as bob and try the
echo functionality again. This time you get a different result:</p>
<p><a href="http://www.sporcic.org/wp-content/uploads/2009/01/sample-badauth.png"><img wp-image-110="" alignnone="" title="sample-badauth" src="http://www.sporcic.org/wp-content/uploads/2009/01/sample-badauth-300x255.png" alt="Not an Admin" width="300" height="255" /></a></p>
<p>This demonstrates what happens with Spring Security via annotations. Here&#8217;s the echo method in the service layer:</p>
<p><a href="http://www.sporcic.org/wp-content/uploads/2009/01/echo-service.png"><img size-full="" wp-image-111="" title="echo-service" src="http://www.sporcic.org/wp-content/uploads/2009/01/echo-service.png" alt="" width="306" height="147" /></a></p>
<p>As you can see, the method is secured with an annotation indicating
the user must be in the ROLE_ADMIN role to use the method. Bob is only
in the ROLE_USER role, so the call to this service fails.</p>
<p>The application makes use of a ResourceBundleMessageSource for the
pages mapped through the htmlDispatch servlet. The login.jsp and
logout.jsp don&#8217;t go through the dispatch servlet, so they can&#8217;t use the
message bundle for the window and page title.</p>
<p>There is way too much to this simple application to cover completely
now, but I&#8217;ll give the highlights of what to go look at in the major
configuration files. Paths are relative to the project root:</p>
<ol>
    <li><code>web/WEB-INF/web.xml </code>- notice that I configure two
    dispatch servlets. One catching *.htm and one *.json. This sets things
    up to treat Ajax requests differently. Spring Security is also
    configured here.</li>
    <li><code>web/WEB-INF/applicationContext.xml</code> &#8211; typical
    application context for a Spring application. I turn on annotation
    handling with package scanning under the sample.core package. Apache
    Tiles is configured in this file, and I have also configured a
    Transaction Manager around the JDBC DataSource. You should tweak this
    based on your underlying persistence preferences. I&#8217;m a RowMapper fan,
    but you can plug in Hibernate or JPA.</li>
    <li><code>web/WEB-INF/htmlDispatch-servlet.xml </code>- the context for
    web (*.htm) requests. This sets up the ResourceBundle for messages and
    a typical ViewResolver mapping the *.htm requests to jsp files under
    web/WEB-INF/jsp. Also configures component scanning for the sample.web
    package.</li>
    <li><code>web/WEB-INF/jsonDispatch-servlet.xml</code> &#8211; the context for
    Ajax (*.json) requests. Configures component scanning for the
    sample.json package and specifies a custom Ajax ViewResolver. This will
    automagically serialize all ModelMaps returned out of the Controller
    responded to *.json requests to JSON.</li>
    <li><code>web/WEB-INF/applicationContext-security.xml</code> &#8211; so
    simple to look at, yet so painful to figure out. This is the Spring
    Security configuration file. Although it looks deceptively simple, it
    was a beating to figure it all out. It enables annotation-driven
    security, which you saw on the EchoService above. It also sets up the
    form login and locks down all the pages. Note I leave the /resource
    directory open. This is where I put all my javascript, stylesheets and
    images. If you want to secure those resources, you&#8217;ll need to get more
    specific on the intercept-urls.<br />
    Users are declared at the bottom. Passwords are clear text, which is
    fine for a trivial demo, but you would want to replace this with
    something more industrial-strength in a real application.</li>
    <li><code>web/WEB-INF/tiles-config.xml</code> &#8211; the Apache Tiles configuration. I only setup one definition here to keep it simple.</li>
    <li><code>web/WEB-INF/jsp/layouts/baseLayout.jsp</code> &#8211; the base
    layout used for tiles. I&#8217;m only inserting content at two locations in
    the template. In the HTML head section, I allow for an optional insert
    of headerContent. I use this to include JavaScripts specific to a page.
    The other content is within the center div called mainContent.<br />
    Note that the body is pretty empty and that the divs all have the
    x-hidden class. This means they are not normally visible. I use an
    ExtJS Viewport for layout, which uses the contents of these divs.</li>
    <li><code>web/resources/javascripts/layout.js</code> &#8211; this JavaScript file was included in the baseLayout.jsp above. It creates the Viewport using a border layout.</li>
    <li><code>web/resources/javascripts/index.js</code> &#8211; the JavaScript
    file included for the index.jsp page. It decorates the plain HTML
    inputs on the page to do the cool Ajax stuff. Note towards the bottom
    how I use ExtJS to set focus on a form field and to bind the ENTER key
    to the submit button.</li>
    <li><code>web/resources/javascripts/login.js</code> &#8211; the JavaScript
    file use for the login.jsp page. If you look at login.jsp, you&#8217;ll
    notice there is no form. Everything is created by ExtJS from the
    login.js file, including the cool box effect. One trick is that Spring
    Security wants a normal form POST for the login form. I override the
    form to do a standard submit instead of an Ajax submit and set the form
    action explicitly.</li>
</ol>
<p>This covers the major features. I&#8217;ll be using this as a base for
other projects and will be expanding out my ExtJS demo. Since there are
pretty much zero examples out there of tying together this stack, I
hope this can be of use to some folks.</p>
<p><a href="http://www.sporcic.org/wp-content/uploads/2009/01/template.zip">Project Template</a></p>
<ul>
    <li>2/28/2009 &#8211; upgrade to ExtJS 2.2.1, fixed compile issue on Linux, added in iBatis for ORM</li>
    <li>3/26/2009 &#8211; I&#8217;ve setup a dev server with the latest version of the template at <a href="http://www.codezombie.com/template">CodeZombie.com</a>
    so you can check it out without having to install. User scott/tiger for
    credentials. I&#8217;ll be updating this install with a more feature-rich
    demo application shortly</li>
    <li>3/28/2009 &#8211; changed around the project structure and build.xml to
    use Ivy for dependency management. Check the README.txt file in the
    root directory for the details.</li>
    <li>4/28/2009 &#8211; updated the included JSON encoder to be able to return JSON arrays by using the key <code>_root</code>
    in the model map. If this key is found, its content will be used as the
    root element of the return JSON instead of encoding the whole map. Also
    removed the unicode characters from StringScrubber to make the compiler
    on Linux happy.</li>
</ul>
<p>p.s. this pulls together a lot of jars and pieces from different
folks to build the demo. If you use this for more than playing around,
you need to make sure you respect whatever licenses the authors have in
place.</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/302934.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-11-19 15:21 <a href="http://www.blogjava.net/sealyu/archive/2009/11/19/302934.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSGi原理与最佳实践（精选版）（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/10/30/300299.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 30 Oct 2009 01:12:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/30/300299.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/300299.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/30/300299.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/300299.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/300299.html</trackback:ping><description><![CDATA[<p>
作者
<strong><a href="http://www.infoq.com/cn/bycategory.action?authorName=%E6%9E%97%E6%98%8A" class="editorlink">
林昊</a>
</strong>
发布于
2009年10月16日 上午5时47分
</p>
<dl><dt>社区</dt><dd><a href="http://www.infoq.com/cn/architecture" name="architecture" id="1,390" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">Architecture</a>,</dd><dd><a href="http://www.infoq.com/cn/java" name="java" id="739" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">Java</a></dd><dt>主题</dt><dd><a href="http://www.infoq.com/cn/opensource" name="opensource" id="833" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">开放源代码</a>,</dd><dd><a href="http://www.infoq.com/cn/ApplicationServers" name="ApplicationServers" id="834" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">应用服务器</a>,</dd><dd><a href="http://www.infoq.com/cn/platforms" name="platforms" id="3,697" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">平台</a>,</dd><dd><a href="http://www.infoq.com/cn/enterprise-architecture" name="enterprise-architecture" id="786" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">企业架构</a></dd><dt>标签</dt><dd><a href="http://www.infoq.com/cn/osgi" name="osgi" id="1,049" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">OSGi</a>,</dd><dd><a href="http://www.infoq.com/cn/book" name="book" id="1,292" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">图书</a></dd></dl>
<p>
<img src="http://www.infoq.com/resource/minibooks/osgi-best-practice/zh/cover/osgi-big-cover.jpg" alt="" /><br />
</p>
<p>OSGi
在Java业界正是风生水起。几乎所有的JEE Server，比如IBM Websphere、Oracle Weblogic和Sun
glassfish，都采用了OSGi作为基础平台，甚至推崇实用主义的SpringSource也挟Spring
DM之势，推出了基于OSGi的应用服务器——Spring DM
Server。山雨欲来风满楼，乘着这股东风，在BlueDavy发布了《OSGi实战》以及《OSGi进阶》之后，国内第一本OSGi的专业书——
《OSGi原理与最佳实践》刚刚面世。InfoQ中文站精选了其中的两章，特做成迷你书，供各位读者免费下载。</p>
<h2>免费下载迷你书</h2>
<p>如果你喜欢本书，请通过<a href="http://www.china-pub.com/195813">购买原版《OSGi原理与最佳实践》</a>支持出版商和InfoQ中文站。</p>
<p><strong>点击这里：
<span id="beforeLogin" style="display: inline;">
<a href="javascript:void(0)" onclick="showLoginWindow(this,ALIGN_RIGHT,new Function('document.getElementById(" beforelogin=""  ;document.getelementbyid(="" afterlogin=""  ).style. display="'none"  ))="">
免费下载这本书（PDF）</a>
</span>
<span id="afterLogin" style="display: none;">
<a href="http://www.infoq.com/resource/minibooks/osgi-best-practice/zh/pdf/OSGi-best-practice-minibook-by-InfoQ.pdf">免费下载这本书（PDF）</a>
</span>
</strong>。</p>
<h2>迷你书目录</h2>
<p><a href="http://www.china-pub.com/195813" target="_blank">《OSGi原理与最佳实践》原书详细信息</a></p>
<p>前言<br />
目录<br />
<br />
第1章 OSGi框架简介<br />
<br />
1.1 Equinox<br />
<br />
1.1.1 简介<br />
1.1.2 环境搭建<br />
1.1.3 HelloWorld<br />
1.1.4 开发传统类型的应用<br />
1.1.5 从外部启动Equinox<br />
<br />
1.2 Felix<br />
<br />
1.2.1 简介 <br />
1.2.2 环境搭建<br />
1.2.3 应用部署<br />
1.2.4 在Eclipse中调试Felix<br />
<br />
1.3 SpringDM<br />
<br />
1.3.1 简介<br />
1.3.2 环境搭建<br />
1.3.3 HelloWorld<br />
1.3.4 Web版HelloWorld<br />
<br />
第2章 基于SpringDM实现Petstore<br />
<br />
2.1 即插即用的Petstore<br />
<br />
2.2 新一代Petstore的实现<br />
<br />
2.2.1 环境准备<br />
2.2.2 Utils模块<br />
2.2.3 Bootstrap模块<br />
2.2.4 ProductDal模块<br />
2.2.5 ShoppingCartDal模块<br />
2.2.6 ProductList模块<br />
2.2.7 ShoppingCast模块<br />
2.2.8 ProductManagement模块<br />
<br />
2.3 部署<br />
<br />
2.4 Petstore的扩展</p>
<h2>BlueDavy《OSGi原理与最佳实践》采访</h2>
<p>InfoQ中文站就这次出版邀请BlueDavy对OSGi的近况、在具体项目上应用OSGi应该注意的问题和解决方法，以及如何在OSGi开发过程中结合使用敏捷实践的问题进行了一番访谈。</p>
<p><strong>InfoQ：自从你上一次发布&lt;OSGi进阶&gt;后，OSGi联盟最近有什么新进展？OSGi社区发展如何？</strong></p>
<p><strong>BlueDavy</strong>：OSGi联盟目前正在制定4.2的规范，并已发布公开草稿版本。在草稿版本中，我很欣慰的看到了OSGi联盟对
OSGi所做出的众多改进，包括了OSGi使用者们期待已久的对于Declarative
Services的细节改进，并将其版本定为1.1，目前Equinox也已推出1.1版本Declarative
Service的实现；除了对DS的改进外，在Core部分也可以看到提出了Framework
Lunch这样的新规范部分，这对于按照标准使用OSGi和嵌入OSGi至其他容器提供了很大的帮助。除了以上这些外，还有其他很多的改进，在《OSGi
原理与最佳实践》一书中对OSGi R4.2的公众草稿版做了更多详细的分析。</p>
<p>但由于公布的公众草稿版本并不涉及企业应用领域，也就是EEG小组的工作，因此尽管大家期待的RFC 119: Distributed
OSGi以及RFC 66: OSGi web container出现在了Early
Draft中，但并没有出现在公众草稿版中，这两个最受大家关注的规范内容应该会被列入EEG出版的规范中。</p>
<p>对于社区这一块，OSGi尽管已经发展这么多年了，到目前为止确实仍然没有非常成熟的社区，但其相关的maillist，例如equinox
maillist、OSGi-Dev maillist以及Spring-DM
maillist都相当的活跃，业界的OSGi的使用者们根据自己的经验提出了不少OSGi的最佳实践，其中Bea的最佳实践总结以及Peter和BJ
Hargrave在2007 JavaOne做的OSGi最佳实践总结给大家提供了很大的帮助。</p>
<p><strong>InfoQ：相信很多人都想在真实项目中使用OSGi。请问如果要基于OSGi开发新系统时需要注意什么问题？如何设计系统的架构才能充分利用OSGi的好处？</strong></p>
<p><strong>BlueDavy</strong>：基于OSGi开发新系统时最值得注意的问题就是如何合理地划分模块的粒度，以及遵循OSGi框架的实现方式来构建真正的模块化、动态化的系统。</p>
<p>由于OSGi的强项在于模块化以及动态化，如果想在系统中充分发挥这两个优势，一方面是要让系统真正的模块化，把握好每个模块需要对外提供的功能，充分合理地使用OSGi提供的模块化交互的方式，例如import-package以及OSGi。</p>
<p>Service另一方面则是要让系统真正的动态化，这包括了基于OSGi框架支持的Bundle生命周期管理以及服务组件生命周期管理合理构建动态
化的模块，同时也需要合理处理动态化时所带来的影响，例如引用的服务的注销、对象状态的保留与恢复等。在《OSGi原理与最佳实践》一书中提供了一些构建
模块化和动态化系统的实践建议以及为什么要如此做的分析。</p>
<p><strong>InfoQ：我们也看到在现实中存在着大量的遗留项目。那么，对于把传统的遗留系统改造成基于OSGi的架构，一般需要注意什么问题？</strong></p>
<p><strong>BlueDavy</strong>：突出的问题一般是模块ClassLoader隔离后带来的问题，对于OSGi的入门者来
说，ClassNotFoundException或者ClassCastException这类异常会成为常见的现象，这就要求使用者能够对
ClassLoader以及OSGi模块隔离和交互这两方面的知识有充分的掌握，就如BEA的microServices的开发者们总结的一样：当你不使
用OSGi来构建模块化系统时，你根本就不知道什么是真正的模块化系统，他们在移植原有的BEA的产品到OSGi上时花了一年多的时间，这也意味着要将传
统系统改造为OSGi架构确实有不小的难度，无论是OSGi知识方面的学习还是设计思想方面的转变。</p>
<p><strong>InfoQ：Oracle收购了Sun，这两家公司势必要在很多方面整合，比如双方对OSGi的态度。我们知道，虽然有很多JEE
Server都选择架构在OSGi之上，但是Oracle Fushion
11G里面却没有采用OSGi，请问你认为Oracle收购Sun对OSGi会产生什么影响？</strong></p>
<p><strong>BlueDavy</strong>：从我2005年接触OSGi而言，对OSGi的前景一直非常的看好，也许在2005、2006年时OSGi的前景
看似一片迷茫，但进入2008、2009后，无论从Java主流应用服务器都基于OSGi来看，还是从Java将从语言级提供对模块化的支持来
看，OSGi已经逐步的得到认可并成为事实性标准。</p>
<p>Oracle在很久之前就开始关注OSGi，并且Oracle也是OSGi联盟的成员之一，尽管Oracle Fushion
11G没有采用OSGi，但我认为这并不表示Oracle否定OSGi，或者不愿意采用OSGi，也许Oracle只是认为目前采用OSGi会对其原有积
累的技术产生不小的冲击，毕竟BEA为了移植到OSGi花了一年多的时间，从另外的角度来看，提供模块化的支持已经成为Java语言发展的必然，OSGi
是目前仅有的已投入实际商业产品使用的模块化规范，另一方面从OSGi对JSR277产生的影响来看，可见OSGi在Java模块化规范方面的领先地位，
因此也许不久后我们就能看到Oracle对OSGi的态度，相信很大概会是好消息。</p>
<p><strong>InfoQ：应用系统开发引入OSGi之后，又如何应用TDD、自动构建等敏捷实践？</strong></p>
<p><strong>BlueDavy</strong>：OSGi Service为POJO方式，再加上OSGi和Spring一样支持方法方式的依赖注入，因此对于依赖关系不复杂的OSGi Service的TDD和自动构建没有问题。</p>
<p>对于依赖状况复杂的而言，在Spring中多数仍然会采用配置文件编写依赖注入关系，启动Spring容器，获取相应的bean进行测试的方式。在
这种情况下，目前OSGi容器的支持则不是很好，较Spring容器的单元测试而言复杂很多。一方面是由于OSGi采用的为Bundle部署机制，这就要
求在测试时将所有需要依赖的Bundle部署至OSGi容器中，在目前的情况下这需要通过编写程序来安装。这个状况等到OBR进入使用阶段后会好很多，原
因在于通 过OBR可以很容易的自动安装所需依赖的Bundle；另外一方面则是由于OSGi并不直接提供在外部获取OSGi
Service的方法，就像Spring可以通过ApplicationContext来获取Bean。但是，也不是没有办法，如何在外部获取OSGi
Service的方法在《OSGi原理与最佳实践》一书中有进行讲解。除了书中讲解的方法外，在OSGi R4.2中新提供的Framework
launch也将有助于在OSGi容器外部获取OSGi Service。</p>
<p>鉴于上面的两个原因，在目前的情况下只能自行实现一个单元测试框架，OSGi China User Group最近会公布一个OSGi单元测试框架以方便大家对OSGi程序进行TDD和自动构建。</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/300299.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-30 09:12 <a href="http://www.blogjava.net/sealyu/archive/2009/10/30/300299.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenSessionInViewFilter 的两个异常:Illegal attempt to associate a collection with two open sessions; Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL)</title><link>http://www.blogjava.net/sealyu/archive/2009/10/29/300275.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 29 Oct 2009 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/29/300275.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/300275.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/29/300275.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/300275.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/300275.html</trackback:ping><description><![CDATA[<p>使用 Spring 整合 Hibernate, 在懒加载的情况下, 有时候需要在 JSP/View 层显示数据, 这时候就要用到Spring内置的: OpenSessionInViewFilter, 一般来说配置如下(web.xml):</p>
<div>
<pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4;"><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter</span><span style="color: #0000ff;">&gt;</span><br />
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter-name</span><span style="color: #0000ff;">&gt;</span>hibernateFilter<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">filter-name</span><span style="color: #0000ff;">&gt;</span><span style="color: #0000ff;"><br />
&lt;</span><span style="color: #800000;">filter-class</span><span style="color: #0000ff;">&gt;</span><br />
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter<br />
<span style="color: #0000ff;">    &lt;/</span><span style="color: #800000;">filter-class</span><span style="color: #0000ff;">&gt;</span><span style="color: #0000ff;"><br />
&lt;</span><span style="color: #800000;">init-param</span><span style="color: #0000ff;">&gt;</span><span style="color: #0000ff;"><br />
&lt;</span><span style="color: #800000;">param-name</span><span style="color: #0000ff;">&gt;</span>singleSession<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">param-name</span><span style="color: #0000ff;">&gt;<br />
</span><span style="color: #0000ff;">        &lt;</span><span style="color: #800000;">param-value</span><span style="color: #0000ff;">&gt;</span>true<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">param-value</span><span style="color: #0000ff;">&gt;</span><span style="color: #0000ff;"><br />
&lt;/</span><span style="color: #800000;">init-param</span><span style="color: #0000ff;">&gt;</span><span style="color: #008000;">&lt;!-- 和 spring 中的sesssionfactory ID 一致 --&gt;<br />
</span><span style="color: #0000ff;"> </span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">filter</span><span style="color: #0000ff;">&gt;</span><span style="color: #0000ff;"><br />
&lt;</span><span style="color: #800000;">filter-mapping</span><span style="color: #0000ff;">&gt;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter-name</span><span style="color: #0000ff;">&gt;</span>hibernateFilter<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">filter-name</span><span style="color: #0000ff;">&gt;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">url-pattern</span><span style="color: #0000ff;">&gt;</span>*.do<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">url-pattern</span><span style="color: #0000ff;">&gt;&lt;!</span><span style="color: #800000;">--</span> *.<span style="color: #ff0000;">jsp</span>, *.<span style="color: #ff0000;">do</span><span style="color: #ff0000;">--</span><span style="color: #0000ff;">&gt;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">filter-mapping</span><span style="color: #0000ff;">&gt;</span></pre>
</div>
<div>不过, 这时候又会导致更新数据时抛出如下异常: </div>
<blockquote>
<div>Write
operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL):
Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly'
marker from transaction definition.</div>
</blockquote>
<p>这时候再去网上找解决方案, 会有人说: 把参数 singleSession改为false, 就行了. 不过, 改完后, 估计不久就会遇到另一个郁闷的异常:</p>
<blockquote>
<p>org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions</p>
</blockquote>
<p>这下完了, 两个方案都不行, 到底怎么办? 还好, 在<a title="http://xuliangyong.javaeye.com/blog/144818" href="http://xuliangyong.javaeye.com/blog/144818">http://xuliangyong.javaeye.com/blog/144818</a>的主页上, 给了一个方案, 就是改写 OpenSessionInViewFilter 的代码, 非常感谢, 下面给出的就是最终方案:
</p>
<p>web.xml
</p>
<p>
<span style="color: #0000ff;">&lt;</span>
<span style="color: #800000;">filter-name</span>
<span style="color: #0000ff;">&gt;</span>hibernateFilter<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">filter-name</span><span style="color: #0000ff;">&gt;</span></p>
<p>
<span style="color: #0000ff;">&lt;</span>
<span style="color: #800000;">filter-class</span>
<span style="color: #0000ff;">&gt;</span> org.springframework.orm.hibernate3.support.OurOpenSessionInViewFilter <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">filter-class</span><span style="color: #0000ff;">&gt;</span></p>
<p>OurOpenSessionInViewFilter.java 代码:
</p>
<div>
<pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4;">package org.springframework.orm.hibernate3.support;<br />
<br />
<br />
<br />
import org.hibernate.*;<br />
<br />
<br />
<br />
<span style="color: #008000;">/**</span><span style="color: #008000;"> * 单session模式下, 默认会发生无法提交的错误:</span><span style="color: #008000;"> * Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.</span><span style="color: #008000;"> * 需要设置FlushMode并刷新session.</span><span style="color: #008000;"> * 参考: http://xuliangyong.javaeye.com/blog/144818</span><span style="color: #008000;"> * @author 刘长炯</span><span style="color: #008000;"> */</span><span style="color: #0000ff;">public</span><span style="color: #0000ff;">class</span> OurOpenSessionInViewFilter extends OpenSessionInViewFilter {<br />
<br />
<br />
<br />
<span style="color: #0000ff;">public</span> OurOpenSessionInViewFilter() {<br />
<br />
super.setFlushMode(FlushMode.AUTO);<br />
<br />
}<br />
<br />
<br />
<br />
<span style="color: #0000ff;">protected</span><span style="color: #0000ff;">void</span> closeSession(Session session, SessionFactory sessionFactory) {<br />
<br />
session.flush();<br />
<br />
<br />
<br />
<span style="color: #0000ff;">try</span> {<br />
<br />
session.getTransaction().commit();<br />
<br />
} <span style="color: #0000ff;">catch</span> (HibernateException e) {<br />
<br />
<span style="color: #008000;">// TODO Auto-generated catch block</span><span style="color: #008000;">//e.printStackTrace();</span><br />
<br />
}<br />
<br />
<br />
<br />
super.closeSession(session, sessionFactory); <br />
<br />
} <br />
<br />
}<br />
<br />
</pre>
</div>
<p>如果各位有更好的解决方案, 欢迎讨论哦!!!</p>
<p>题外话:</p>
<p>感觉 Spring + Hibernate 的健壮性还是不够啊! 容易抛异常, 这是事实, 也许这是开源软件的通病吧.</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/300275.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-29 22:23 <a href="http://www.blogjava.net/sealyu/archive/2009/10/29/300275.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring MVC集成Tiles时候的属性值国际化(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/10/24/299590.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Sat, 24 Oct 2009 13:16:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/24/299590.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/299590.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/24/299590.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/299590.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/299590.html</trackback:ping><description><![CDATA[<p>在使用Tiles时候, 一般会有类似以下配置:<br />
&lt;definition name="main.layout" template="/jsp/layout/baseLayout.jsp"&gt;<br />
&nbsp;&nbsp;&lt;put-attribute name="title" value="Tiles Test Title" /&gt;<br />
&nbsp;&nbsp;&lt;put-attribute name="header" value="/jsp/layout/header.jsp" /&gt;<br />
&nbsp;&nbsp;&lt;put-attribute name="body" value="/" /&gt;<br />
&nbsp;&nbsp;&lt;put-attribute name="footer" value="/jsp/layout/footer.jsp" /&gt;<br />
&nbsp;&lt;/definition&gt;</p>
默认情况下, 如果value值一"/"开头, 则认为是URL, 其他则任务是字符串, 而如果想让title实现国际化, 如何配置呢?<br />
以下有两种解决方案, 一种就是准备多个tiles配置文件,如tiles_def_zh_CN.xml, tile_def_en_US.xml, 这个优点麻烦了(个人觉得).<br />
第二种方法就是使用标签, 如果spring:message或者fmt等等, 具体如下:<br />
修改tiles配置文件中的title对应的值为资源文件中的key:<br />
&lt;put-attribute name="title" value="project.title" /&gt;<br />
然后修改页面需要渲染的地方:<br />
<span style="color: #008000;">&lt;tiles:useAttribute id="key" name="title"/&gt;<br />
&lt;title&gt;&lt;spring:message code="${key}"/&gt;&lt;/title&gt;</span><br />
或者<br />
<span style="color: #008000;">&lt;tiles:useAttribute id="key" name="title"/&gt;<br />
&lt;title&gt;&lt;fmt:message key="${key}"/&gt;title&gt;</span>
<img src ="http://www.blogjava.net/sealyu/aggbug/299590.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-24 21:16 <a href="http://www.blogjava.net/sealyu/archive/2009/10/24/299590.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 2.5 与 Tiles2.1.X系列的兼容性</title><link>http://www.blogjava.net/sealyu/archive/2009/10/23/299482.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 23 Oct 2009 07:28:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/23/299482.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/299482.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/23/299482.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/299482.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/299482.html</trackback:ping><description><![CDATA[郁闷了差不多一天，终于在一篇文章中发现，原来spring2.5系列与Tiles2.1.X系列不兼容，目前只可以使用Tiles2.0.X和Tiles1.x系列。<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/299482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-23 15:28 <a href="http://www.blogjava.net/sealyu/archive/2009/10/23/299482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Annotation-Based Validation with the Spring Bean Validation Framework</title><link>http://www.blogjava.net/sealyu/archive/2009/10/21/299262.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 21 Oct 2009 15:19:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/21/299262.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/299262.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/21/299262.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/299262.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/299262.html</trackback:ping><description><![CDATA[<h1 style="margin: 0pt 0pt 10px;">Annotation-Based Validation with the Spring Bean Validation Framework</h1>
<div>by Willie Wheeler</div>
<div>2008?7?17? ???</div>
<div id="deck">Use Java annotations to validate your Spring WebMVC form beans.</div>
<div>
<p>The Spring Bean Validation Framework, which is
part of the <a href="https://springmodules.dev.java.net/">Spring Modules</a>
project, allows you to perform validation declaratively using Java annotations.
I've always liked the declarative approach, which we saw for instance in
Commons Validator, but annotation-based validation is especially convenient.</p>
<p><a href="http://jcp.org/en/jsr/detail?id=303">JSR 303 (Bean Validation)</a>
specifies some standards around bean validation, though the Spring Bean Validation
Framework does not adopt those standards. The <a href="http://www.hibernate.org/412.html">Hibernate
Validator</a> project, on the other hand, aims to provide an implementation of
the emerging JSR 303 standard.</p>
<p>While it very well could be subpar Googling skills on my part, there
doesn't seem to be much detailed how-to information out there on
actually using the Bean Validation Framework. Hence this article.</p>
<p>I'm using Spring 2.5.x (specifically, Spring 2.5.5) and Spring
Modules 0.9. I assume that you already know Spring and Spring WebMVC in
particular.</p>
<p>If you want to download the code, you can do so here:</p>
<div><a href="http://wheelersoftware.s3.amazonaws.com/articles/spring-bean-validation-framework/contact-example.zip">contact-example.zip</a></div>
<p>You'll have to download the dependencies separately though.</p>
<h2>Dependencies</h2>
<p>Here's what you'll need (again, I'm using Spring 2.5.x and Spring Modules 0.9):</p>
<ul>
    <li><code>commons-collections.jar</code></li>
    <li><code>commons-lang.jar</code></li>
    <li><code>commons-logging.jar</code></li>
    <li><code>spring.jar</code></li>
    <li><code>spring-modules-validation.jar</code></li>
    <li><code>spring-webmvc.jar</code></li>
</ul>
<h2>Java Sources</h2>
<p>I'm going to do things a little differently than I normally do, and
start with the Java first. We're going to build a very simple "Contact
Us" form of the sort that you might use to ask a question, complain
about lousy service, or whatever. Since we're just showing how
validation works, I've left out the service and persistence tiers.
We're going to do everything with a form bean and a controller.</p>
<p>Here's the form bean:</p>
<div>
Code listing: <code>contact.UserMessage</code>
<div>
<div>
<div><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework.html#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework.html#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework.html#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework.html#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>package&nbsp;contact;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springmodules.validation.bean.conf.loader.annotation.handler.Email;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springmodules.validation.bean.conf.loader.annotation.handler.Length;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springmodules.validation.bean.conf.loader.annotation.handler.NotBlank;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>public&nbsp;final&nbsp;class&nbsp;UserMessage&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@NotBlank&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@Length(max&nbsp;=&nbsp;80)&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;name;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@NotBlank&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@Email&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@Length(max&nbsp;=&nbsp;80)&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;email;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@NotBlank&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@Length(max&nbsp;=&nbsp;4000)&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;text;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;getName()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;name;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setName(String&nbsp;name)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.name&nbsp;=&nbsp;name;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;getEmail()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;email;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setEmail(String&nbsp;email)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.email&nbsp;=&nbsp;email;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;getText()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;text;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setText(String&nbsp;text)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.text&nbsp;=&nbsp;text;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">package contact;
import org.springmodules.validation.bean.conf.loader.annotation.handler.Email;
import org.springmodules.validation.bean.conf.loader.annotation.handler.Length;
import org.springmodules.validation.bean.conf.loader.annotation.handler.NotBlank;
public final class UserMessage {
@NotBlank
@Length(max = 80)
private String name;
@NotBlank
@Email
@Length(max = 80)
private String email;
@NotBlank
@Length(max = 4000)
private String text;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
</pre>
</div>
<p>The bean itself is pretty uninteresting—I have field for the user's
name, e-mail address, and the message text. But the cool part is that
I've included annotations that specify validation constraints. It's
probably self-explanatory, but I've specified that none of the fields
is allowed to be blank, and I've also specified the maximum lengths for
each. (You can also specify minimum lengths, which one could use
instead of <code>@NotBlank</code>, but I'm using <code>@NotBlank</code> instead for a reason I'll explain in just a bit.)  Finally, I've specified that <code>email</code> needs to be a valid e-mail address.  It's that simple!</p>
<p>Here are <a href="https://springmodules.dev.java.net/docs/reference/0.8/html_single/#d0e9699">the rest of the validation rules</a> you can use.</p>
<p>Now here's the Spring MVC controller, which I've implemented as a POJO controller:</p>
<div>
Code listing: <code>contact.ContactController</code>
<div>
<div>
<div><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework.html#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework.html#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework.html#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework.html#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>package&nbsp;contact;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.beans.factory.annotation.Autowired;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.stereotype.Controller;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.ui.ModelMap;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.validation.BindingResult;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.validation.Validator;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.web.bind.annotation.ModelAttribute;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.web.bind.annotation.RequestMapping;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.web.bind.annotation.RequestMethod;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>@Controller&nbsp;&nbsp;</li>
    <li>public&nbsp;final&nbsp;class&nbsp;ContactController&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@Autowired&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;Validator&nbsp;validator;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setValidator(Validator&nbsp;validator)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.validator&nbsp;=&nbsp;validator;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@RequestMapping(value&nbsp;=&nbsp;"/form",&nbsp;method&nbsp;=&nbsp;RequestMethod.GET)&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;ModelMap&nbsp;get()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Because&nbsp;we're&nbsp;not&nbsp;specifying&nbsp;a&nbsp;logical&nbsp;view&nbsp;name,&nbsp;the&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;DispatcherServlet's&nbsp;DefaultRequestToViewNameTranslator&nbsp;kicks&nbsp;in.&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;ModelMap("userMessage",&nbsp;new&nbsp;UserMessage());&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@RequestMapping(value&nbsp;=&nbsp;"/form",&nbsp;method&nbsp;=&nbsp;RequestMethod.POST)&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;post(@ModelAttribute("userMessage")&nbsp;UserMessage&nbsp;userMsg,&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BindingResult&nbsp;result)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;validator.validate(userMsg,&nbsp;result);&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(result.hasErrors())&nbsp;{&nbsp;return&nbsp;"form";&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Use&nbsp;the&nbsp;redirect-after-post&nbsp;pattern&nbsp;to&nbsp;reduce&nbsp;double-submits.&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;"redirect:thanks";&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;@RequestMapping("/thanks")&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;thanks()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">package contact;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public final class ContactController {
@Autowired
private Validator validator;
public void setValidator(Validator validator) {
this.validator = validator;
}
@RequestMapping(value = "/form", method = RequestMethod.GET)
public ModelMap get() {
// Because we're not specifying a logical view name, the
// DispatcherServlet's DefaultRequestToViewNameTranslator kicks in.
return new ModelMap("userMessage", new UserMessage());
}
@RequestMapping(value = "/form", method = RequestMethod.POST)
public String post(@ModelAttribute("userMessage") UserMessage userMsg,
BindingResult result) {
validator.validate(userMsg, result);
if (result.hasErrors()) { return "form"; }
// Use the redirect-after-post pattern to reduce double-submits.
return "redirect:thanks";
}
@RequestMapping("/thanks")
public void thanks() {
}
}
</pre>
</div>
<p>The Bean Validation Framework includes its own <code>Validator</code> implementation, called <code>BeanValidator</code>, and I'm making that injectable here.  Also, note that we're going to autowire it in.</p>
<p>It may be that there's a standard, predefined interceptor to apply <code>BeanValidator</code> (as opposed to injecting the <code>Validator</code> into the controller), but if there is, I haven't seen it.  I'd be interested to hear if you, gentle reader, know of one.</p>
<p>The noteworthy method here is the second <code>post()</code> method, which contains the validation code.  I just call the standard <code>validate()</code> method, passing in the form bean and the <code>BindingResult</code>,
and return the current logical view name if there's an error. That way
the form shows the validation error messages, which we'll see below. If
everything passes validation, I just redirect to a "thank you" page.</p>
<p>Now let's look at how we define the validation messages that the end user sees if his form submission fails validation.</p>
</div>
<div>
<h2>Validation Messages</h2>
<div>
Code listing: <code>/WEB-INF/classes/errors.properties</code>
<div>
<div>
<div><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>UserMessage.name[not.blank]=Please&nbsp;enter&nbsp;your&nbsp;name.&nbsp;&nbsp;</li>
    <li>UserMessage.name[length]=Please&nbsp;enter&nbsp;no&nbsp;more&nbsp;than&nbsp;{2}&nbsp;characters.&nbsp;&nbsp;</li>
    <li>UserMessage.email[not.blank]=Please&nbsp;enter&nbsp;your&nbsp;e-mail&nbsp;address.&nbsp;&nbsp;</li>
    <li>UserMessage.email[email]=Please&nbsp;enter&nbsp;a&nbsp;valid&nbsp;e-mail&nbsp;address.&nbsp;&nbsp;</li>
    <li>UserMessage.email[length]=Please&nbsp;enter&nbsp;no&nbsp;more&nbsp;than&nbsp;{2}&nbsp;characters.&nbsp;&nbsp;</li>
    <li>UserMessage.text[not.blank]=Please&nbsp;enter&nbsp;a&nbsp;message.&nbsp;&nbsp;</li>
    <li>UserMessage.text[length]=Please&nbsp;enter&nbsp;no&nbsp;more&nbsp;than&nbsp;{2}&nbsp;characters.&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">UserMessage.name[not.blank]=Please enter your name.
UserMessage.name[length]=Please enter no more than {2} characters.
UserMessage.email[not.blank]=Please enter your e-mail address.
UserMessage.email[email]=Please enter a valid e-mail address.
UserMessage.email[length]=Please enter no more than {2} characters.
UserMessage.text[not.blank]=Please enter a message.
UserMessage.text[length]=Please enter no more than {2} characters.
</pre>
</div>
<p>The keys should be fairly self-explanatory given <code>UserMessage</code>
above. Each key involves a class, a field and an annotation. The values
are parametrizable messages, not unlike Commons Validator messages if
you're familiar with those. In the three <code>length</code> messages, I'm using <code>{2}</code> to indicate argument #2—viz., <code>max</code>—for the <code>length</code> validation rule.  Argument #1 happens to be <code>min</code>,
and argument #0 in general appears to be the form bean itself. I can
imagine that it would be nice to be able to use the form bean to get at
the specific submitted value so you could say things like "You entered
4012 characters, but the limit is 4000 characters." And I think there's
actually a way to do that though I don't myself know how to do it yet.
(This is another one of those areas where I'd appreciate whatever
information you may have.)</p>
<p>I mentioned above that I chose <code>@NotBlank</code> instead of <code>@Length(min = 1, max = 80)</code>.
The reason is that I wanted to use a specific error message ("Please
enter your name") if the message is blank. I could have just used
"Please enter a name between 1-80 characters" but that sounds slightly
silly compared to "Please enter your name", and since I'm a usability
guy I care about such things.</p>
<h2>The JSPs</h2>
<p>We have two JSPs: the form itself, and a basic (really basic) "thank you" page.</p>
<div>
Code listing: <code>/WEB-INF/form.jsp</code>
<div>
<div>
<div><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>&lt;%@&nbsp;taglib&nbsp;prefix="form"&nbsp;uri="http://www.springframework.org/tags/form"&nbsp;%&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;!DOCTYPE&nbsp;html&nbsp;PUBLIC&nbsp;"-//W3C//DTD&nbsp;XHTML&nbsp;1.0&nbsp;Strict//EN"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;html&nbsp;xmlns="http://www.w3.org/1999/xhtml"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;Contact&nbsp;Us&lt;/title&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;style&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.form-item&nbsp;{&nbsp;margin:&nbsp;20px&nbsp;0;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.form-label&nbsp;{&nbsp;font-weight:&nbsp;bold;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.form-error-field&nbsp;{&nbsp;background-color:&nbsp;#FFC;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.form-error-message&nbsp;{&nbsp;font-weight:&nbsp;bold;&nbsp;color:&nbsp;#900;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/style&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&lt;h1&gt;Contact&nbsp;Us&lt;/h1&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;%--&nbsp;Give&nbsp;command&nbsp;object&nbsp;a&nbsp;meaningful&nbsp;name&nbsp;instead&nbsp;of&nbsp;using&nbsp;default&nbsp;name,&nbsp;'command'&nbsp;--%&gt;&nbsp;&nbsp;</li>
    <li>&lt;form:form&nbsp;commandName="userMessage"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-item"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-label"&gt;Your&nbsp;name:&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;form:input&nbsp;path="name"&nbsp;size="40"&nbsp;cssErrorClass="form-error-field"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-error-message"&gt;&lt;form:errors&nbsp;path="name"/&gt;&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-item"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-label"&gt;Your&nbsp;e-mail&nbsp;address:&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;form:input&nbsp;path="email"&nbsp;size="40"&nbsp;cssErrorClass="form-error-field"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-error-message"&gt;&lt;form:errors&nbsp;path="email"/&gt;&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-item"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-label"&gt;Your&nbsp;message:&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;form:textarea&nbsp;path="text"&nbsp;rows="12"&nbsp;cols="60"&nbsp;cssErrorClass="form-error-field"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-error-message"&gt;&lt;form:errors&nbsp;path="text"/&gt;&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="form-item"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;input&nbsp;type="submit"&nbsp;value="Submit"&nbsp;/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;&nbsp;&nbsp;</li>
    <li>&lt;/form:form&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;&nbsp;&nbsp;</li>
    <li>&lt;/html&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="xml">&lt;%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
&lt;title&gt;Contact Us&lt;/title&gt;
&lt;style&gt;
.form-item { margin: 20px 0; }
.form-label { font-weight: bold; }
.form-error-field { background-color: #FFC; }
.form-error-message { font-weight: bold; color: #900; }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Contact Us&lt;/h1&gt;
&lt;%-- Give command object a meaningful name instead of using default name, 'command' --%&gt;
&lt;form:form commandName="userMessage"&gt;
&lt;div class="form-item"&gt;
&lt;div class="form-label"&gt;Your name:&lt;/div&gt;
&lt;form:input path="name" size="40" cssErrorClass="form-error-field"/&gt;
&lt;div class="form-error-message"&gt;&lt;form:errors path="name"/&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="form-item"&gt;
&lt;div class="form-label"&gt;Your e-mail address:&lt;/div&gt;
&lt;form:input path="email" size="40" cssErrorClass="form-error-field"/&gt;
&lt;div class="form-error-message"&gt;&lt;form:errors path="email"/&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="form-item"&gt;
&lt;div class="form-label"&gt;Your message:&lt;/div&gt;
&lt;form:textarea path="text" rows="12" cols="60" cssErrorClass="form-error-field"/&gt;
&lt;div class="form-error-message"&gt;&lt;form:errors path="text"/&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="form-item"&gt;
&lt;input type="submit" value="Submit" /&gt;
&lt;/div&gt;
&lt;/form:form&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<p>This is just a standard Spring WebMVC form, so I'll invoke my "I assume you know Spring WebMVC" assumption here.  The <code>cssErrorClass</code>
attribute is kind of fun if you don't already know about it. It
indicates the CSS class to use in the event of a validation error. You
can combine that with the <code>cssClass</code> attribute (which applies in the non-error case) though I haven't done that here.</p>
<p>Now here's the basic "thank you" page:</p>
<div>
Code listing: <code>/WEB-INF/thanks.jsp</code>
<div>
<div>
<div><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-2.html#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>&lt;!DOCTYPE&nbsp;html&nbsp;PUBLIC&nbsp;"-//W3C//DTD&nbsp;XHTML&nbsp;1.0&nbsp;Strict//EN"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&lt;html&nbsp;xmlns="http://www.w3.org/1999/xhtml"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;Thank&nbsp;You&lt;/title&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;Thank&nbsp;You&lt;/h1&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;&nbsp;&nbsp;</li>
    <li>&lt;/html&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="xml">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
&lt;title&gt;Thank You&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Thank You&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<p>(I told you it was basic...)</p>
<p>OK, now we're ready to move onto application configuration.  Almost done!</p>
<div>
<h2>Servlet and Spring Configuration</h2>
<p>Here's our completely standard <code>web.xml</code>:</p>
<div>
Code listing: <code>/WEB-INF/web.xml</code>
<div>
<div>
<div><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-3.html#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-3.html#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-3.html#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-3.html#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt;&nbsp;&nbsp;</li>
    <li>&lt;web-app&nbsp;xmlns="http://java.sun.com/xml/ns/javaee"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;xsi:schemaLocation="http://java.sun.com/xml/ns/javaee&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;version="2.5"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;contact&lt;/servlet-name&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-class&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.springframework.web.servlet.DispatcherServlet&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-class&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-mapping&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;contact&lt;/servlet-name&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;/contact/*&lt;/url-pattern&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-mapping&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&lt;/web-app&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5"&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;contact&lt;/servlet-name&gt;
&lt;servlet-class&gt;
org.springframework.web.servlet.DispatcherServlet
&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;contact&lt;/servlet-name&gt;
&lt;url-pattern&gt;/contact/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;/web-app&gt;
</pre>
</div>
<p>Though I said I'm assuming you already know Spring WebMVC, I'll just
point out that since I didn't specify a custom location for the
application context file, I have to put it at <code>/WEB-INF/contact-servlet.xml</code>.  If you want the file to live elsewhere, or if you want it to be associated with the servlet context instead of the <code>DispatcherServlet</code>, you'll have to set that up in <code>web.xml</code> accordingly.</p>
<p>Here's the Spring application context:</p>
<div>
Code listing: <code>/WEB-INF/contact-servlet.xml</code>
<div>
<div>
<div><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-3.html#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-3.html#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy to clipboard</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-3.html#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a><a href="http://wheelersoftware.com/articles/spring-bean-validation-framework-3.html#" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div>
</div>
<ol start="1">
    <li>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt;&nbsp;&nbsp;</li>
    <li>&lt;beans&nbsp;xmlns="http://www.springframework.org/schema/beans"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;xmlns:context="http://www.springframework.org/schema/context"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;xmlns:p="http://www.springframework.org/schema/p"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;xsi:schemaLocation="http://www.springframework.org/schema/beans&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http://www.springframework.org/schema/context&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;Enable&nbsp;annotation-based&nbsp;validation&nbsp;using&nbsp;Bean&nbsp;Validation&nbsp;Framework&nbsp;--&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;Using&nbsp;these&nbsp;instead&nbsp;of&nbsp;vld&nbsp;namespace&nbsp;to&nbsp;prevent&nbsp;Eclipse&nbsp;from&nbsp;complaining&nbsp;--&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;id="configurationLoader"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class="org.springmodules.validation.bean.conf.loader.annotation&nbsp;&nbsp;</li>
    <li>.AnnotationBeanValidationConfigurationLoader"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;id="validator"&nbsp;class="org.springmodules.validation.bean.BeanValidator"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p:configurationLoader-ref="configurationLoader"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;Load&nbsp;messages&nbsp;--&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;id="messageSource"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class="org.springframework.context.support.ResourceBundleMessageSource"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p:basenames="errors"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;Discover&nbsp;POJO&nbsp;@Components&nbsp;--&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;These&nbsp;automatically&nbsp;register&nbsp;an&nbsp;AutowiredAnnotationBeanPostProcessor&nbsp;--&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;context:component-scan&nbsp;base-package="contact"/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;Map&nbsp;logical&nbsp;view&nbsp;names&nbsp;to&nbsp;physical&nbsp;views&nbsp;--&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;class="org.springframework.web.servlet.view.InternalResourceViewResolver"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p:prefix="/WEB-INF/"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p:suffix=".jsp"/&gt;&nbsp;&nbsp;</li>
    <li>&lt;/beans&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;
&lt;!-- Enable annotation-based validation using Bean Validation Framework --&gt;
&lt;!-- Using these instead of vld namespace to prevent Eclipse from complaining --&gt;
&lt;bean id="configurationLoader"
class="org.springmodules.validation.bean.conf.loader.annotation
.AnnotationBeanValidationConfigurationLoader"/&gt;
&lt;bean id="validator" class="org.springmodules.validation.bean.BeanValidator"
p:configurationLoader-ref="configurationLoader"/&gt;
&lt;!-- Load messages --&gt;
&lt;bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"
p:basenames="errors"/&gt;
&lt;!-- Discover POJO @Components --&gt;
&lt;!-- These automatically register an AutowiredAnnotationBeanPostProcessor --&gt;
&lt;context:component-scan base-package="contact"/&gt;
&lt;!-- Map logical view names to physical views --&gt;
&lt;bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/"
p:suffix=".jsp"/&gt;
&lt;/beans&gt;
</pre>
</div>
<p>(<strong>IMPORTANT:</strong> In the <code>configurationLoader</code> definition, make sure you put the class name on a single line.  I had to break it up for formatting purposes.)</p>
<p>If you're not familiar with it, I'm using the <code>p</code> namespace here for syntactic sugar—it allows me to specify properties using a nice shorthand.</p>
<p>The first two beans basically create the <code>BeanValidator</code>
instance we're going to use. It turns out that instead of defining
these two beans explicitly, you can use a special element from a
special namespace:</p>
<ul>
    <li>namespace is <code>xmlns:vld="http://www.springmodules.org/validation/bean/validator"</code>;</li>
    <li>purported schema location is <code>http://www.springmodules.org/validation/bean/validator-2.0.xsd</code>;</li>
    <li>element is <code>&lt;vld:annotation-based-validator id="validator"/&gt;</code></li>
</ul>
<p>But when I do it, Eclipse complains (even though the code works when
you run it) since there isn't at the time of this writing actually an
XSD at the specified location. (At least there's a <a href="http://jira.springframework.org/browse/MOD-464">JIRA ticket</a> for it.)  So I'll just use the two beans for now.</p>
<p>The other stuff is mostly normal Spring WebMVC stuff. I put the
message source on the app context, scan for the controller (which is
why I'm autowiring the validator into the controller), and put a view
resolver on the context too.</p>
<h2>Finis</h2>
<p>Build and deploy your WAR, and then point your browser to your web app; for example:</p>
<p style="text-align: center;"><code>http://localhost:8080/contact-example/contact/form</code></p>
<p>Try submitting the form with empty fields, or an invalid e-mail
address, or fields that are too long. If things are working correctly,
you ought to see error messages and even field highlighting when
validation fails.</p>
<p>And that, my friends, is it! Feel free to post a comment if you run
into problems getting it to work and I'll try to help if I can.</p>
<p>Again, if you want to download the sample code (minus dependencies; see above), here it is:</p>
<div><a href="http://wheelersoftware.s3.amazonaws.com/articles/spring-bean-validation-framework/contact-example.zip">contact-example.zip</a></div>
<p>Have fun!</p>
</div>
</div>
<div>
<script type="text/javascript"><!--
google_ad_client = "pub-7290661600063392";
/* 728x90, created 2/24/08 */
google_ad_slot = "9202962775";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script>google_protectAndRun("ads_core.google_render_ad", google_handleError, google_render_ad);</script>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/299262.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-21 23:19 <a href="http://www.blogjava.net/sealyu/archive/2009/10/21/299262.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 2.5.1 MVC + Tiles 2.0.5 基本配置（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/10/20/299070.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 20 Oct 2009 09:02:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/20/299070.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/299070.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/20/299070.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/299070.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/299070.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 从sourceforge下载spring-framework-2.5.1-with-dependencies.zip，并从中拉出以下jar文件（最小化依赖）#spring 2.5.1commons-logging.jarlog4j-1.2.14.jarspring-beans.jarspring-core.jarspring-context.jarspring-web....&nbsp;&nbsp;<a href='http://www.blogjava.net/sealyu/archive/2009/10/20/299070.html'>阅读全文</a><img src ="http://www.blogjava.net/sealyu/aggbug/299070.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-20 17:02 <a href="http://www.blogjava.net/sealyu/archive/2009/10/20/299070.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring MVC framework深入分析（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/10/20/299048.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Tue, 20 Oct 2009 07:36:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/20/299048.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/299048.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/20/299048.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/299048.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/299048.html</trackback:ping><description><![CDATA[<h3>Spring MVC framework深入分析之一--总体分析</h3>
leopallas | 08 九月, 2005 14:54
<p><br />
</p>
<p>在当今的MVC framework里，似乎Webwork2逐渐成为主流，
Webwork2+SpringFramework的组合变得越来越流行。这似乎意味着Spring自带的MVC
framework远比Webwork2差，所以大家纷纷用Webwork2来代替。确实，Spring的MVC
framework不算是整个Spring的核心部件，但它的威力却超过了很多人的想象。很多人包括xiecc认为Spring的MVC
framework是非常优秀的，甚至比Webwork2更优秀。<br />
下面列举一下Spring的MVC framework在设计时做出的一些重要的决定，并将之和相关的MVC framework如Webwork2或struts进行对比：</p>
<p><br />
一、 Spring的整个MVC配置是基于IOC容器的<br />
与
struts或webwork2相比，这是一个ms有点奇怪的决定，看一下Spring
MVC的配置文件，最先看到的不是action或者form，而是一些有着特定名字的bean，Bean下面的配置是一些简单或有点复杂的属性。我们看到
的是机器更容易的数据结构，而不是人更容易理解的元素。<br />
但是这恰恰是Spring的MVC强大的根源！因为它的配置就是Spring的核心IOC
容器的配置，这意味着所有IOC容器的威力都可以在这里展现，我们可以为所欲为地对Spring MVC进行扩展和增强，我们可以完成在其它MVC
framwork中很多难以想象的任务。想扩展新的URL映射方式吗？要换一个themeResolver或LocalReolver的实现吗?想在页面
中显示新类型的View（比如说RDF，呵呵，一个小秘密：xiecc是研究语义网的，虽然成天不务正业，不写论文，只写八卦）？甚至想直接在
Controller里定义AOP吗？这些对Spring的MVC来说都是小菜一碟。<br />
我没有仔细研究过Webwork2的扩展机制，我知道通过
Webwork2的interceptor机制，可以进行很多的扩展，甚至有一个简单简单的IOC容器。但不管它有多强大，提供了多少扩展点。它的威力都
很难和真正的IOC容器相比。而struts的plugin功能则是出名的滥，虽然它也提供了plugin机制。<br />
Spring采用IOC配置的另
一个原因是使Spring的MVC与Spring的IOC容器的整合变得非常的容易。Spring提供了与struts与webwork2的整合，但是这
样整合都需要在进行间接的包装，感觉总不是很自然。而且还会导致一个概念多个配置，webwork2就需要在Spring里配置bean，再配置自己的
xwork文件。想象一下吧，我们的bean直接就是一个controller,直接可以完成MVC的所有任务,这是多少爽的感觉。<br />
Rod
Johnson采用IOC容器来实现的另一个原因是这会减少好多开发工作量。看一下urlMapping吧，它提供的property本身就是一个
HashMap，只有配置完成，我们的bean里的数据就自然存在了，哈哈，好爽吧。不用象struts那样解析XML，再把它的内容一项一项地读到
HashMap里。<br />
虽然这样的配置会有点怪异，但假如我们对Spring的IOC容器非常熟悉的话，会发现它非常的亲切，也非常的简单。<br />
最
后是一个简单的小秘密，Spring怎么知道某个bean的配置就是urlMapping？另一个bean的配置就是viewResolver？其实很简
单，把所有的bean全部读到内存里，然后通过bean的名字或类型去找就行了。通过名字去找就是简单的getBean方法，通过类型去找则使用了
BeanFactoryUtils.beansOfTypeIncludingAncestors的静态方法。</p>
<p>二、 Spring提供了明确的Model, View概念和相应的数据结构<br />
在Spring里有一个有趣的数据类型叫做ModelAndView，它只是简单地把要显示的数据和显示的结果封装在一个类里。但是它却提供了明确的MVC概念，尤其是model概念的强化，使程序的逻辑变得更清晰了。<br />
记
得以前在Struts里写程序里的时候，为了显示数据经常自己把东西放到HttpSession或HttpServletRequest里（或set到
form里，虽然不太有用），这造成了model概念的模糊，而且也导致了struts与JSP页面的紧耦合。假如我们要替换成Veloctiy,就得另
外加一个plugin,因为在velocity里数据是不需要不放到request里的。<br />
Webwork2里强调的是与Web
framework解耦和它的command模式的简单性，因此在它的action里只有简单的get或set方法，假如返回数据，也只是简单地返回一个
String。当然这样的实现有它的好处，但是它淡化了model和view的概念。Rod
Johnson认为Webwork2里的Action同时包含了Action和Model的职责，这样一个类的职责太多，不是一个很好的设计。当然
Jason
Carreira不太认同这种观点，因为Action里的model对象完成可以delege给其它对象。但不管怎样，这种争论的根源在于
Webwork2里淡化了model, view甚至web的概念。仁者见仁，智者见智，最后的结果还是看个人喜欢好吧。</p>
<p>三、 Spring的Controller是Singleton的，或者是线程不安全的<br />
和
Struts一样，Spring的Controller是Singleton的，这意味着每个request过来，系统都会用原有的instance去处
理，这样导致了两个结果：我们不用每次创建Controller，减少了对象创建和垃圾收集的时间；由于只有一个Controller的
instance，当多个线程调用它的时候，它里面的instance变量不是线程安全的。<br />
这也是Webwork2吹嘘的地方，它的每个
Action都是线程安全的。因为每过来一个request，它就创建一个Action对象。由于现代JDK垃圾收集功能的效率已经不成问题，所以这种创
建完一个对象就扔掉的模式也得到了好多人的认可。Rod Johnson甚至以此为例证明J2EE提供的object pool功能是没多大价值的。<br />
但
是当人们在吹嘘线程安全怎么怎么重要的时候，我想请问有多少人在多少情况下需要考虑线程安全？Rod
Johnson在分析EJB的时候也提出过其它问题，并不是没有了EJB的线程安全魔法，世界就会灭亡的，大多数情况下，我们根本不需要考虑线程安全的问
题，也不考虑object pool。因为我们大多数情况下不需要保持instance状态。<br />
至少我写了那么多的struts
Action，写了那么多的Spring
Controller，几乎没有碰到需要在instance变量保持状态的问题。当然也许是我写的代码不够多，Struts的设计者Craig R.
McClanahan曾经说当时他设计struts时有两个条件不成熟：当时没有测试驱动开发的概念；当时JVM的垃圾收集性能太次。假如现在重新设计的
话，他也会采用每个request生成一个新对象的设计方法，这样可以解决掉线程安全的问题了。</p>
<p>四、 Spring不象Webwork2或tapestry那样去隐藏Servlet相关的元素如HttpServletRequest或HttpServletResponse<br />
这
又是一个重要的设计决定。在Webwork2里我们没有HttpServletRequest或者HttpServletResponse，只有
getter,
setter或ActionContext里数据，这样的结果导致一个干净的Action，一个与Web完全无关的Action，一个可以在任何环境下独
立运行的bean。那么Webwork2的这样一个基于Command模式的Action究竟给我们带来了什么?我想主要有两点：<br />
1、 它使我们的Action可以非常容易地被测试。<br />
2、 用户可以在Action里添加业务逻辑，并被其它类重用。<br />
然
而仔细跟Spring比较一下，我们就会发现这两点功能所带来的好处其实并不象我们想象的那么显著。Spring的Controller类也可以非常轻松
被测试，看一下spring-mock下面的包吧，它提供的MockHttpServletRequest,
MockHttpServletResponse还有其它一些类让测试Controller变得异常轻松。再看一下Action里的业务逻辑
吧，Jason
Carreira曾经说我们可以尽情地在Webwork2的Action里加业务逻辑，因为Action是不依赖于Web的。但是有多少人真正往
Action里加业务逻辑的？大多数人都会业务逻辑delegate给另一个Service类或Manager类。因为我们很清楚，往Action里加业
务逻辑会使整个体系的分层架构变得不清晰，不管怎样，Web层就是Web层，业务层就是业务层，两者的逻辑混在一起总会带来问题的。而且往Action里
加业务逻辑会使用这个Action类变得庞大，Webwork2的Action是每个request都创建实例的，尽管带来的性能影响不太大，但并不表示
每次都要把业务逻辑再new出来，业务逻辑在大多数的情况下应该是单例的。<br />
不把request和response展现给用户当然还会带来功能上的
损失，也许一般的场合，用用webwork2提供的接口已经足够了，但有时我们必须要知道request和response才能发挥出更大的威力。比如我
以前的一个项目里有一个通过递归动态生成的树状结构的页面，在jsp页面上显示递归是痛苦或不可能的，因此我用response直接write出页面，这
在spring里很easy，但在webwork里可能比较难了（偶不敢肯定，偶研究得不够深，也许高手是有办法的）。</p>
<p>五、 Spring提供了不错但不够充分的interceptor机制<br />
回头看一下struts，它在架构里甚至没有给我们提供hook point的机会，我们没有任何机会加入自己的interceptor。我们只能通过重载struts的RequestProcessor类来进行一点有限的扩展。<br />
到
了Webwork2，似乎interceptor一下子成了整个Framework的核心，除了Action的核心部件，其它所有的东西都是
interceptor。它的超强的interceptor功能使们扩展整个架构变得非常方便。有人称这种interceptor为AOP，Jason
Carreira则自豪地宣称这个叫做pragamtic
AOP。我不认同这是AOP，它只是简单的interceptor机制。但不管如何，它的interceptor确实有强大的功能。<br />
Spring
也提供了它的interceptor机制，它的HandlerInterceptor三个interceptor方法：peHandle,
postHandle, afterCompletion。分别对应Controller执行前，Controller执行后和page
render之后。虽然大多数情况下已经够用，但是从功能上来说显然它没有Webwork2强大。从AOP的角度来看，它没有提供around
interceptor，而只有before与after
interceptor。这意味着我们无法在interceptor前后保持状态，最简单的情况假如我们要计算一个Controller的执行时间，我们
必须在执行完before后把begintime这个状态保持住，再在after里把它调出来，但是显然这个状态保持会是个问题，我们不能把它放到
instance变量里，因为interceptor不是线程安全的。也许通过ThreadLocal可以解决这个问题，但是如此简单的功能要用到这样的
方法来处理，显然这个Interceptor本身设计上还是有点问题的。<br />
六、 Spring提供了MultiActionController，使它可以在一个类里包含多个Action<br />
这
个设计和struts的DispatchAction有点类似，只不过提供了更灵活的机制。当我们的项目变大的时候，把功能类似的方法放到同一个
Action里完全值得的！Webwork2缺少这样的机制。假如看一下Spring的源代码，会发现其实实现
MultiActionController的工作量相当的少，只不过是用反射机制把解析出来的方法名执行一下就完事了。其实Webwork2也完全可以
提供这样的机制。虽然从设计上来说确实不是很优雅，但是它确实很有用。</p>
<p>七、 Spring提供了更多的选择方式<br />
看看Spring里
提供的Controller吧，它提供了好多不同的Controller类。要生成Wizard吗？要专门用于提交form的Controller吗?要
执多个方法的类吗?Spring提供了丰富的子类来扩展这些选择。当然我们还可以很轻松地自己扩展这些功能。<br />
再看看Spring的
ViewResolver吧，
它提供了无数不同类型的ViewResolver。更重要的是我们自定义我们的页面映射方式。看看strtus，看看webwork2，都会存在页面与
forward
name的一层间接转换，我们必须在配置文件里配置好某个字符串（典型的是success）对应的是那个页面。但是Spring里我们有了更大的自由度，
我们可以采用webwork2的策略，也可以采用更简单的策略，如将JSP文件名去掉扩展名的映射方法。也许有人认为这种映射方式很幼稚，但是我觉得它是
非常有用的方式，即使在大项目里。<br />
还有新的扩展吗？看看Spring Web Flow吧，它是SpringFramework的子项目。它为一长串的基于页面流的Wizard页面提供了可配置的实现方式。在Spring 1.3里，它将是SpringFramework的一部分。</p>
<p>八、 Spring的tag<br />
尽
管Spring的tag数量上少得可怜，但它却是精心设计的。它的目标很简单：让美工可以轻松地编辑页面。因为在Spring的页面里Text仍然是
Text，checkbox仍然是CheckBox，而不象在struts或webwork2中的Tag。它只是用Springbind对输入内容进行了
一下包装。所以尽管页面显示代码上会比Webwork2多，但这绝对是有价值的。</p>
<p>在接下来的几章里，我会分析一下Spring是如何让我们的Web应用不需要知道ApplicationContext就能够访问IOC容器的，然后会对Spring的设计和执行过程进行简单的源码分析，然后给出几个扩展Spring MVC的方法。</p>
<h3>Spring MVC framework深入分析之二--ApplicationContext之谜 </h3>
leopallas | 09 九月, 2005 20:25
<br />
<p>假如我们在写一个基于Spring的普通应用程序，不管我们用了多么精妙的设计模式，进行了如何巧妙的设计，我们必须在某个地方执行这样的代码：</p>
<p>ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(<br />
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});<br />
appContext.getBean("&#8230;"); </p>
<p><br />
也
许这样的代码算不上丑陋，但是它无疑破坏了程序的纯洁性和透明性。我们的应用程序开始显式地依赖SpringFramework，我们必须清楚地知道
Spring的配置文件有哪几个，每个配置文件的加入或修改源代码，我们必须在某些代码模块里调用丑陋的getBean方法来创造对象。</p>
<p><br />
但
是所有的这些丑陋的事情似乎在我们的Web应用程序里消失啦，所有的代码都是那么干净，只有简单的get与set及接口之间的调用，我们不需要知道
ApplicationContext，我们甚至不需要知道Spring。但是我们所有的对象却又是通过Spring的
ApplicationContext来创造的！</p>
<p><br />
看上去似乎很神奇，但是假如我们稍微思考一下，就会发现这是一件合情合理又如此简单的事情，呵呵，只有第一个想到这个方法的人才是伟大的。让我们仔细想一下普通应用程序和Web应用程序的最大区别在哪里？</p>
<p><br />
其
实真正的区别只有一个，普通应用程序是一个主动执行的程序，而Web应用程序却是被动的组件。这意味着Web应用程序无法自己主动去生成自己的线程去执行
某项任务，而必须借用Web容器中的一个线程。想象一下一个简单的任务：我们想每隔一段时间执行一个任务，比如说在Console里打印出一行文字。在我
们的Web应用程序里应该怎么完成？在我不知道Servlet
Listener或Spring里提供的Schedule之前（其实Spring就是利用Servlet Listner初始化Application
Context时启动schedule的），这么简单的任务在一个Web应用程序里竟然是不可想象。还记得我当时采用的是最傻的做法：写了一个单独的应用
程序，在这应用程序的main函数里启动了timetask。</p>
<p><br />
但是如果换一种角度来看，整个Web应用程序生活在容器里也给我们带
来了额外的好处，当我们让出了对应用程序的控制权之后，我们可以让容器帮我们完成很多本来很难处理的事情。其实IOC容器的真正作用也在于此，当我们把我
们的对象创建工作移交给IOC容器之后，我们发现整个程序变得如此清晰，如此透明，对象之间的关联、哪些类需要事务处理或AOP功能、哪些类要远程访问，
所有这些复杂的事情在我们的程序里都不见了，我们只看到了简单的get和set。</p>
<p><br />
也许废话太多，但我觉得经过这样分析，其实
ApplicationContext之谜已经不再是谜了。真正的关键在于当我们的Web应用程序是被动的组件时，它除了可以错用容器的线程之外还可以错
用其它一些东西。我们可以让容器来帮我们创建ApplicationContext，然后把它放在某个地方，然后在需要使用时让容器从这个地方把
ApplicationContext读出来，并执行相应的Controller就可以了。<br />
这个"某个地方"就是ServletContext，而这个创建ApplicationContext的地方就是Servlet Listner，而取到ApplicationContext的地方是我们的DispatcherServlet。</p>
<p><br />
仔
细想一下，其实Web服务器并没有什么了不起的地方，它只是一个Java程序，它只是会在启动的时候去ClassLoad某些指定文件夹下的lib或
classes，它会读某个在WEB-INF下面一个叫做web.xml的配置文件，再做一些初始化工作。Servlet
Listener就是这个初始化工作的重要一步，服务器会读出web.xml里配置好的所有listner，然后调用每个Listner的
contextInitialized方法（它还会去调每个Servlet的init方法，不过把初始化方法写在Listner里才是天经地义的）。哈
哈，这也正是Spring
MVC创建ApplicationContext的最好时机，当我们在web.xml里配置好ContextLoaderListener的时
候，Spring就完成了ApplicationContext的创建过程，如果有人想研究源代码的话可以去看一下，不过这个创建过程并不象想象中的那么
有趣，只是通过Class.forName和BeanUtils.instantiateClass创建出一个
WebApplicationContext，然后再读了一下IOC容器的配置文件。<br />
接下来的一个问题是我们要把创建的
ApplicationContext放在哪里？答案是ServletContext，其实没必须对ServletContext进行深究，它只是可以一
个可以全局存放Web应用程序的场所，我们只要想象成一个全局的HashMap就可以了，我们可以要把它put进去，就可以在Servlet或其它地方把
它get出来。</p>
<p><br />
Web服务器还要干的一件事件当然是在某个request到来时，它会启动一个单独的线程（这也是为何
Webwork可以把Context放到ThreadLocal里的原因），根据web.xml里的配置和request的URI匹配去执行相应的
Servlet。由于Servlet可以很轻松地读到ServletContext，当然也可以很轻松地读到ApplicationContext啦。接
下来的事情就比想象中要简单啦，经过一些准备工作之后ApplicationContext中的URLMapping里配置好的某个
Controller，执行一下再rend某个view就可以了。其实struts或webwork2的执行过程也是如此，所以MVC
framwork分析透了其实真没什么了不起，远比O/R
Mapping或其它的framework简单。虽然MVC的执行过程如此简单，但是我们还需要了解一些细节上的事件，所以让我们下次来讨论一下
Spring MVC framework的执行过程吧。</p>
<h3>Spring MVC framework深入分析之三--执行过程 </h3>
leopallas | 10 九月, 2005 22:24
<br />
其实每个MVC framework<span style="font-family: 宋体;">的执行过程都是大同小异的，当个</span>request<span style="font-family: 宋体;">过来时，它都通过一个</span>Servlet<span style="font-family: 宋体;">来响应</span>request<span style="font-family: 宋体;">，再根据</span>request<span style="font-family: 宋体;">的路径名和配置将这个</span>request dispatch<span style="font-family: 宋体;">给一个</span>Controller<span style="font-family: 宋体;">执行，最后将之返回配置文件里对应的页面。在</span>Spring MVC<span style="font-family: 宋体;">里，这个</span>Servlet<span style="font-family: 宋体;">的名字叫</span>DispatchServlet<span style="font-family: 宋体;">。稍看一下它的源码会发现这是一很简单的类。</span>
<p><span style="font-family: 宋体;">下面是</span>DispatchServlet<span style="font-family: 宋体;">的类图</span>:</p>
<p><span style="font-family: 宋体;"><img alt="servlet类图" src="http://blog.itpub.net/resserver.php?blogId=1476&amp;resource=servlet.gif&amp;mode=medium" align="baseline" border="0" hspace="0" /></span></p>
<p><span style="font-family: 宋体;">简单吧，这是典型的</span>Template Method<span style="font-family: 宋体;">模式。每个类都会完成一些自己的本职工作，把不属于自己的工作延迟到子类来完成。这些类的子职责在下面会有分析。其实整个</span>SpringFramework<span style="font-family: 宋体;">用的最多的</span><span style="font-family: 宋体;">模式就是</span>Template Method<span style="font-family: 宋体;">（</span>Strategy<span style="font-family: 宋体;">也挺多，呵呵），也许任何</span>Framework<span style="font-family: 宋体;">用的最多的都是</span>Template Method<span style="font-family: 宋体;">模式。</span>Why?<span style="font-family: 宋体;">看看</span>Expert One on One J2EE Design and Development<span style="font-family: 宋体;">吧</span><span style="font-family: 宋体;">，</span><span style="font-family: 宋体;">至少</span>Template Method<span style="font-family: 宋体;">和</span>Strategy<span style="font-family: 宋体;">模式的分析这本书甚至比</span>Head first Design Pattern<span style="font-family: 宋体;">还好。</span></p>
<p><span style="font-family: 宋体;">我们先来看</span>DispatchServlet<span style="font-family: 宋体;">的初始化执行过程分析吧：</span></p>
<p><span style="font-family: 宋体;">我们知道每个</span>Servlet<span style="font-family: 宋体;">在</span>Web<span style="font-family: 宋体;">服务器启动时都会有一个初始化的机会，这就是</span>Servlet<span style="font-family: 宋体;">的</span>init<span style="font-family: 宋体;">过程，这是配置</span>Servlet<span style="font-family: 宋体;">的最好机会。我们可以在这个阶段干些啥事情呢？</span></p>
<p>1<span style="font-family: 宋体;">、把初始化的那些</span>init-param<span style="font-family: 宋体;">读到</span>Servlet<span style="font-family: 宋体;">的属性里。我们知道</span>Servlet<span style="font-family: 宋体;">的</span>init-param<span style="font-family: 宋体;">是放在</span>ServletConfig<span style="font-family: 宋体;">里的，我们可以用循环去取这些属性。但是每次都这么干实在太累了，干吗不把在</span>Servlet<span style="font-family: 宋体;">里增加几个</span>property<span style="font-family: 宋体;">，再这些</span>init-param<span style="font-family: 宋体;">直放到</span>Servlet<span style="font-family: 宋体;">的</span>property<span style="font-family: 宋体;">里呢？呵呵，以后那些初始参数都可以直接拿来用啦，真方便。</span>DispatchServlet<span style="font-family: 宋体;">的一个祖先类叫做</span>HttpServletBean<span style="font-family: 宋体;">就是专门干这个的。以后假如我们要写自己的</span>Servlet<span style="font-family: 宋体;">也可以直接继承</span>HttpServletBean<span style="font-family: 宋体;">这个类，这样读</span>ServletConfig<span style="font-family: 宋体;">的操作都省掉了，哈哈！</span></p>
<p>2<span style="font-family: 宋体;">、从</span>ServletContext<span style="font-family: 宋体;">里取出</span>ApplicationContext<span style="font-family: 宋体;">，并扩展成自己的</span>ApplicationContext.</p>
<p><span style="font-family: 宋体;">在</span>ApplicationContext<span style="font-family: 宋体;">之谜里我们已经提到我们可以用</span>Servlet Listner<span style="font-family: 宋体;">执行的机会把</span>ApplicationContext<span style="font-family: 宋体;">放到</span>ServletContext<span style="font-family: 宋体;">中去。但是不是直接拿这个</span>ApplicationContext<span style="font-family: 宋体;">就足够了呢？</span>no<span style="font-family: 宋体;">。我们先问一个简单的问题吧：在</span>Spring MVC<span style="font-family: 宋体;">里我们是不是只能配置一个</span>Servlet<span style="font-family: 宋体;">呢？</span>Struts<span style="font-family: 宋体;">就是那么干的，所以在</span>Struts<span style="font-family: 宋体;">里所有的</span>request<span style="font-family: 宋体;">过来都会交给一个</span>Servlet<span style="font-family: 宋体;">去处理。但是在</span>Spring MVC<span style="font-family: 宋体;">里，我们却可以有好多个</span>Servlet<span style="font-family: 宋体;">！它们可以处理不同类型的</span>request<span style="font-family: 宋体;">，而且更重要的是它们的</span>ApplicationContext<span style="font-family: 宋体;">不是相同的，它们共享了一个父</span>ApplicationContext<span style="font-family: 宋体;">，也就是从</span>ServletContext<span style="font-family: 宋体;">里取出来的那个，但是它们却会根据自己的配置作扩展，形成这个</span>Servlet<span style="font-family: 宋体;">特有的</span>ApplicationContext<span style="font-family: 宋体;">。这个子的</span>ApplicationContext<span style="font-family: 宋体;">里有自己的</span>namespace<span style="font-family: 宋体;">，也就是将一个叫做（假如</span>servlet<span style="font-family: 宋体;">名称叫</span>xiecc<span style="font-family: 宋体;">）</span> xiecc-servlet.xml<span style="font-family: 宋体;">的配置文件读进来，行成一个自己的</span>ServletContext<span style="font-family: 宋体;">。所以这些过程全是在</span>DispatchSevlet<span style="font-family: 宋体;">的一个父类</span>FrameworkServlet<span style="font-family: 宋体;">里干的。</span></p>
<p>3<span style="font-family: 宋体;">、初始化接来要干的一件最重要的事情是初始</span>DispatchServlet<span style="font-family: 宋体;">里的接口。如果用</span>IOC<span style="font-family: 宋体;">容器的角度来说，其实是将</span>ApplicationContext<span style="font-family: 宋体;">里定义好的接口注入到一个叫做</span>DispatchServlet<span style="font-family: 宋体;">的</span>bean<span style="font-family: 宋体;">里，只不过这个注入过程是手动的。注入的代码大致如果下（部分角色的含义和作用以后会解释）：</span></p>
<p>initMultipartResolver();</p>
<p>initLocaleResolver();</p>
<p>initThemeResolver();</p>
<p>initHandlerMappings();</p>
<p>initHandlerAdapters();</p>
<p>initHandlerExceptionResolvers();</p>
<p>initViewResolvers();</p>
<p><span style="font-family: 宋体;">每个</span>init<span style="font-family: 宋体;">方法其实是属性设置的过程，因为我们可以拿到自己的</span>ApplicationContext<span style="font-family: 宋体;">，所以这一切都变得很轻松啦。比如说</span>multipartResolver<span style="font-family: 宋体;">，直接用</span>getWebApplicationContext().getBean(MULTIPART_RESOLVER_BEAN_NAME);<span style="font-family: 宋体;">就能拿到了。</span></p>
<p><span style="font-family: 宋体;">不过很多东西在配置文件里是不需要写的，比如说</span>multipartResolver<span style="font-family: 宋体;">，如果在</span>xml<span style="font-family: 宋体;">里取不到，</span>Spring<span style="font-family: 宋体;">会初始化默认的</span>MultipartResolver<span style="font-family: 宋体;">。</span></p>
<p><span style="font-family: 宋体;">接下来我们分析一下</span>DispatchServlet<span style="font-family: 宋体;">怎么处理</span>Request<span style="font-family: 宋体;">的执行流程吧：</span></p>
<p><span style="font-family: 宋体;">看一下</span>DispatchServlet<span style="font-family: 宋体;">的源码会发现它出奇的简单。简单的原因是类</span>Template Method<span style="font-family: 宋体;">的</span>Strategy<span style="font-family: 宋体;">模式，呵呵，这是我取的一个怪名字。因为</span>DispatchServlet<span style="font-family: 宋体;">是不负责任何具体的操作的，它将具体的操作都</span>delegate<span style="font-family: 宋体;">给了相应的接口，这是典型的</span>Strategy<span style="font-family: 宋体;">模式。但是</span>DispatchServlet<span style="font-family: 宋体;">里的</span>doDispatch<span style="font-family: 宋体;">方法却控制了执行流程，所有的</span>request<span style="font-family: 宋体;">过来，我们都会按这样的一个流程处理，这和</span>Template Method<span style="font-family: 宋体;">里的由父类控制流程不谋而合。所以它仍然是</span>Strategy<span style="font-family: 宋体;">模式，只不过主类还多了个控制流程的职责。</span></p>
<p><span style="font-family: 宋体;">所以</span>DispatchServlet<span style="font-family: 宋体;">本身的</span>doService<span style="font-family: 宋体;">方法只是负责控制执行流程，而将所有具体的实现细节全都</span>delegate<span style="font-family: 宋体;">给相应接口，难怪会那么简单。整个流程也异常的简单，我们甚至找不到循环跳转，所有的东西都是一直线下来的，撇开一些细节不说，剩下的就是这以几步了：</span></p>
<span style="font-family: 宋体;">
<table style="border: medium none ; border-collapse: collapse;" border="1" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 30.95pt; background-color: transparent;" valign="top" width="41">
            <p><span style="font-family: 宋体;">序号</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 114.9pt; background-color: transparent;" valign="top" width="153">
            <p><span style="font-family: 宋体;">步骤</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 211pt; background-color: transparent;" valign="top" width="281">
            <p><span style="font-family: 宋体;">具体内容</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 69.25pt; background-color: transparent;" valign="top" width="92">
            <p><font face="Times New Roman">Delegate</font><span style="font-family: 宋体;">给谁干？</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 30.95pt; background-color: transparent;" valign="top" width="41">
            <p><font face="Times New Roman">1</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 114.9pt; background-color: transparent;" valign="top" width="153">
            <p><span style="font-family: 宋体;">准备工作</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 211pt; background-color: transparent;" valign="top" width="281">
            <p><span style="font-family: 宋体;">设置</span><font face="Times New Roman">theme, locale</font><span style="font-family: 宋体;">之类的东西。看看</span><font face="Times New Roman">Request</font><span style="font-family: 宋体;">里是不是要上传文件，如果需要就转成</span><font face="Times New Roman">MultipartRequest</font><span style="font-family: 宋体;">。</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 69.25pt; background-color: transparent;" valign="top" width="92"><br />
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 30.95pt; background-color: transparent;" valign="top" width="41">
            <p><font face="Times New Roman">2</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 114.9pt; background-color: transparent;" valign="top" width="153">
            <p><span style="font-family: 宋体;">从</span><font face="Times New Roman">request</font><span style="font-family: 宋体;">中取到</span><font face="Times New Roman">HandlerExecutionChain</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 211pt; background-color: transparent;" valign="top" width="281">
            <p><span style="font-family: 宋体;">这个过程其实是提交给配置文件里定义的</span><font face="Times New Roman">HandlerMapping </font><span style="font-family: 宋体;">来做。</span><font face="Times New Roman">HandlerExecutionChain</font><span style="font-family: 宋体;">是一个很有趣的东西，它包括就是我们要执行的</span><font face="Times New Roman">Controller</font><span style="font-family: 宋体;">和一组</span><font face="Times New Roman">interceptor</font><span style="font-family: 宋体;">，我们把它想象成一个执行单元就行啦。（细节上略有不同，下面会有分析）</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 69.25pt; background-color: transparent;" valign="top" width="92">
            <p><font face="Times New Roman">HandlerMapping</font></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 30.95pt; background-color: transparent;" valign="top" width="41">
            <p><font face="Times New Roman">3</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 114.9pt; background-color: transparent;" valign="top" width="153">
            <p><span style="font-family: 宋体;">执行</span><font face="Times New Roman">HandlerExecutionChain</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 211pt; background-color: transparent;" valign="top" width="281">
            <p><span style="font-family: 宋体;">其实</span><font face="Times New Roman">HandlerExecutionChain</font><span style="font-family: 宋体;">里已经包括了要执行的一切东西啦，我们只要把它分解开来执行就行啦。这有点象</span><font face="Times New Roman">AOP</font><span style="font-family: 宋体;">或</span><font face="Times New Roman">Webwork</font><span style="font-family: 宋体;">的执行方法</span><font face="Times New Roman">pre interceptors</font><span style="font-family: Wingdings;">&#224;</span><font face="Times New Roman">Controller</font><span style="font-family: Wingdings;">&#224;</span><font face="Times New Roman">postInterceptors</font><span style="font-family: 宋体;">。</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 69.25pt; background-color: transparent;" valign="top" width="92">
            <p><font face="Times New Roman">HandlerExecutionChain</font><span style="font-family: 宋体;">，它再</span><font face="Times New Roman">delegate</font><span style="font-family: 宋体;">给一组</span><font face="Times New Roman">interceptor</font><span style="font-family: 宋体;">和一个</span><font face="Times New Roman">Controller</font></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 30.95pt; background-color: transparent;" valign="top" width="41">
            <p><font face="Times New Roman">4</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 114.9pt; background-color: transparent;" valign="top" width="153">
            <p><span style="font-family: 宋体;">执行完的结果拿去显示，也就是所谓的</span><font face="Times New Roman">render</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 211pt; background-color: transparent;" valign="top" width="281">
            <p><span style="font-family: 宋体;">通过</span><font face="Times New Roman">ViewResolver</font><span style="font-family: 宋体;">的转换后，最后我们会</span><font face="Times New Roman">delegate</font><span style="font-family: 宋体;">给一个</span><font face="Times New Roman">View</font><span style="font-family: 宋体;">来完成最后的任务</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 69.25pt; background-color: transparent;" valign="top" width="92">
            <p><font face="Times New Roman">ViewResolver</font></p>
            <p><font face="Times New Roman">View</font></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 30.95pt; background-color: transparent;" valign="top" width="41">
            <p><font face="Times New Roman">5</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 114.9pt; background-color: transparent;" valign="top" width="153">
            <p><font face="Times New Roman">Render</font><span style="font-family: 宋体;">完后还有</span><font face="Times New Roman">interceptor</font></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 211pt; background-color: transparent;" valign="top" width="281">
            <p><font face="Times New Roman">Spring</font><span style="font-family: 宋体;">提供了三个</span><font face="Times New Roman">interceptor</font><span style="font-family: 宋体;">的机会，前两个</span><font face="Times New Roman">Controller</font><span style="font-family: 宋体;">方法执行前后</span></p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 69.25pt; background-color: transparent;" valign="top" width="92">
            <p><font face="Times New Roman">HandlerExecutionChain</font><span style="font-family: 宋体;">里的</span><font face="Times New Roman">interceptor</font></p>
            </td>
        </tr>
    </tbody>
</table>
<p>OK.<span style="font-family: 宋体;">是不是写得很乱？我自己都觉得惭愧啦，没办法，只好让我们再回头分析一下我们碰到几个角色吧：</span></p>
<p>1<span style="font-family: 宋体;">、</span>HandlerMapping</p>
<p>HandlerMapping<span style="font-family: 宋体;">这个接口的定义非常简单</span><span style="font-family: 宋体;">：</span></p>
<p>public interface HandlerMapping {</p>
<p>HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;</p>
<p>}</p>
<p><span style="font-family: 宋体;">不就是根据</span>request<span style="font-family: 宋体;">的</span>URL path<span style="font-family: 宋体;">来取得相应的</span>HandlerExecutionChain<span style="font-family: 宋体;">。</span></p>
<p><span style="font-family: 宋体;">例如：我的</span>URL<span style="font-family: 宋体;">是</span>http://localhost:8080/blog/xiecc.htm</p>
<p>RequestURL<span style="font-family: 宋体;">的字符串一截，拿到了</span>&#8221;/xiecc.htm&#8221;<span style="font-family: 宋体;">，再去每个</span>HandlerMapping<span style="font-family: 宋体;">里一查（还记得初始化时我们已经将所有的</span>HandlerMapping<span style="font-family: 宋体;">都从配置文件里注入进来了吧），假如我们在某个</span>SimpleUrlHandlerMapping<span style="font-family: 宋体;">里找到了</span>&#8221;/xiecc.htm&#8221;<span style="font-family: 宋体;">，我就立刻可以拿到它对应</span>Controller<span style="font-family: 宋体;">和一组</span>intercetpors<span style="font-family: 宋体;">了，拿过来组装一下就是一个</span>HandlerExecutionChain<span style="font-family: 宋体;">啦。</span></p>
<p><span style="font-family: 宋体;">下面是</span>HandlerMapping<span style="font-family: 宋体;">的类图：</span></p>
<p><img src="http://blog.itpub.net/resserver.php?blogId=1476&amp;resource=urlMapping.gif&amp;mode=medium" align="baseline" border="0" hspace="0"  alt="" /></p>
<p><span style="font-family: 宋体;">看上去有点麻烦，其实挺简单，具体的类我就不分析啦。它的核心是</span><span style="font-family: 宋体;">：</span>it&#8217;s all about HashMap<span style="font-family: 宋体;">。</span></p>
<p><span style="font-family: 宋体;">还记得我们在</span>Spring MVC<span style="font-family: 宋体;">最常用的</span>HandlerMapping<span style="font-family: 宋体;">吗</span><span style="font-family: 宋体;">？</span><span style="font-family: 宋体;">是</span>SimpleUrlHandlerMapping<span style="font-family: 宋体;">，</span><span style="font-family: 宋体;">我们在配置它的时候</span><span style="font-family: 宋体;">，最核心的结构就是</span>HashMap<span style="font-family: 宋体;">，哈哈！东西都在</span>HashMap<span style="font-family: 宋体;">里，只要通过</span>URL<span style="font-family: 宋体;">分析找到</span>HashMap<span style="font-family: 宋体;">的</span>key<span style="font-family: 宋体;">，比如说</span>&#8221;/xiecc.htm&#8221;<span style="font-family: 宋体;">，用个</span>get<span style="font-family: 宋体;">方法不就啥都取到了。</span></p>
<p><span style="font-family: 宋体;">在</span>Spring<span style="font-family: 宋体;">的配置文件里我们可以配置多个</span>HandlerMapping<span style="font-family: 宋体;">，它会一个一个去找到的，直到找到跟</span>URL<span style="font-family: 宋体;">匹配的那个</span>Controller<span style="font-family: 宋体;">，要不然就返回</span>null<span style="font-family: 宋体;">啦。</span></p>
<p>2<span style="font-family: 宋体;">、</span>HandlerExecutionChain</p>
<p><span style="font-family: 宋体;">我前面说了</span>HandlerExecutionChain<span style="font-family: 宋体;">就是一个</span>Controller<span style="font-family: 宋体;">和一组</span>interceptors<span style="font-family: 宋体;">。这是我们执行一个</span>request<span style="font-family: 宋体;">最基本的单元啦。</span></p>
<p><span style="font-family: 宋体;">不过现实情况会稍有些出入，</span>HandlerExecutionChain<span style="font-family: 宋体;">实际上包括的一个</span>Object<span style="font-family: 宋体;">和一组</span>interceptor<span style="font-family: 宋体;">。这个</span>Object<span style="font-family: 宋体;">是</span>Adaptor<span style="font-family: 宋体;">，它可以是</span>Controller<span style="font-family: 宋体;">的</span>Adaptor<span style="font-family: 宋体;">，也可以是其它类的</span>Adaptor<span style="font-family: 宋体;">。但现实中我们一般用到的都是</span>Controller<span style="font-family: 宋体;">，因此不详细分析啦，这里用了</span>Adaptor<span style="font-family: 宋体;">后大大降低了代码的可读性，来换取与</span>Controller<span style="font-family: 宋体;">非紧耦合的灵活性。至少我现在认为这样做不是太值。</span></p>
<p>3<span style="font-family: 宋体;">、</span>Controller</p>
<p>Controller<span style="font-family: 宋体;">是</span>Spring MVC<span style="font-family: 宋体;">执行的核心单元，也是程序员需要自完成的重要部分。用过</span>Spring MVC<span style="font-family: 宋体;">的人应该都对它非常熟悉啦。所以不做太具体的分析。以下是它的类图：</span></p>
<p><span style="font-family: 宋体;"><img src="http://blog.itpub.net/resserver.php?blogId=1476&amp;resource=controller.gif&amp;mode=medium" align="baseline" border="0" hspace="0"  alt="" /></span></p>
<p><span style="font-family: 宋体;">看一下类图就知道啦，这又是</span>Template Method<span style="font-family: 宋体;">的典型应用。</span>Controller<span style="font-family: 宋体;">最大的优势也正是利用</span>Template Method<span style="font-family: 宋体;">，把</span>Controller<span style="font-family: 宋体;">分解成不同功能的子类。想要把</span>request<span style="font-family: 宋体;">里的东西</span>populate<span style="font-family: 宋体;">到一个</span>bean<span style="font-family: 宋体;">里吗？直接继承</span>SimpleFormController<span style="font-family: 宋体;">就行啦。想要在</span>Controller<span style="font-family: 宋体;">里写多个方法吗？用</span>MultiActionController<span style="font-family: 宋体;">。这些</span>Controller<span style="font-family: 宋体;">设计得面面俱到，但因为</span>Controller<span style="font-family: 宋体;">的类层次太多，有的人会觉得烦。呵呵，随个人喜好啦。</span></p>
<p><span style="font-family: 宋体;">不过最核心的是不管这些</span>Controller<span style="font-family: 宋体;">如何千变万化，它们都实现了统一的</span>Controller<span style="font-family: 宋体;">接口，这使</span>DispatchAction<span style="font-family: 宋体;">调它时候根据不需要知道</span>Controller<span style="font-family: 宋体;">的细节，嗯，</span>the power of interface<span style="font-family: 宋体;">。</span></p>
<p>Controller<span style="font-family: 宋体;">里的数据绑定也是一个值得研究的东西，挺好玩的，不过这次没空写啦。</span></p>
<p>4<span style="font-family: 宋体;">、</span>interceptor</p>
<p>Interceptor<span style="font-family: 宋体;">的接口定义如下：</span></p>
<p>public interface HandlerInterceptor {</p>
<p>boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)</p>
<p>throws Exception;</p>
<p>void postHandle(</p>
<p>HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)</p>
<p>throws Exception;</p>
<p>void afterCompletion(</p>
<p>HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)</p>
<p>throws Exception;</p>
<p>}</p>
<p><span style="font-family: 宋体;">具体的我就不展开分析啦，我们只要记住</span>interceptor<span style="font-family: 宋体;">的三个</span>hook point<span style="font-family: 宋体;">（在</span>AOP<span style="font-family: 宋体;">里叫</span>join point<span style="font-family: 宋体;">，哈哈）：</span>Controller<span style="font-family: 宋体;">执行前，</span>Controller<span style="font-family: 宋体;">执行后，页面显示完成后。</span></p>
<p>5<span style="font-family: 宋体;">、</span>ViewResolver</p>
<p>ViewResolver<span style="font-family: 宋体;">是一个有趣的角色，它本身完成两个功能：一是完成了</span>View<span style="font-family: 宋体;">与实际页面名称对应关系的配置，二是</span>View<span style="font-family: 宋体;">的工厂（这可是标准的工厂模式啊，每个</span>ViewResolver<span style="font-family: 宋体;">负责生产自己的</span>View<span style="font-family: 宋体;">）。</span></p>
<p><span style="font-family: 宋体;">以下是</span>ViewResolver<span style="font-family: 宋体;">接口的定义：</span></p>
<p>public interface ViewResolver {</p>
<p>View resolveViewName(String viewName, Locale locale) throws Exception;</p>
<p>}</p>
<p><span style="font-family: 宋体;">用过</span>Spring MVC<span style="font-family: 宋体;">的人都配置过</span>ViewResolver<span style="font-family: 宋体;">，因此这里不详细展开。</span></p>
<p><span style="font-family: 宋体;">我将它对属性分成两类：一类是页面文件配置，包括</span>prefix, suffix<span style="font-family: 宋体;">；另一类是作为</span>view<span style="font-family: 宋体;">的工厂注入到</span>View<span style="font-family: 宋体;">里的属性，如</span>ContentType<span style="font-family: 宋体;">之类的。</span></p>
<p><span style="font-family: 宋体;">以下是</span>ViewResolver<span style="font-family: 宋体;">的类图：</span></p>
<p><span style="font-family: 宋体;"><img src="http://blog.itpub.net/resserver.php?blogId=1476&amp;resource=viewResolver.gif&amp;mode=medium" align="baseline" border="0" hspace="0"  alt="" /></span></p>
<p>6<span style="font-family: 宋体;">、</span>View</p>
<p>View<span style="font-family: 宋体;">是真正负责显示页面的地方。它的接口如下：</span></p>
<p>public interface View {</p>
<p>void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception; </p>
<p>}</p>
<p><span style="font-family: 宋体;">其实这是一个简单任务，给我一个</span>HashMap<span style="font-family: 宋体;">，再给个页面</span>URI<span style="font-family: 宋体;">，把这个页面显示出来还不是小</span>Case<span style="font-family: 宋体;">。不过不同的</span>View<span style="font-family: 宋体;">显示到页面上的区别还是挺大的，如果是</span>JSP<span style="font-family: 宋体;">，我只要把</span>HashMap<span style="font-family: 宋体;">里的东西填到</span>request<span style="font-family: 宋体;">里，再交给</span>RequestDispatcher<span style="font-family: 宋体;">来</span>forward<span style="font-family: 宋体;">一下就行了；如果是</span>Velocity<span style="font-family: 宋体;">，那就把</span>HashMap<span style="font-family: 宋体;">里的东西填到</span>Veloticy<span style="font-family: 宋体;">的</span>Context<span style="font-family: 宋体;">里，再把模板生成的东西</span>merge<span style="font-family: 宋体;">到</span>response<span style="font-family: 宋体;">的</span>writer<span style="font-family: 宋体;">里就完事了。当然还有</span>pdf<span style="font-family: 宋体;">或</span>xls<span style="font-family: 宋体;">的</span>View<span style="font-family: 宋体;">，我还没空研究它，哪天有兴趣了再看看吧，以下是</span>View<span style="font-family: 宋体;">的类图：</span></p>
<p><img src="http://blog.itpub.net/resserver.php?blogId=1476&amp;resource=view.gif&amp;mode=medium" align="baseline" border="0" hspace="0"  alt="" /></p>
<p><span style="font-family: 宋体;">写完这篇文章后，终于明白了什么叫做眼高手低。本来很希望写得抽象些，最后却发现我写的东西跟一般的流水帐式的源码分析其实区别不太大。呜呜，从具体到抽象很难，再把抽象的东西转化成具体更难，但只有经过这样的一层转换，我们的认识才能有很大的提高，我们的水平才能进步。</span></p>
</span>
<img src ="http://www.blogjava.net/sealyu/aggbug/299048.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-20 15:36 <a href="http://www.blogjava.net/sealyu/archive/2009/10/20/299048.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Spring中使用tiles2</title><link>http://www.blogjava.net/sealyu/archive/2009/10/19/298846.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 19 Oct 2009 06:32:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/10/19/298846.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/298846.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/10/19/298846.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/298846.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/298846.html</trackback:ping><description><![CDATA[<h1>Spring Tiles Integration</h1>
<p>In this example you will learn how to integrate Spring with Tiles 2. The directory structure of the example is shown below.</p>
<img src="http://www.vaannila.com/images/spring/spring-tiles-pic-1.gif"  alt="" />
<p>
Add the following library files to the lib directory.
</p>
<div id="highlighter_61452">
<div>
<div alt1=""><code>01.</code><span style="margin-left: 0px ! important;"><code>antlr-runtime-3.0</code></span></div>
<div alt2=""><code>02.</code><span style="margin-left: 0px ! important;"><code>commons-logging-1.0.4</code></span></div>
<div alt1=""><code>03.</code><span style="margin-left: 0px ! important;"><code>org.springframework.asm-3.0.0.M3</code></span></div>
<div alt2=""><code>04.</code><span style="margin-left: 0px ! important;"><code>org.springframework.beans-3.0.0.M3</code></span></div>
<div alt1=""><code>05.</code><span style="margin-left: 0px ! important;"><code>org.springframework.context-3.0.0.M3</code></span></div>
<div alt2=""><code>06.</code><span style="margin-left: 0px ! important;"><code>org.springframework.context.support-3.0.0.M3</code></span></div>
<div alt1=""><code>07.</code><span style="margin-left: 0px ! important;"><code>org.springframework.core-3.0.0.M3</code></span></div>
<div alt2=""><code>08.</code><span style="margin-left: 0px ! important;"><code>org.springframework.expression-3.0.0.M3</code></span></div>
<div alt1=""><code>09.</code><span style="margin-left: 0px ! important;"><code>org.springframework.web-3.0.0.M3</code></span></div>
<div alt2=""><code>10.</code><span style="margin-left: 0px ! important;"><code>org.springframework.web.servlet-3.0.0.M3</code></span></div>
<div alt1=""><code>11.</code>&nbsp;</div>
<div alt2=""><code>12.</code><span style="margin-left: 0px ! important;"><code>commons-beanutils-1.7.0</code></span></div>
<div alt1=""><code>13.</code><span style="margin-left: 0px ! important;"><code>commons-digester-1.8</code></span></div>
<div alt2=""><code>14.</code><span style="margin-left: 0px ! important;"><code>commons-logging-api-1.1</code></span></div>
<div alt1=""><code>15.</code><span style="margin-left: 0px ! important;"><code>jstl</code></span></div>
<div alt2=""><code>16.</code><span style="margin-left: 0px ! important;"><code>standard</code></span></div>
<div alt1=""><code>17.</code><span style="margin-left: 0px ! important;"><code>tiles-api-2.0.4</code></span></div>
<div alt2=""><code>18.</code><span style="margin-left: 0px ! important;"><code>tiles-core-2.0.4</code></span></div>
<div alt1=""><code>19.</code><span style="margin-left: 0px ! important;"><code>tiles-jsp-2.0.4</code></span></div>
</div>
</div>
<p>
You will see how to create a simple classic tile layout with a header, menu, body and footer regions.
</p>
<img src="http://www.vaannila.com/images/spring/spring-tiles-pic-2.gif"  alt="" /><br />
<p>In Spring to use Tiles, configure the following Tile definition in the Spring configuration file.</p>
<div id="highlighter_730903">
<div>
<div alt1=""><code>01.</code><span style="margin-left: 0px ! important;"><code>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"UTF-8"</code><code>?&gt;</code></span></div>
<div alt2=""><code>02.</code><span style="margin-left: 0px ! important;"><code>&lt;</code><code>beans</code> <code>xmlns</code><code>=</code><code>"http://www.springframework.org/schema/beans"</code></span></div>
<div alt1=""><code>03.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>xmlns:xsi</code><code>=</code><code>"http://www.w3.org/2001/XMLSchema-instance"</code></span></div>
<div alt2=""><code>04.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>xmlns:p</code><code>=</code><code>"http://www.springframework.org/schema/p"</code></span></div>
<div alt1=""><code>05.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>xmlns:context</code><code>=</code><code>"http://www.springframework.org/schema/context"</code></span></div>
<div alt2=""><code>06.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>xsi:schemaLocation="</code></span></div>
<div alt1=""><code>07.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>http://www.springframework.org/schema/beans </code></span></div>
<div alt2=""><code>08.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>http://www.springframework.org/schema/beans/spring-beans.xsd</code></span></div>
<div alt1=""><code>09.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>http://www.springframework.org/schema/context </code></span></div>
<div alt2=""><code>10.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>http://www.springframework.org/schema/context/spring-context.xsd"&gt;</code></span></div>
<div alt1=""><code>11.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code>&nbsp;</div>
<div alt2=""><code>12.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"viewResolver"</code> <code>class</code><code>=</code><code>"org.springframework.web.servlet.view. ResourceBundleViewResolver"</code> <code>p:basename</code><code>=</code><code>"views"</code> <code>/&gt;</code></span></div>
<div alt1=""><code>13.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code>&nbsp;</div>
<div alt2=""><code>14.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>&lt;</code><code>context:component-scan</code> <code>base-package</code><code>=</code><code>"com.vaannila.web"</code> <code>/&gt;</code></span></div>
<div alt1=""><code>15.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code>&nbsp;</div>
<div alt2=""><code>16.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 32px ! important;"><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"tilesConfigurer"</code> <code>class</code><code>=</code><code>"org.springframework.web.servlet.view.tiles2. TilesConfigurer"</code> <code>p:definitions</code><code>=</code><code>"/WEB-INF/tiles-defs.xml"</code> <code>/&gt;&nbsp;&nbsp;&nbsp; </code></span></div>
<div alt1=""><code>17.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code>&nbsp;</div>
<div alt2=""><code>18.</code><span style="margin-left: 0px ! important;"><code>&lt;/</code><code>beans</code><code>&gt;</code></span></div>
</div>
</div>
<p>
Using the <em>definitions</em> attribute specify the location of the tiles definition file. Here the location is "<em>/WEB-INF/tiles-defs.xml</em>". The tiles definition file is shown below.
</p>
<div id="highlighter_154970">
<div>
<div alt1=""><code>01.</code><span style="margin-left: 0px ! important;"><code>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"UTF-8"</code> <code>?&gt;</code></span></div>
<div alt2=""><code>02.</code>&nbsp;</div>
<div alt1=""><code>03.</code><span style="margin-left: 0px ! important;"><code>&lt;!DOCTYPE tiles-definitions PUBLIC</code></span></div>
<div alt2=""><code>04.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 56px ! important;"><code>"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"</code></span></div>
<div alt1=""><code>05.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 56px ! important;"><code>"http://tiles.apache.org/dtds/tiles-config_2_0.dtd"&gt;</code></span></div>
<div alt2=""><code>06.</code>&nbsp;</div>
<div alt1=""><code>07.</code><span style="margin-left: 0px ! important;"><code>&lt;</code><code>tiles-definitions</code><code>&gt;</code></span></div>
<div alt2=""><code>08.</code>&nbsp;</div>
<div alt1=""><code>09.</code><code>&nbsp;&nbsp;</code><span style="margin-left: 16px ! important;"><code>&lt;</code><code>definition</code> <code>name</code><code>=</code><code>"baseLayout"</code> <code>template</code><code>=</code><code>"/WEB-INF/tiles/baseLayout.jsp"</code><code>&gt;</code></span></div>
<div alt2=""><code>10.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"title"</code>&nbsp; <code>value</code><code>=</code><code>"Template"</code><code>/&gt;</code></span></div>
<div alt1=""><code>11.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"header"</code> <code>value</code><code>=</code><code>"/WEB-INF/tiles/header.jsp"</code><code>/&gt;</code></span></div>
<div alt2=""><code>12.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"menu"</code>&nbsp;&nbsp; <code>value</code><code>=</code><code>"/WEB-INF/tiles/menu.jsp"</code><code>/&gt;</code></span></div>
<div alt1=""><code>13.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"body"</code>&nbsp;&nbsp; <code>value</code><code>=</code><code>"/WEB-INF/tiles/body.jsp"</code><code>/&gt;</code></span></div>
<div alt2=""><code>14.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"footer"</code>&nbsp;&nbsp; <code>value</code><code>=</code><code>"/WEB-INF/tiles/footer.jsp"</code><code>/&gt;</code></span></div>
<div alt1=""><code>15.</code><code>&nbsp;&nbsp;</code><span style="margin-left: 16px ! important;"><code>&lt;/</code><code>definition</code><code>&gt;</code></span></div>
<div alt2=""><code>16.</code><code>&nbsp;&nbsp;</code>&nbsp;</div>
<div alt1=""><code>17.</code><code>&nbsp;&nbsp;</code><span style="margin-left: 16px ! important;"><code>&lt;</code><code>definition</code> <code>name</code><code>=</code><code>"welcome"</code> <code>extends</code><code>=</code><code>"baseLayout"</code><code>&gt;</code></span></div>
<div alt2=""><code>18.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"title"</code>&nbsp; <code>value</code><code>=</code><code>"Welcome"</code><code>/&gt;</code></span></div>
<div alt1=""><code>19.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"body"</code>&nbsp;&nbsp; <code>value</code><code>=</code><code>"/WEB-INF/jsp/welcome.jsp"</code><code>/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </code></span></div>
<div alt2=""><code>20.</code><code>&nbsp;&nbsp;</code><span style="margin-left: 16px ! important;"><code>&lt;/</code><code>definition</code><code>&gt;</code></span></div>
<div alt1=""><code>21.</code><code>&nbsp;&nbsp;</code>&nbsp;</div>
<div alt2=""><code>22.</code><code>&nbsp;&nbsp;</code><span style="margin-left: 16px ! important;"><code>&lt;</code><code>definition</code> <code>name</code><code>=</code><code>"friends"</code> <code>extends</code><code>=</code><code>"baseLayout"</code><code>&gt;</code></span></div>
<div alt1=""><code>23.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"title"</code>&nbsp; <code>value</code><code>=</code><code>"Friends"</code><code>/&gt;</code></span></div>
<div alt2=""><code>24.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"body"</code>&nbsp;&nbsp; <code>value</code><code>=</code><code>"/WEB-INF/jsp/friends.jsp"</code><code>/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </code></span></div>
<div alt1=""><code>25.</code><code>&nbsp;&nbsp;</code><span style="margin-left: 16px ! important;"><code>&lt;/</code><code>definition</code><code>&gt;</code></span></div>
<div alt2=""><code>26.</code><code>&nbsp;&nbsp;</code>&nbsp;</div>
<div alt1=""><code>27.</code><code>&nbsp;&nbsp;</code><span style="margin-left: 16px ! important;"><code>&lt;</code><code>definition</code> <code>name</code><code>=</code><code>"office"</code> <code>extends</code><code>=</code><code>"baseLayout"</code><code>&gt;</code></span></div>
<div alt2=""><code>28.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"title"</code>&nbsp; <code>value</code><code>=</code><code>"Office"</code><code>/&gt;</code></span></div>
<div alt1=""><code>29.</code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><span style="margin-left: 48px ! important;"><code>&lt;</code><code>put-attribute</code> <code>name</code><code>=</code><code>"body"</code>&nbsp;&nbsp; <code>value</code><code>=</code><code>"/WEB-INF/jsp/office.jsp"</code><code>/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </code></span></div>
<div alt2=""><code>30.</code><code>&nbsp;&nbsp;</code><span style="margin-left: 16px ! important;"><code>&lt;/</code><code>definition</code><code>&gt;</code></span></div>
<div alt1=""><code>31.</code><code>&nbsp;&nbsp;</code>&nbsp;</div>
<div alt2=""><code>32.</code><span style="margin-left: 0px ! important;"><code>&lt;/</code><code>tiles-definitions</code><code>&gt;</code></span></div>
</div>
</div>
<p>Here we first define a base layout, later we extend the base layout
and create three more tile definitions by changing only the title and
the body regions.</p>
<p>To dispaly views we use the <em>ResourceBundleViewResolver</em>. By default the <em>views.properties</em> file will be used to store the key value pairs, we specify this using the <em>basename</em> attribute.</p>
<div id="highlighter_638535">
<div>
<div alt1=""><code>01.</code><span style="margin-left: 0px ! important;"><code>welcome.(</code><code>class</code><code>)=org.springframework.web.servlet.view.tiles2.TilesView</code></span></div>
<div alt2=""><code>02.</code><span style="margin-left: 0px ! important;"><code>welcome.url=welcome</code></span></div>
<div alt1=""><code>03.</code>&nbsp;</div>
<div alt2=""><code>04.</code><span style="margin-left: 0px ! important;"><code>friends.(</code><code>class</code><code>)=org.springframework.web.servlet.view.tiles2.TilesView</code></span></div>
<div alt1=""><code>05.</code><span style="margin-left: 0px ! important;"><code>friends.url=friends</code></span></div>
<div alt2=""><code>06.</code>&nbsp;</div>
<div alt1=""><code>07.</code><span style="margin-left: 0px ! important;"><code>office.(</code><code>class</code><code>)=org.springframework.web.servlet.view.tiles2.TilesView</code></span></div>
<div alt2=""><code>08.</code><span style="margin-left: 0px ! important;"><code>office.url=office</code></span></div>
<div alt1=""><code>09.</code>&nbsp;</div>
<div alt2=""><code>10.</code><span style="margin-left: 0px ! important;"><code>about.(</code><code>class</code><code>)=org.springframework.web.servlet.view.JstlView</code></span></div>
<div alt1=""><code>11.</code><span style="margin-left: 0px ! important;"><code>about.url=/WEB-INF/jsp/about.jsp</code></span></div>
</div>
</div>
<p>The <em>welcome</em>, <em>friends</em> and <em>office</em> refers to the tile definition names (the one to right side of the = sign). For tile views we use "<em>org.springframework.web.servlet.view.tiles2. TilesView</em>" class. You can also have other views together with the tile views. The <em>about</em> url is mapped to the <em>about.jsp</em> page with is a <em>org.springframework.web.servlet.view.JstlView</em>.</p>
<p>The baseLayout.jsp file contains the table structure to hold the different regions.</p>
<pre xml;="" toolbar:=""  false;="" auto-links:false="">&lt;%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %&gt;<br />
&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"<br />
"http://www.w3.org/TR/html4/loose.dtd"&gt;<br />
<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;<br />
&lt;title&gt;&lt;tiles:insertAttribute name="title" ignore="true" /&gt;&lt;/title&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
&lt;table border="1" cellpadding="2" cellspacing="2" align="center"&gt;<br />
&lt;tr&gt;<br />
&lt;td height="30" colspan="2"&gt;<br />
&lt;tiles:insertAttribute name="header" /&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;tr&gt;<br />
&lt;td height="250"&gt;<br />
&lt;tiles:insertAttribute name="menu" /&gt;<br />
&lt;/td&gt;<br />
&lt;td width="350"&gt;<br />
&lt;tiles:insertAttribute name="body" /&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;tr&gt;<br />
&lt;td height="30" colspan="2"&gt;<br />
&lt;tiles:insertAttribute name="footer" /&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;/table&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
<br />
</pre>
<p>
Here we use annotated controller handler mapping to handle the request. In the <em>redirect.jsp</em> page we forward the request to the <em>welcome.htm </em>url.
</p>
<pre xml;="" toolbar:=""  false;="" auto-links:false="">&lt;% response.sendRedirect("welcome.htm"); %&gt;<br />
<br />
</pre>
<p>The <em>welcome.htm</em> url will be handled by the <em>WelcomeController</em> class, where we forward it to the <em>welcome</em> tile page.</p>
<pre java;="" toolbar:=""  false;="">package com.vaannila.web;<br />
<br />
import org.springframework.stereotype.Controller;<br />
import org.springframework.web.bind.annotation.RequestMapping;<br />
<br />
@Controller<br />
public class WelcomeController {<br />
<br />
@RequestMapping("/welcome.htm")<br />
public String redirect()<br />
{<br />
return "welcome";<br />
}<br />
}<br />
<br />
</pre>
<img src="http://www.vaannila.com/images/spring/spring-tiles-pic-3.gif"  alt="" />
<p>
You can download and try the example here.
</p>
<table width="230">
    <tbody>
        <tr>
            <td height="30">
            <div>
            Source  :<a href="http://www.vaannila.com/examples/spring/src/SpringExample16.zip">Download</a></div>
            </td>
        </tr>
        <tr>
            <td height="30">
            <div>
            War :<a href="http://www.vaannila.com/examples/spring/example/SpringExample16.war">Download</a></div>
            </td>
        </tr>
    </tbody>
</table>
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/298846.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-10-19 14:32 <a href="http://www.blogjava.net/sealyu/archive/2009/10/19/298846.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring MVC中的两种验证方式（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/04/20/266487.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 20 Apr 2009 01:21:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/04/20/266487.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/266487.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/04/20/266487.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/266487.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/266487.html</trackback:ping><description><![CDATA[<h2 style="clear: both;">1.&nbsp;Commons Validator</h2>
<div style="margin-left: 0.5in; margin-right: 0.5in;">
<table summary="Important" border="0">
    <tbody>
        <tr>
            <td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="https://springmodules.dev.java.net/docs/reference/0.5/images/admons/important.png" /></td>
            <th align="left">Important</th>
        </tr>
        <tr>
            <td colspan="2" align="left" valign="top">
            <p>
            In version 0.4, the commons validator support was moved from the commons module to the validation module. As a
            consequence, all related classes in the commons module were deprecated and will be removed in version 0.5.
            </p>
            <p>
            Please note that due to this change, the packages were renamed. Apart from that, the functionality stayed more
            or less the same with the exception of some bug fixes and small improvements. Also note that all bug fixes and
            maintenance will be done on the validation module solely.
            </p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>
The Commons Validator is a library that allows you to perform
validation based on rules specified in XML configuration files.
</p>
<p>
TODO: Describe the concepts of Commons Validator in more details.
</p>
<div>
<div>
<div>
<div>
<h3>1.1.&nbsp;Configure an Validator Factory</h3>
</div>
</div>
</div>
<p>
Firstly you need to configure the Validator Factory which is the
factory to get Validator instances. To do so, the support provides the
class DefaultValidatorFactory in the package org.springmodules.validation.commons
</p>
<p>
You need to specify with the property validationConfigLocations the
file containing the Commons Validator rules and the file containing the
validation rules specific to the application.
</p>
<p>
The following code shows how to configure this factory.
</p>
<pre>&lt;bean id="validatorFactory"<br />
class="org.springmodules.validation.commons.DefaultValidatorFactory"&gt;<br />
&lt;property name="validationConfigLocations"&gt;<br />
&lt;list&gt;<br />
&lt;value&gt;/WEB-INF/validator-rules.xml&lt;/value&gt;<br />
&lt;value&gt;/WEB-INF/validation.xml&lt;/value&gt;<br />
&lt;/list&gt;<br />
&lt;/property&gt;<br />
&lt;/bean&gt;</pre>
</div>
<div>
<div>
<div>
<div>
<h3>1.2.&nbsp;Use a dedicated validation-rules.xml</h3>
</div>
</div>
</div>
<p>
The file <em>validation-rules.xml</em> must contain
Commons Validator elements based on classes provided by the support of
this framework in Spring Modules.
</p>
<p>
For example, the configuration of the entities "required" and
"requiredif" must be now in the <em>validation-rules.xml</em>
file.
</p>
<pre>&lt;validator name="required"<br />
classname="org.springmodules.validation.commons.FieldChecks"<br />
method="validateRequired"<br />
methodParams="java.lang.Object,<br />
org.apache.commons.validator.ValidatorAction,<br />
org.apache.commons.validator.Field,<br />
org.springframework.validation.Errors"<br />
msg="errors.required"&gt;<br />
<br />
&lt;javascript&gt;&lt;![CDATA[<br />
(...)<br />
]]&gt;&lt;/javascript&gt;<br />
&lt;/validator&gt;<br />
<br />
&lt;validator name="requiredif"<br />
classname="org.springmodules.validation.commons.FieldChecks"<br />
method="validateRequiredIf"<br />
methodParams="java.lang.Object,<br />
org.apache.commons.validator.ValidatorAction,<br />
org.apache.commons.validator.Field,<br />
org.springframework.validation.Errors,<br />
org.apache.commons.validator.Validator"<br />
msg="errors.required"&gt;<br />
&lt;/validator&gt;</pre>
<p>
The validation sample of the distribution provides a complete
<em>validation-rules.xml</em> based on the classes of the
support.
</p>
<p>
You must note that the support of <em>validwhen</em> is
not provided at the moment in the support. However, some codes are
provides in JIRA. For more informations, see the issues
<a href="http://opensource2.atlassian.com/projects/spring/browse/MOD-38" target="_top">MOD-38</a> and
<a href="http://opensource2.atlassian.com/projects/spring/browse/MOD-49" target="_top">MOD-49</a>.
</p>
</div>
<div>
<div>
<div>
<div>
<h3>1.3.&nbsp;Configure a Commons Validator</h3>
</div>
</div>
</div>
<p>
Then you need to configure the Validator itself basing the previous
Validator Factory. It corresponds to an adapter in order to hide Commons
Validator behind a Spring Validator.
</p>
<p>
The following code shows how to configure this validator.
</p>
<pre>&lt;bean id="beanValidator" class="org.springmodules.validation.commons.DefaultBeanValidator"&gt;<br />
&lt;property name="validatorFactory" ref="validatorFactory"/&gt;<br />
&lt;/bean&gt;</pre>
</div>
<div>
<div>
<div>
<div>
<h3>1.4.&nbsp;Server side validation</h3>
</div>
</div>
</div>
<p>
Spring MVC provides the implementation <em>SimpleFormController</em>
of the interface <em>Controller</em> in order to process HTML forms.
It allows a validation of informations processing by the controller by using the
property v<em>alidator</em> of the controller. In the case of
Commons Validator, this property must be set with the bean <em>beanValidator</em>
previously configured.
</p>
<p>
The following code shows how to configure a controller which validates a form on
the server side using the support of Commons Validator.
</p>
<pre>&lt;bean id="myFormController" class="org.springmodules.sample.MyFormController"&gt;<br />
(...)<br />
&lt;property name="validator" ref="beanValidator"/&gt;<br />
&lt;property name="commandName" value="myForm"/&gt;<br />
&lt;property name="commandClass" value="org.springmodules.sample.MyForm"/&gt;<br />
(...)<br />
&lt;/bean&gt;</pre>
<p>
The <em>beanValidator</em> bean uses the value of the property
<em>commandClass</em> of the controller to select the name of
the form tag in the <em>validation.xml</em> file. The configuration
is not based on the <em>commandName</em> property. For example, with
the class name <em>org.springmodules.sample.MyForm</em>,
Commons Validator must contain a form tag with <em>myForm</em>
as value of the name property. The following code shows the contents of this file.
</p>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />
&lt;!DOCTYPE form-validation PUBLIC<br />
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN"<br />
"http://jakarta.apache.org/commons/dtds/validator_1_1.dtd"&gt;<br />
<br />
&lt;form-validation&gt;<br />
&lt;formset&gt;<br />
&lt;form name="myForm"&gt;<br />
&lt;field property="field1" depends="required"&gt;<br />
&lt;arg0 key="error.field1" /&gt;<br />
&lt;/field&gt;<br />
&lt;field property="field2" depends="email"&gt;<br />
&lt;arg0 key="error.field2" /&gt;<br />
&lt;/field&gt;<br />
&lt;/form&gt;<br />
&lt;/formset&gt;<br />
&lt;/form-validation&gt;</pre>
</div>
<div>
<div>
<div>
<div>
<h3>1.5.&nbsp;Partial Bean Validation Support</h3>
</div>
</div>
</div>
<p>
Partial validation support enables partial validation of beans where not all properties are validated
but only selected ones.
</p>
<p>
Commons validator enables partial validation by specifying the <em>page</em> attribute
for each field in the form configuration:
</p>
<pre>&lt;form name="personDataWizard"&gt;<br />
&lt;field property="firstName" depends="required" page="0"&gt;<br />
&lt;arg0 key="person.first.name" /&gt;<br />
&lt;/field&gt;<br />
&lt;field property="lastName" depends="required" page="0"&gt;<br />
&lt;arg0 key="person.last.name" /&gt;<br />
&lt;/field&gt;<br />
&lt;field property="email" depends="required,email" page="0"&gt;<br />
&lt;arg0 key="person.email" /&gt;<br />
&lt;/field&gt;<br />
&lt;field property="password" depends="required" page="1"&gt;<br />
&lt;arg0 key="person.password" /&gt;<br />
&lt;/field&gt;<br />
&lt;field property="verifyPassword" depends="validwhen" page="1"&gt;<br />
&lt;arg0 key="person.password.not.matching" /&gt;<br />
&lt;var&gt;<br />
&lt;var-name&gt;test&lt;/var-name&gt;<br />
&lt;var-value&gt;(*this* == password)&lt;/var-value&gt;<br />
&lt;/var&gt;<br />
&lt;/field&gt;<br />
&lt;/form&gt;</pre>
<p>
The <em>org.springmodules.validation.commons.ConfigurablePageBeanValidator</em> and
<em>org.springmodules.validation.commons.DefaultPageBeanValidator</em> classes support
partial validation by setting their <em>page</em> property. The value of this property will
be matched with the page attribute in the form configuration, and only the fields with the appropriate
page configured will be validated.
</p>
<p>
The following is an example of a partial validation support usage within a wizard controller:
</p>
<pre>&lt;bean id="personWizardController" class="PersonWizardController"&gt;<br />
&lt;property name="pages"&gt;<br />
&lt;list&gt;<br />
&lt;value&gt;personPage0&lt;/value&gt;<br />
&lt;value&gt;personPage1&lt;/value&gt;<br />
&lt;/list&gt;<br />
&lt;/property&gt;<br />
&lt;property name="allowDirtyForward" value="false"/&gt;<br />
&lt;property name="validators"&gt;<br />
&lt;list&gt;<br />
&lt;ref bean="pageValidator0"/&gt;<br />
&lt;ref bean="pageValidator1"/&gt;<br />
&lt;/list&gt;<br />
&lt;/property&gt;<br />
&lt;property name="commandName" value="person"/&gt;<br />
&lt;property name="commandClass" value="PersonData"/&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;bean id="pageValidator0" class="ConfigurablePageBeanValidator" parent="pageValidator"&gt;<br />
&lt;property name="page" value="0"/&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;bean id="pageValidator1" class="ConfigurablePageBeanValidator" parent="pageValidator"&gt;<br />
&lt;property name="page" value="1"/&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;bean id="pageValidator" abstract="true"&gt;<br />
&lt;property name="formName" value="personDataWizard"/&gt;<br />
&lt;property name="validatorFactory" ref="validatorFactory"/&gt;<br />
&lt;/bean&gt;<br />
<br />
...<br />
<br />
</pre>
<p>
The controller will look like this:
</p>
<pre>public class PersonWizardController extends AbstractWizardFormController {<br />
<br />
...<br />
<br />
protected void validatePage(Object command, Errors errors, int page) {<br />
Validator[] validators = getValidators();<br />
for (int i=0; i&lt;validators.length; i++) {<br />
Validator validator = validators[i];<br />
if (validator instanceof PageAware) {<br />
if (((PageAware)validator).getPage() == page) {<br />
validator.validate(command, errors);<br />
}<br />
}<br />
}<br />
}<br />
}</pre>
</div>
<div>
<div>
<div>
<div>
<h3>1.6.&nbsp;Client side validation</h3>
</div>
</div>
</div>
<p>
The support of Commons Validator in Spring Modules provides too the
possibility to use a client side validation. It provides a dedicated
taglib to generate the validation javascript code. To use this taglib, we
firstly need to declare it at the beginnig of JSP files as following.
</p>
<pre>&lt;%@ tglib uri="http://www.springmodules.org/tags/commons-validator" prefix="validator" %&gt;</pre>
<p>
You need then to include the generated javascript code in the JSP file as
following by using the <em>javascript</em> tag.
</p>
<pre>&lt;validator:javascript formName="account"<br />
staticJavascript="false" xhtml="true" cdata="false"/&gt;</pre>
<p>
At last, you need to set the <em>onSubmit</em> attribute
on the <em>form</em> tag in order to trigger the validation on
the submission of the form.
</p>
<pre>&lt;form method="post" action="(...)" onsubmit="return validateMyForm(this)"&gt;</pre>
</div>
<div>
<div>
<div>
<div>
<h2 style="clear: both;">2.&nbsp;Valang</h2>
</div>
</div>
</div>
<p>
Valang (<em>Va</em>-lidation <em>Lang</em>-uage), provides a simple and
intuitive way for creating spring validators. It was initially create with three goals in mind:
</p>
<div>
<ul type="disc">
    <li>
    <p>
    Enables writing validation rules quickly, without the need of writing classes or even any java code.
    </p>
    </li>
    <li>
    <p>
    Ease the use of Spring validation tools.
    </p>
    </li>
    <li>
    <p>
    Make validation rules compact, readable and easily maintainable.
    </p>
    </li>
</ul>
</div>
<p>
Valang is built upon two major constructs - The valang expression
language and valang validators. The former is a generic boolean
expression language that enables expressing boolean rule in a "natural
language"-like fashion. The later is a concrete implementation of the
Spring <em>Validator</em> interface that is built around the expression
language.
</p>
<p>
Before going into details, lets first have a look at a small example, just to have
an idea of what valang is and how it can be used. For this example, we'll assume a <em>Person</em> class
with two properties - firstName and lastName. In addition, there are two main validation rules that
need to be applied:
</p>
<div>
<ul type="disc">
    <li>
    <p>
    The first name of the person must be shorter than 30 characters.
    </p>
    </li>
    <li>
    <p>
    The last name of the person must be shorter than 50 characters.
    </p>
    </li>
</ul>
</div>
<p>
One way of applying these validation rules (and currently the most common one) is to implement the <em>Validator</em>
interface specifically for the <em>Person</em> class:
</p>
<pre>public class PersonValidator implements Validator {<br />
<br />
public boolean supports(Class aClass) {<br />
return Person.class.equals(aClass);<br />
}<br />
<br />
public void validate(Object person, Errors errors) {<br />
String firstName = ((Person)person).getFirstNam();<br />
String lastName = ((Person)person).getLastName();<br />
if (firstName == null || firstName.length() &gt;= 30) {<br />
errors.reject("first_name_length", new Object[] { new Integer(30) },<br />
"First name must be shorter than 30");<br />
}<br />
if (lastName == null || lastName.length() &gt;= 50) {<br />
errors.reject("last_name_length", new Object[] { new Integer(50) },<br />
"Last name must be shorter than 50");<br />
}<br />
}<br />
}</pre>
<p>
While this is a perfectly valid approach, it has its downsides. First,
it is quite verbose and time consuming - quite a lot of code to write
just for two very simple validation rules. Second, it required an
additional class which clutters the code (in case it is an inner-class)
or the design - just imagine having a validator class for each of the
domain model objects in the application. </p>
<p>
The following code snippet shows how to create a valang validator to apply the same rules as above:
</p>
<pre>&lt;bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator"&gt;<br />
&lt;property name="valang"&gt;<br />
&lt;value&gt;<br />
&lt;![CDATA[<br />
{ firstName : length(?) &lt; 30 : 'First name too long' : 'first_name_length' : 30}<br />
{ lastName : length(?) &lt; 50 : 'Last name too long' : 'last_name_length' : 50 }<br />
]]&gt;<br />
&lt;/value&gt;<br />
&lt;/property&gt;<br />
&lt;/bean&gt;</pre>
<p>
There are a few things to notice here. First, no new class is created -
with valang, one can reuse a predefined validator class (as shown
here). Second, This validator is not part of the java code, but put in
the application context instead - In the above case, the <em>ValangValidator</em>
is instantiated and can be injected to other objects in the system.
Last but not least, The validation rules are defined using the valang
expression language which is very simple and quick to define. </p>
<p>
The following two sections will elaborate on the expression language and the use of the Valang validator in greater details.
</p>
<div>
<div>
<div>
<div>
<h3>2.1.&nbsp;Valang Syntax</h3>
</div>
</div>
</div>
<p>
The valang syntax is based on the valang expression language and the
valang validation rule configuration. As mentioned above, the former is
a boolean expression language by which the validation rules predicates
(conditions) are expressed. The later binds the rule predicates to a
key (usually a bean property), error message, and optionally error code
and arguments. </p>
<div>
<div>
<div>
<div>
<h4>2.1.1.&nbsp;Rule Configuration</h4>
</div>
</div>
</div>
<p>
Here is the basic structure of the valang rule configuration:
</p>
<pre>{ &lt;key&gt; : &lt;predicate_expression&gt; : &lt;message&gt; [: &lt;error_code&gt; [: &lt;args&gt; ]] }</pre>
<div>
<ul type="disc">
    <li>
    <p>&lt;key&gt; - The key to which the validation error will be bound to. <em>(mandatory)</em></p>
    </li>
    <li>
    <p>&lt;predicate_expression&gt; - A valang expression that defines the predicate (condition) of the validation rule. <em>(mandatory)</em></p>
    </li>
    <li>
    <p>
    &lt;message&gt; - The error message of the validation rule. The message
    is mandatory but can be an empty string if not used. This message is
    also used as the default message in case the error code could not be
    resolved. <em>(mandatory)</em>
    </p>
    </li>
    <li>
    <p>&lt;error_code&gt; - An error code that represents the validation error. Used to support <em>i18n</em>. <em>(optional)</em></p>
    </li>
    <li>
    <p>
    &lt;args&gt; - A comma separated list of arguments to associate with
    the error code. When error codes are resolved, this arguments may be
    used in the resolved message. <em>(optional)</em>
    </p>
    </li>
</ul>
</div>
</div>
<div>
<div>
<div>
<div>
<h4>2.1.2.&nbsp;Expression Language</h4>
</div>
</div>
</div>
<p>
As mentioned, the valang expression language is used to define the
predicate to be associated with the validation rule. The expression is
always evaluated against a context bean. The expression can be defined
as follows: </p>
<pre>&lt;expression&gt; ::= &lt;expression&gt; ( ( "AND" | "OR" ) &lt;expression&gt; )+ | &lt;predicate&gt;</pre>
<p>
The &lt;predicate&gt; in an evaluation that is composed of operators,
literals, bean properties, functions, and mathematical expressions. </p>
<div>
<div>
<div>
<div>
<h5>Operators</h5>
</div>
</div>
</div>
<p>
The following are the supported operators:
</p>
<div>
<ul compact="compact" type="disc">
    <li style="list-style-type: disc;">
    <p>Binary Operators:</p>
    <div>
    <ul compact="compact" type="opencircle">
        <li style="list-style-type: circle;">
        <p>String, boolean, date and number operators:</p>
        <div>
        <ul compact="compact" type="disc">
            <li>
            <p>= | == | IS | EQUALS</p>
            </li>
            <li>
            <p>!= | &lt;&gt; | &gt;&lt; | IS NOT | NOT EQUALS</p>
            </li>
        </ul>
        </div>
        </li>
        <li style="list-style-type: circle;">
        <p>Number and date operators:</p>
        <div>
        <ul compact="compact" type="disc">
            <li>
            <p>&gt; | GREATER THAN | IS GREATER THAN</p>
            </li>
            <li>
            <p>&lt; | LESS THAN | IS LESS THAN</p>
            </li>
            <li>
            <p>&gt;= | =&gt; | GREATER THAN OR EQUALS | IS GREATER THAN OR EQUALS</p>
            </li>
            <li>
            <p>&lt;= | =&lt; | LESS THAN OR EQUALS | IS LESS THAN OR EQUALS</p>
            </li>
        </ul>
        </div>
        </li>
    </ul>
    </div>
    </li>
    <li style="list-style-type: disc;">
    <p>Unary Operators:</p>
    <div>
    <ul compact="compact" type="opencircle">
        <li style="list-style-type: circle;">
        <p>Object operators:</p>
        <div>
        <ul compact="compact" type="disc">
            <li>
            <p>NULL | IS NULL</p>
            </li>
            <li>
            <p>NOT NULL | IS NOT NULL</p>
            </li>
        </ul>
        </div>
        </li>
        <li style="list-style-type: circle;">
        <p>String operators:</p>
        <div>
        <ul compact="compact" type="disc">
            <li>
            <p>HAS TEXT</p>
            </li>
            <li>
            <p>HAS NO TEXT</p>
            </li>
            <li>
            <p>HAS LENGTH</p>
            </li>
            <li>
            <p>HAS NO LENGTH</p>
            </li>
            <li>
            <p>IS BLANK</p>
            </li>
            <li>
            <p>IS NOT BLANK</p>
            </li>
            <li>
            <p>IS UPPERCASE | IS UPPER CASE | IS UPPER</p>
            </li>
            <li>
            <p>IS NOT UPPERCASE | IS NOT UPPER CASE | IS NOT UPPER</p>
            </li>
            <li>
            <p>IS LOWERCASE | IS LOWER CASE | IS LOWER</p>
            </li>
            <li>
            <p>IS NOT LOWERCASE | IS NOT LOWER CASE | IS NOT LOWER</p>
            </li>
            <li>
            <p>IS WORD</p>
            </li>
            <li>
            <p>IS NOT WORD</p>
            </li>
        </ul>
        </div>
        </li>
    </ul>
    </div>
    </li>
    <li style="list-style-type: disc;">
    <p>Special Operators:</p>
    <div>
    <ul compact="compact" type="opencircle">
        <li style="list-style-type: circle;">
        <p>BETWEEN</p>
        </li>
        <li style="list-style-type: circle;">
        <p>NOT BETWEEN</p>
        </li>
        <li style="list-style-type: circle;">
        <p>IN</p>
        </li>
        <li style="list-style-type: circle;">
        <p>NOT IN</p>
        </li>
        <li style="list-style-type: circle;">
        <p>NOT</p>
        </li>
    </ul>
    </div>
    </li>
</ul>
</div>
<p>
These operators are case insensitive. Binary operators have a left and a right side.
Unary operators only have a left side.
</p>
<p>
Value types on both sides of the binary operators must always match. The following expressions will
throw an exception:
</p>
<pre>name &gt; 0<br />
age == 'some string'</pre>
</div>
<div>
<div>
<div>
<div>
<h5>BETWEEN / NOT BETWEEN Operators</h5>
</div>
</div>
</div>
<p>
The BETWEEN and NOT BETWEEN operators have the following special syntax:
</p>
<pre>&lt;between_operator&gt; ::= &lt;left_side&gt; BETWEEN &lt;value&gt; AND &lt;value&gt;<br />
&lt;not_between_operator&gt; ::= &lt;left_side&gt; NOT BETWEEN &lt;value&gt; AND &lt;value&gt;</pre>
<p>
Both the left side and the values can be any valid combination of literals, bean properties,
functions and mathematical operations.
</p>
<p>Examples:</p>
<pre>width between 10 and 90<br />
length(name) between minLength and maxLength</pre>
</div>
<div>
<div>
<div>
<div>
<h5>IN / NOT IN Operators</h5>
</div>
</div>
</div>
<p>
The IN and NOT IN operators have the following special syntax:
</p>
<pre>&lt;in_operator&gt; ::= &lt;left_side&gt; IN &lt;value&gt; ( "," &lt;value&gt; )*<br />
&lt;not_in_operator&gt; ::= &lt;left_side&gt; NOT IN &lt;value&gt; ( "," &lt;value&gt; )*</pre>
<p>
Both the left side and the values can be any valid combination of literals, bean properties,
functions and mathematical operations.
</p>
<p>
There's another special syntax where a <em>java.util.Collection</em>, <em>java.util.Enumeration</em>,
<em>java.util.Iterator</em>
or object array instance can be retrieved from a bean property. These
values are then used as right side of the operator. This feature
enables to create dynamic sets of values based on other properties of
the bean. </p>
<pre>&lt;special_in_operator&gt; ::= &lt;left_side&gt; IN "@"&lt;bean_property&gt;<br />
&lt;special_not_in_operator&gt; ::= &lt;left_side&gt; NOT IN "@"&lt;bean_property&gt;</pre>
<p>Examples:</p>
<pre>size in 'S', 'M', 'L', 'XL'<br />
size in @sizes</pre>
</div>
<div>
<div>
<div>
<div>
<h5>NOT Operator</h5>
</div>
</div>
</div>
<p>
The not operator has the following special syntax:
</p>
<pre>&lt;not_operator&gt; ::= "NOT" &lt;expression&gt;</pre>
<p>
This operator inverses the result of one or a set of predicates.
</p>
</div>
<div>
<div>
<div>
<div>
<h5>Literals</h5>
</div>
</div>
</div>
<p>
Four type of literals are supported by valang: <em>string</em>,
<em>number</em>, <em>date</em>, and <em>boolean</em>.
</p>
<p>
Strings are quoted with single quotes:
</p>
<pre>'Bill', 'George', 'Junior'</pre>
<p>
Number literals are unquoted and are parsed by <em>java.math.BigDecimal</em>:
</p>
<pre>0.70, 1, 2000, -3.14</pre>
<p>
Date literals are delimited with square brackets and are parsed upon each evaluation by a special date parser.
[TODO: write documentation for date parser]
</p>
<pre>[T&lt;d], [2005-05-28]</pre>
<p>
Boolean literals are not quoted and have the following form:
</p>
<pre>&lt;boolean&gt; ::= ( "TRUE" | "YES" | "FALSE" | "NO" )</pre>
</div>
<div>
<div>
<div>
<div>
<h5>Bean Properties</h5>
</div>
</div>
</div>
<p>
As mentioned above, the valang always evaluates the expressions against a context bean. Once can
access this bean's properties directly within the expression. To better understand how this works
lets assume a Person class with the following properties:
</p>
<div>
<ul compact="compact" type="disc">
    <li>
    <p>name (String)</p>
    </li>
    <li>
    <p>address (Address)</p>
    </li>
    <li>
    <p>specialFriends (Map&lt;String, Object&gt;)</p>
    </li>
    <li>
    <p>friends (Person[])</p>
    </li>
    <li>
    <p>enemies (List&lt;Person&gt;)</p>
    </li>
</ul>
</div>
<p>
The Address class has the following properties:
</p>
<div>
<ul compact="compact" type="disc">
    <li>
    <p>street (String)</p>
    </li>
    <li>
    <p>city (String)</p>
    </li>
    <li>
    <p>Country (String)</p>
    </li>
</ul>
</div>
<p>
The context bean properties can be accessed directly by using their names:
</p>
<pre>name, address, attributes</pre>
<p>
Accessing nested properties is also supported by using a dot-separated expression. For example,
accessing the street of the person can be done as follows:
</p>
<pre>address.street</pre>
<p>
List and/or array elements can be access by their index number as follows:
</p>
<pre>friends[1].name<br />
enemies[0].address.city</pre>
<p>
Map entries can also be accessed by their keys:
</p>
<pre>specialFriends[bestFriend].name</pre>
</div>
<div>
<div>
<div>
<div>
<h5>Functions</h5>
</div>
</div>
</div>
<p>
Valang expressions can contain functions. A function is basically an
operation which accepts arguments and returns a result. Functions can
accept one or more arguments where each may be either a literal, bean
property, or a function as described in the following definition: </p>
<pre>function ::= &lt;function_name&gt; "(" &lt;arg&gt; [ "," &lt;arg&gt; ]* ")"<br />
&lt;arg&gt; ::= &lt;literal&gt; | &lt;bean_property&gt; | &lt;function&gt;</pre>
<p>
Valang ships with the following predefined functions:
</p>
<div>
<p><strong>Table&nbsp;11.1.&nbsp;Functions</strong></p>
<table summary="Functions" border="1">
    <colgroup><col align="left"><col align="left"></colgroup>
    <thead>
        <tr>
            <th align="left">Name</th><th align="left">Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td align="left">length</td>
            <td align="left">
            Returns the size of the passed in collection or array. If the passed in argument
            is neither, the length of the string returned from the <em>toString()</em> call on
            the passed in argument.
            </td>
        </tr>
        <tr>
            <td align="left">len</td>
            <td align="left">See <em>length</em> above</td>
        </tr>
        <tr>
            <td align="left">size</td>
            <td align="left">See <em>length</em> above</td>
        </tr>
        <tr>
            <td align="left">count</td>
            <td align="left">See <em>length</em> above</td>
        </tr>
        <tr>
            <td align="left">match</td>
            <td align="left">
            Matches the given regular expression (first argument) to the
            string returned from the <em>toString()</em> call on the passed
            in value (second argument).
            </td>
        </tr>
        <tr>
            <td align="left">matches</td>
            <td align="left">See <em>match</em> above.</td>
        </tr>
        <tr>
            <td align="left">email</td>
            <td align="left">
            Returns <em>true</em> if the string returned from the
            <em>toString()</em> call on the passed in argument represents
            a valid email
            </td>
        </tr>
        <tr>
            <td align="left">upper</td>
            <td align="left">
            Converts the string returned from the <em>toString()</em> call on the argument
            to upper case.
            </td>
        </tr>
        <tr>
            <td align="left">lower</td>
            <td align="left">
            Converts the string returned from the <em>toString()</em> call on the argument
            to lower case.
            </td>
        </tr>
        <tr>
            <td align="left">!</td>
            <td align="left">Not operation on a boolean value.</td>
        </tr>
        <tr>
            <td align="left">resolve</td>
            <td align="left">Wrap string in <em>org.springframework.context.support.DefaultMessageSourceResolvable</em>.</td>
        </tr>
        <tr>
            <td align="left">inRole</td>
            <td align="left">
            Accepts a role name as an argument and returns <em>true</em> if the current user has this role.
            This function uses <em>Acegi</em> to fetch the current user.
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>
Examples:
</p>
<pre>length(?)<br />
size(upper('test'))<br />
upper(address.city)</pre>
<p>
One of the more powerful features in Valang expression language is that it is extensible with custom
functions. To add a custom function one first needs to implement the
<em>org.springmodules.validation.valang.functions.Function</em> interface or extend the
<em>org.springmodules.validation.valang.functions.AbstractFunction</em>. Then, when using
the <em>ValangValidatorFactoryBean</em> or <em>ValangValidator</em>,
register the new function with the <em>customFunctions</em> property using the
function name as the key.
[TODO: show an example of a custom function]
</p>
</div>
<div>
<div>
<div>
<div>
<h5>Mathematical Expressions</h5>
</div>
</div>
</div>
<p>
The following mathematical operators are supported:
</p>
<div>
<ul compact="compact" type="disc">
    <li>
    <p>+</p>
    </li>
    <li>
    <p>-</p>
    </li>
    <li>
    <p>*</p>
    </li>
    <li>
    <p>/ | div</p>
    </li>
    <li>
    <p>% | mod</p>
    </li>
</ul>
</div>
<p>
Parentheses are supported and expression are parsed left to right so that
</p>
<pre>2 - 3 + 5 = 4</pre>
<p>
Values in the mathematical expression can be literals, bean properties, and functions.
</p>
<p>
Examples:
</p>
<pre>(2 * (15 - 3) + ( 20 / 5 ) ) * -1<br />
(22 / 7) - (22 div 7)<br />
10 % 3<br />
length(?) mod 4</pre>
</div>
</div>
</div>
<div>
<div>
<div>
<div>
<h3>2.2.&nbsp;Valang Validator Support</h3>
</div>
</div>
</div>
<p>
As we saw in the previous chapter, Valang offers quite a reach and powerful expression language to
represent the validation rules. Language that for most cases relieves the user from creating custom
Validator classes.
</p>
<p>
The only missing piece of the puzzle now is to see how this expression language and the validation rule
configuration integrate with Spring validation support.
</p>
<p>
The 2 most important constructs of Spring validation are the
<em>org.springframework.validation.Validator</em> and <em>org.springframework.validation.Errors</em>
classes. The <em>Errors</em> class serves as a registry for validation errors that are associated with
an object (a.k.a the target object). The <em>Validator</em> interface provides a mechanism to validate objects
and register the various validation error within the passed in <em>Errors</em>.
</p>
<p>
Valang ships with some support classes that leverage the power of the Valang expression language and validation
rule configuration, and integrates nicely with Spring validation. The most important of them all is the
<em>org.springmodules.validation.valang.ValangValidator</em> class.
</p>
<div>
<div>
<div>
<div>
<h4>2.2.1.&nbsp;ValangValidator</h4>
</div>
</div>
</div>
<p>
The <em>org.springmodules.validation.valang.ValangValidator</em> class is a concrete
implementation of Spring's <em>Validator</em> interface. The most important property of
this validator is the <em>valang</em> property.
</p>
<p>
The <em>valang</em> property is of type <em>java.lang.String</em> and holds a
textual representation of the validation rules that are applied by the validator. We saw in the previous
section that a single validation rule is represented in valang using the following format:
</p>
<pre>{ &lt;key&gt; : &lt;predicate_expression&gt; : &lt;message&gt; [: &lt;error_code&gt; [: &lt;args&gt; ]] }</pre>
<p>
Since, a validator may apply more then just one rule, the <em>valang</em> property accepts
a set of such rule definitions.
</p>
<p>
Example:
</p>
<pre>{ firstName : length(?) &lt; 30 : 'First name too long' : 'first_name_length' : 30}<br />
{ lastName : length(?) &lt; 50 : 'Last name too long' : 'last_name_length' : 50 }</pre>
<p>
There are two ways to use the valang validator. It can be explicitly instantiated and initialized with
the rule definitions by calling the <em>setValang(String)</em> method on it. But the recommended
way is actually to let the Spring IoC container do this job for you. The valang validator was design
as a POJO specifically for that reason - to easily define it within Spring application context and inject
it to all other dependent objects in the application.
</p>
<p>
Here is an example of how to define a simple valang validator within the application context:
</p>
<pre>&lt;bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator"&gt;<br />
&lt;property name="valang"&gt;<br />
&lt;value&gt;<br />
&lt;![CDATA[<br />
{ firstName : length(?) &lt; 30 : 'First name too long' : 'first_name_length' : 30}<br />
{ lastName : length(?) &lt; 50 : 'Last name too long' : 'last_name_length' : 50 }<br />
]]&gt;<br />
&lt;/value&gt;<br />
&lt;/property&gt;<br />
&lt;/bean&gt;</pre>
<p>
This validator defines two validation rules - one for the maximum size of the first name of the person and
the other for the maximum size of the last name of the person.
</p>
<p>
Also notice that the above validator is unaware of the object type it validates. The valag validator is
not restricted to a specific class to be validated. It will always apply the defined validation rules as
long as the validated object has the validated properties (firstName and lastName in this case).
</p>
<p>
This configuration should be enough for most cases. But there are some
cases in which you need to apply extra configuration. With <em>ValangValidator</em> it is possible to register custom
function (thus, extend the valang expression language). This can be done by registering the functions within
the <em>customFunctions</em> property, where the function name serves as the registration key.
</p>
<p>
Here is an example of a valang validator configuration with a custom function:
</p>
<pre>&lt;bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator"&gt;<br />
&lt;property name="customFunctions"&gt;<br />
&lt;map&gt;<br />
&lt;entry key="doIt"&gt;<br />
&lt;value&gt;org.springmodules.validation.valang.functions.DoItFunction&lt;/value&gt;<br />
&lt;/entry&gt;<br />
&lt;/map&gt;<br />
&lt;/property&gt;<br />
&lt;property name="valang"&gt;<br />
&lt;value&gt;<br />
&lt;![CDATA[<br />
{ firstName : doIt(?) and length(?) &lt; 30 : 'First name too long' : 'first_name_length' : 30}<br />
{ lastName : length(?) &lt; 50 : 'Last name too long' : 'last_name_length' : 50 }<br />
]]&gt;<br />
&lt;/value&gt;<br />
&lt;/property&gt;<br />
&lt;/bean&gt;</pre>
<p>
It is also possible to register extra property editors and custom date parsers for valang to use. For
more details about valang validator configuration options, please refer to the class javadoc.
</p>
<p>
<span><em>
NOTE: Until version 0.3 the org.springmodules.validation.valang.ValangValidatorFactoryBean
class served the same purpose as the ValangValidator. In version 0.4, this class was deprecated
and is planned to be removed in version 0.5.
</em></span>
</p>
</div>
</div>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/266487.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-04-20 09:21 <a href="http://www.blogjava.net/sealyu/archive/2009/04/20/266487.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Valang Validator 攻略(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/04/20/266483.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 20 Apr 2009 01:13:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/04/20/266483.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/266483.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/04/20/266483.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/266483.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/266483.html</trackback:ping><description><![CDATA[<table align="right" border="0" cellpadding="0" cellspacing="0" width="160">
    <tbody>
        <tr>
            <td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10" /></td>
            <td>
            <table border="0" cellpadding="0" cellspacing="0" width="150">
                <tbody>
                    <tr>
                        <td>文档选项</td>
                    </tr>
                </tbody>
            </table>
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td width="150"><noscript>
                        <tr
                        valign="top">
                        <td width="8"><img alt="" height="1" width="8"
                        src="//www.ibm.com/i/c.gif"/ /></td>
                        <td width="16"><img alt="" width="16"
                        height="16" src="//www.ibm.com/i/c.gif"/ /></td>
                        <td>
                        <p><span>未显示需要 JavaScript
                        的文档选项</span></p>
                        </td>
                    </tr>
                    </noscript>
                    <table border="0" cellpadding="0" cellspacing="0" width="143">
                        <script language="JavaScript" type="text/javascript">
                        <!--
                        document.write('
                        <tr valign="top">
                            <td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/ /></td>
                            <td width="16"><img alt="将打印机的版面设置成横向打印模式" height="16" src="//www.ibm.com/i/v14/icons/printer.gif" width="16" vspace="3" /></td>
                            <td width="122">
                            <p><strong><a href="javascript:print()">打印本页</a></strong></p>
                            </td>
                        </tr>
                        ');
                        //-->
                        </script>
                        <tbody>
                            <tr valign="top">
                                <td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8" /></td>
                                <td width="16"><img alt="将打印机的版面设置成横向打印模式" src="http://www.ibm.com/i/v14/icons/printer.gif" height="16" vspace="3" width="16" /></td>
                                <td width="122">
                                <p><strong><a href="javascript:print()">打印本页</a></strong></p>
                                </td>
                            </tr>
                            <input value="Valang 是 Validation Language 的缩写，Valang Validator 的字面含义就是&#8220;使用验证语言的验证器&#8221;，是一种支持声明的验证器。本文详细的介绍了 ValangValidator 的使用和相关所需的配置。作为一种新型的验证工具，它提供的验证语言（valang）具有简单、易学，易扩展等特点。" name="body" type="hidden" /><input value="Valang Validator 攻略" name="subject" type="hidden" /><input value="cn" name="lang" type="hidden" />
                            <script language="JavaScript" type="text/javascript">
                            <!--
                            document.write('
                            <tr valign="top">
                                <td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/ /></td>
                                <td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td>
                                <td width="122">
                                <p><a href="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p>
                                </td>
                            </tr>
                            ');
                            //-->
                            </script>
                            <tr valign="top">
                                <td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8" /></td>
                                <td width="16"><img src="http://www.ibm.com/i/v14/icons/em.gif" alt="将此页作为电子邮件发送" height="16" vspace="3" width="16" /></td>
                                <td width="122">
                                <p><a href="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br />
        </td>
    </tr>
</tbody>
</table>
<p>级别： 初级</p>
<p><a href="http://www.ibm.com/developerworks/cn/opensource/os-lo-valang/index.html#author">胡 键</a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#106;&#105;&#97;&#110;&#104;&#103;&#114;&#101;&#97;&#116;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#86;&#97;&#108;&#97;&#110;&#103;&#37;&#50;&#48;&#86;&#97;&#108;&#105;&#100;&#97;&#116;&#111;&#114;&#37;&#50;&#48;&#37;&#69;&#54;&#37;&#57;&#52;&#37;&#66;&#66;&#37;&#69;&#55;&#37;&#57;&#53;&#37;&#65;&#53;">jianhgreat@hotmail.com</a>), 西安交通大学硕士<br />
</p>
<p>2007 年  4 月  12 日</p>
<blockquote>Valang
是 Validation Language 的缩写，Valang Validator
的字面含义就是&#8220;使用验证语言的验证器&#8221;，是一种支持声明的验证器。本文详细的介绍了 ValangValidator
的使用和相关所需的配置。作为一种新型的验证工具，它提供的验证语言（valang）具有简单、易学，易扩展等特点。</blockquote>
<p>Valang 是 Validation Language 的缩写，Valang Validator 的字面含义就是&#8220;使用验证语言的验证器&#8221;，它是一种支持声明的验证器。作为新出的验证工具，它拥有不同于现有其它相似工具的特点：</p>
<ul>
    <li>基于声明的验证工具包，大多数情况下可以避免手工书写代码。</li>
    <li>提供了一套精巧的 DSL（Domain Specific language），支持表达式语言。语法非常简洁，且支持扩展方便。</li>
    <li>配置精简，可读性高、易维护。</li>
    <li>包含在 Spring Module 中，简化了 Spring Framework 的 Validator 使用。</li>
    <li>支持 JavaScript，对于 Web 应用，支持客户端的验证。</li>
</ul>
<p>本文将带领读者对以上特点一一寻访，在进一步阅读本文之前，请确保读者具有 Spring Framework 的使用经验，如果有 Spring MVC 的经验更佳。</p>
<p><a name="N1005A">使用概览</a></p>
<p>Valang
Validator 属于 Spring Module 的 Validation 模块。Spring Module 是一组扩展 Spring
Framework 功能的工具集，当前的版本是 0.6。它作为独立于 Spring Framework 的项目而存在，其目的非常单纯：方便
Spring Framework 与其它框架的集成，避免为此修改 Spring Framework 的核心。</p>
<p>使用 Valang Validator 必须先了解它的两个重要组件：</p>
<ul>
    <li>valang，即验证语法，使用者使用它将约束定义转化为 ValangValidator 能解析的验证规则。</li>
    <li>ValangValidator，它是 Spring Validator 接口的具体实现。负责验证规则解析，完成实际的验证工作。</li>
</ul>
<p>了解了这些关于 Valang Validator 的信息之后，不妨从一个例子来了解它的使用。本例使用的环境：JDK 1.5、Spring Framework2.0 和 Spring Module 0.6。</p>
<p><a name="N10072">问题描述</a></p>
<p>为
了尽可能的体现 Valang Validator 的特点，例子中需要验证的对象有意地被设计得稍微有些复杂：首先，属性是最常见的 3
种类型，而且其中还有一个是在 Java 中有些烦人的Date；其次，它还包含了一个成员类属性。关于类结构和相关的约束如下表：</p>
<table border="1" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <th>类结构</th><th>约束</th>
        </tr>
        <tr>
            <td>public class User {
            private String name;
            private int age;
            private Date birthday;
            private Address address;
            &#8230;&#8230;
            }
            public class Address {
            private String state;
            private String town;
            private String street;
            &#8230;&#8230;
            }</td>
            <td>name，必填，且长度不超过 50。<br />
            	age，非必填，必须大于等于 0，小于等于 60。<br />
            	birthday，非必填，必须在 2000-01-01 之后。<br />
            	address.state，必填，长度不超过 50。<br />
            	address.town，必填，长度不超过 50。<br />
            	address.street，必填，长度不超过 200。</td>
        </tr>
    </tbody>
</table>

<p><a name="N1009C">实现 Validator</a></p>
<p>实
现 Valang Validator 就是要完成两部分工作：定义 valang 描述和 Validator 创建。Validator
的创建可以使用 Java 代码进行，也可使用 Spring DI 完成。本例采用后一种方法，Bean 定义如下（文件名：valang.xml）：</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>&lt;bean id="userValidator" class="org.springmodules.validation.valang.ValangValidator"&gt;<br />
            &lt;property name="valang"&gt;<br />
            &lt;value&gt;<br />
            &lt;![CDATA[<br />
            { name  : ? is not null and ? has text and length(?)&lt;= 50 <br />
            : 'Name is empty or too long.'}<br />
            { age : ? between 0 and 60 : 'Age should between 0 and 60.'}<br />
            <span style="margin-top: 0pt; margin-bottom: 0pt; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 11px; color: #ff0000;">|-------10--------20--------30--------40--------50--------60--------70--------80--------9|</span><br />
            <span style="margin-top: 0pt; margin-bottom: 0pt; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 11px; color: #ff0000;">|-------- XML error:  The previous line is longer than the max of 90 characters ---------|</span><br />
            { birthday : ? is null or ? &gt;[20000101]<br />
            : 'Birthday should be after 2000-01-01.'}<br />
            { address : ? is not null : 'Address is empty.'}<br />
            { address.state : address is null or ( ? is not null and<br />
            ? has text and length(?)&lt;=50 )<br />
            : 'state is empty or too long.'}<br />
            { address.town : address is null or ( ? is not null and  <br />
            ? has text and length(?)&lt;=50 )<br />
            : 'town is empty or too long.'}<br />
            { address.street : address is null or ( ? is not null and<br />
            ? has text and length(?)&lt;=50 )<br />
            : 'street is empty or too long.'}	<br />
            ]]&gt;<br />
            &lt;/value&gt;<br />
            &lt;/property&gt;<br />
            &lt;property name="dateParsers"&gt;<br />
            &lt;map&gt;<br />
            &lt;entry key="^""d{8}$" value="yyyyMMdd" /&gt;<br />
            &lt;/map&gt;<br />
            &lt;/property&gt;<br />
            &lt;/bean&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>瞧，没有一行 Java 代码，居然完成了一个 Validator 的定义。这其中的秘密就是 valang 属性，它定义了验证规则。再看看它的语法，是不是很酷？！另一个属性 dateParsers 定义了日期转换的格式。关于它们的解释在后面有详细的介绍。</p>
<p>技巧：</p>
<p>如
果 Adress 对象被多个对象引用，且对于 Address 的验证约束相同，那么以上的 address
相关的约束规则就需要重复书写。这样，既繁且不利于维护。此时，可以将 address 相关的约束组织成一个
validator，实现约束的复用。具体步骤：</p>
<p>a)	抽取 Address 的约束到 addressValidator。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>&lt;bean id="addressValidator" class="org.springmodules.validation.valang.ValangValidator"&gt;<br />
            &lt;property name="valang"&gt;<br />
            &lt;value&gt;<br />
            &lt;![CDATA[<br />
            { state : ? is not null and  ? has text and length(?)&lt;=50<br />
            : 'state is empty or too long.'}<br />
            { town : ? is not null and  ? has text and length(?)&lt;=50<br />
            : 'town is empty or too long.'}<br />
            { street : ? is not null and  ? has text and length(?)&lt;=50<br />
            : 'street is empty or too long.'}<br />
            ]]&gt;			<br />
            &lt;/value&gt;<br />
            &lt;/property&gt;<br />
            &lt;/bean&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>b)	实现继承 ValangValidator 的 UserValidator，包含 addressValidator：</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>public class UserValidator extends ValangValidator {<br />
            private Validator addressValidator;<br />
            <br />
            public void setAddressValidator(Validator addressValidator) {<br />
            this.addressValidator = addressValidator;<br />
            }<br />
            <br />
            public void validate(Object target, Errors errors){<br />
            super.validate( target, errors);<br />
            Address address= ((User)target).getAddress();<br />
            // address 为 null 时，不进行进一步的验证。<br />
            if( null!= address){<br />
            //注意与 popNestedPath 成对使用<br />
            errors.pushNestedPath("address");<br />
            //验证 address<br />
            addressValidator.validate( address, errors);<br />
            errors.popNestedPath();<br />
            }<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>c)	修改 userValidator 的配置。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>&lt;bean id="userValidator" class="包名.UserValidator"&gt;<br />
            &lt;property name="addressValidator" ref="addressValidator"/&gt;<br />
            &lt;property name="valang"&gt;<br />
            &lt;value&gt;<br />
            &lt;![CDATA[<br />
            { name : ? is not null and ? has text and length(?)&lt;= 50<br />
            : 'Name is empty or too long.'}<br />
            { age : ? between 0 and 60 : 'Age should between 0 and 60.'}<br />
            { birthday : ? is null or ? &gt;[20000101] : 'Birthday should be after 2000-01-01.'}<br />
            <span style="margin-top: 0pt; margin-bottom: 0pt; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 11px; color: #ff0000;">|-------10--------20--------30--------40--------50--------60--------70--------80--------9|</span><br />
            <span style="margin-top: 0pt; margin-bottom: 0pt; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 11px; color: #ff0000;">|-------- XML error:  The previous line is longer than the max of 90 characters ---------|</span><br />
            { address : ? is not null : 'Address is empty.'}<br />
            ]]&gt;			<br />
            &lt;/value&gt;<br />
            &lt;/property&gt;<br />
            &lt;property name="dateParsers"&gt;<br />
            &lt;map&gt;<br />
            &lt;entry key="^""d{8}$" value="yyyyMMdd" /&gt;<br />
            &lt;/map&gt;<br />
            &lt;/property&gt;<br />
            &lt;/bean&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N100C6">单元测试</a></p>
<p>对
ValangValidator 进行单元测试非常简单，需要注意的是它的实例化。这里使用 BeanFactory 来创建待测的
Validator。Spring 提供了测试用例基类
AbstractDependencyInjectionSpringContextTests，使得手工创建完全避免：</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>public class UserValidatorTest extends AbstractDependencyInjectionSpringContextTests {<br />
            private Validator userValidator;<br />
            <br />
            public UserValidatorTest(){<br />
            super();<br />
            //缺省是AUTOWIRE_BY_TYPE，当Bean文件有2个以上的同类型<br />
            //Bean定义时就应该采用AUTOWIRE_BY_NAME方式。<br />
            setAutowireMode(AUTOWIRE_BY_NAME);<br />
            }<br />
            <br />
            protected String[] getConfigLocations() {<br />
            //指明配置文件的位置<br />
            return new String[]{"file:文件路径/valang.xml"};<br />
            }<br />
            //基类调用该方法完成依赖注入（DI）<br />
            public void setUserValidator(Validator userValidator) {<br />
            this.userValidator = userValidator;<br />
            }<br />
            <br />
            public void testEmptyObject(){<br />
            User user= new User();<br />
            BindException errors= new BindException(user, "target");<br />
            userValidator.validate( user, errors);<br />
            assertTrue( errors.getAllErrors().size()== 2);<br />
            ObjectError error1= (ObjectError)(errors.getAllErrors().get(0));<br />
            assertEquals(error1.getDefaultMessage(), "Name is empty or too long.");<br />
            ObjectError error2= (ObjectError)(errors.getAllErrors().get(1));<br />
            assertEquals(error2.getDefaultMessage(), "Address is empty.");<br />
            }<br />
            &#8230;&#8230;<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N100D2">使用 Validator</a></p>
<p>上面的单元测试已经展示了 ValangValidator 的使用，除此之外，也可使用 BeanFactory 将它注入某个控制器中使用：</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>&lt;bean id="userController" class="&#8230;&#8230;"&gt;<br />
            &#8230;&#8230;<br />
            &lt;property name="validator" ref="userValidator"/&gt;<br />
            &lt;/bean&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>从上面的例子来看，ValangValidator
的使用相当简单。尤其对于有过 Spring 经验的用户来说，除了需要掌握 valang 语法之外，其余的根本就和 Spring
Validator 使用一样。而且，例子中 valang 的语法所展示的简洁、直观给人留下了深刻的印象。</p>
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/opensource/os-lo-valang/index.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N100E1">Valang 语法</a></p>
<p>很
酷！这是我看到 valang 的第一印象。Spring
数据绑定语法相同的属性表达式，灵活、便捷、易学的语法。这些，都使得另一个被广泛使用的声明性验证工具 Commons Validator
显得相形见绌。虽然作为早期的声明性验证工具的实现，Commons Validator 的功绩不可磨灭，但是其配置的繁琐也使人厌烦不已。</p>
<p>&#8220;以数据为中心&#8221;是 ValangValidator 的设计思路，此处的数据就是 valang 描述。valang 由一系列的规则组成，对于每个规则，它的结构如下：</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>{ &lt;key&gt; : &lt;predicate_expression&gt; : &lt;message&gt; [: &lt;error_code&gt; [: &lt;args&gt; ]] }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>其中：</p>
<ul>
    <li>&lt;key&gt;，属性表达式。它是要验证的属性名，必填。</li>
    <li>&lt;predicate_expression&gt;，谓词表达式。期望满足的条件，必填。</li>
    <li>&lt;message&gt;，缺省消息。当不满足预期表达式时显示的消息，必填。</li>
    <li>&lt;error_code&gt;，错误码。国际化时使用的消息键值，当它存在时缺省消息被忽略，可选。</li>
    <li>&lt;args&gt;，参数。逗号分隔的参数值，用于替换错误码中定义的站位符，可选。</li>
</ul>
<p>这些参数，除了谓词表达式之外，都可以在 Spring 中见到它们的踪影。而它们的含义、用法也丝毫没有发生变化。因此，让我们把关注的焦点集中在谓词表达式上。</p>
<p><a name="N10109">表达式语法</a></p>
<p>valang 将谓词表达式定义成如下结构：</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>&lt;expression&gt; ::= &lt;expression&gt; ( ( "AND" | "OR" ) &lt;expression&gt; )+ | &lt;predicate&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>其中的谓词（&lt;predicate&gt;），是由操作符、文字常量、bean 属性、数学表达式和函数组成的可计算实体。</p>
<p>
<strong>1.操作符</strong>
</p>
<p>valang 的操作符有三种：一元操作符，二元操作符和其他操作符。支持的操作符如下表：</p>
<table border="1" cellpadding="2" cellspacing="0" width="557">
    <tbody>
        <tr>
            <td width="51">类型</td>
            <td width="141">支持类型</td>
            <td width="278">操作符</td>
            <td width="81">说明</td>
        </tr>
        <tr>
            <td rowspan="12" width="51">一元</td>
            <td rowspan="2" width="141">对象</td>
            <td width="278">NULL | IS NULL</td>
            <td width="81">为空</td>
        </tr>
        <tr>
            <td width="278">NOT NULL | IS NOT NULL</td>
            <td width="81">非空</td>
        </tr>
        <tr>
            <td rowspan="10" width="141">字符串</td>
            <td width="278">HAS TEXT</td>
            <td width="81">包含非空格的字符</td>
        </tr>
        <tr>
            <td width="278">HAS NO TEXT</td>
            <td width="81">null或只有空格字符</td>
        </tr>
        <tr>
            <td width="278">HAS LENGTH | IS NOT BLANK</td>
            <td width="81">长度&gt;0</td>
        </tr>
        <tr>
            <td width="278">HAS NO LENGTH | IS BLANK</td>
            <td width="81">null或长度=0</td>
        </tr>
        <tr>
            <td width="278">IS UPPERCASE | IS UPPER CASE |
            <br />
            IS UPPER</td>
            <td width="81">所有字母大写</td>
        </tr>
        <tr>
            <td width="278">IS NOT UPPERCASE |
            <br />
            IS NOT UPPER CASE |
            <br />
            IS NOT UPPER</td>
            <td width="81">不是所有字母大写</td>
        </tr>
        <tr>
            <td width="278">IS LOWERCASE | IS LOWER CASE | IS LOWER</td>
            <td width="81">所有字母小写</td>
        </tr>
        <tr>
            <td width="278">IS NOT LOWERCASE |
            <br />
            IS NOT LOWER CASE |
            <br />
            IS NOT LOWER</td>
            <td width="81">不是所有字母小写</td>
        </tr>
        <tr>
            <td width="278">IS WORD</td>
        </tr>
        <tr>
            <td width="278">IS NOT WORD</td>
        </tr>
        <tr>
            <td rowspan="6" width="51">二元</td>
            <td rowspan="2" width="141">字符串、日期、布尔和数字</td>
            <td width="278">= | == | IS | EQUALS</td>
            <td width="81">相等</td>
        </tr>
        <tr>
            <td width="278">!= | &lt;&gt; | &gt;&lt; | IS NOT | NOT EQUALS</td>
            <td width="81">不等</td>
        </tr>
        <tr>
            <td rowspan="4" width="141">数字、日期</td>
            <td width="278">&gt;|GREATER THAN |<br />
            IS GREATER THAN</td>
            <td width="81">大于</td>
        </tr>
        <tr>
            <td width="278">&lt; | LESS THAN | IS LESS THAN</td>
            <td width="81">小于</td>
        </tr>
        <tr>
            <td width="278">&gt;= | =&gt; |
            <br />
            GREATER THAN OR EQUALS |
            <br />
            IS GREATER THAN OR EQUALS</td>
            <td width="81">大于等于</td>
        </tr>
        <tr>
            <td width="278">&lt;= | =&lt; | LESS THAN OR EQUALS | IS LESS THAN OR
            EQUALS</td>
            <td width="81">小于等于</td>
        </tr>
        <tr>
            <td rowspan="5" width="51">其他</td>
            <td rowspan="2" width="141">数字、日期</td>
            <td width="278">BETWEEN A AND B</td>
            <td width="81">大于等于A，且小于等于B</td>
        </tr>
        <tr>
            <td width="278">NOT BETWEEN A AND B</td>
            <td width="81">小于A，或大于B</td>
        </tr>
        <tr>
            <td rowspan="2" width="141">字符串、日期、布尔和数字</td>
            <td width="278">IN &lt;value&gt; (, &lt;value&gt;)*</td>
            <td width="81">在值列表中</td>
        </tr>
        <tr>
            <td width="278">NOT IN &lt;value&gt; (, &lt;value&gt;)*</td>
            <td width="81">不在值列表中</td>
        </tr>
        <tr>
            <td width="141">布尔表达式</td>
            <td width="278">NOT</td>
            <td width="81">取反</td>
        </tr>
    </tbody>
</table>
<p>这些操作符是大小写非敏感的，另外在使用时需要注意：</p>
<ul>
    <li>避免在验证时出现 NullException，这一点可以在 userValidator 的例子中看到。</li>
    <li>二元操作符两边的操作数类型必须一致。</li>
</ul>

<p>
<strong>2.文字常数</strong>
</p>
<p>Valang 支持 4 种文字常数：字符串、数字、日期和布尔。这四种类型的文字常数如下表：</p>
<table border="1" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <th>
            <strong>类型</strong>
            </th><th>
            <strong>说明</strong>
            </th>
        </tr>
        <tr>
            <td>字符串</td>
            <td>字符串使用单引号包含，如 &#8217;foxgem&#8217;。</td>
        </tr>
        <tr>
            <td>数字</td>
            <td>数字被转换为 java.math.BigDecimal。</td>
        </tr>
        <tr>
            <td>布尔</td>
            <td>布尔值必须是：TRUE、YES、FALSE、NO 中的一个，且不要使用单引号。</td>
        </tr>
        <tr>
            <td>日期</td>
            <td>日期值使用 [] 包含，如 [2000-01-01]。</td>
        </tr>
    </tbody>
</table>
<p>valang 对于日期类型的处理非常特殊，而且有趣：</p>
<ul>
    <li>使用 T 来代表当前时间， 如 [T]。</li>
    <li>
    <p>提供了移位操作，语法是：日期 &gt; 时间单位，日期 &lt; 时间单位。前者表示将日期中对应的单位部分向后滚动到该时间单位的最大值；后者表示将日期中对应的单位部分向前滚动到该时间单位的最小值。支持的时间单位和对应范围：</p>
    <p>
    1)	秒（s）：0~999 毫秒<br />
    2)	分钟（m）：0秒0毫秒 ~ 59秒999毫秒<br />
    3)	小时（H）：0分0秒0毫秒 ~ 59分59秒999毫秒<br />
    4)	天（d）：0时0分0秒0毫秒 ~ 23时59分59秒999毫秒<br />
    5)	星期（w）：周一0时0分0秒0毫秒 ~ 周日23时59分59秒999毫秒<br />
    6)	月（M）：月第一天0时0分0秒0毫秒 ~ 月最后一天23时59分59秒999毫秒<br />
    7)	年（y）：年第一天0时0分0秒0毫秒 ~ 年最后一天23时59分59秒999毫秒<br />
    </p>
    <p>如：[T&gt;d]，表示当天的 23 时 59 分 59 秒 999 毫秒。</p>
    </li>
    <li>
    <p>支持日期的增减运算。增减分别对应：&#8220;+&#8221;和&#8220;-&#8221;；数量对应：i 时间单位，i 表示整数。如：[T&gt;d-1d+2H]，表示先将当前时间后滚至当天的最后时刻，再减去一天，再加上 2 个小时。</p>
    <p>日期格式在 valang 中也有很好的支持，可以很方便的定义自己的格式。日期解析过程如下：</p>
    <p>
    1)	取出日期字符串，在 dateParsers 中定义 Map 的正则表达式键集合中进行匹配。<br />
    2)	当找到符合的正则表达式时，以它为键值获取对应的日期格式串。<br />
    3)	使用 SimpleDateFomat，将日期字符串按照日期格式串转化为对应的 Date 类型。<br />
    </p>
    <p>
    因此，如果要在 valang 中使用 yyyy-MM-dd 的格式时，那么使用以下的定义：</p>
    <table border="0" cellpadding="0" cellspacing="0" width="100%">
        <tbody>
            <tr>
                <td>
                <pre>&lt;entry key="^""d{4}-""d{2}-""d{2}$" value="yyyy-MM-dd" /&gt;<br />
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br />
    </li>
</ul>
<p>
<strong>3. bean 属性</strong>
</p>
<p>bean 属性的用法和属性表达式的用法一样，在运行时它的值会被取出。另外，valang 使用&#8220;?&#8221;来表示在规则中定义的属性表达式的值。</p>
<p>
<strong>4. 数学表达式</strong>
</p>
<p>valang 支持的数学运算符有：+、-、*、/（或 div）、%（或 mod）。</p>
<p>
<strong>5.函数</strong>
</p>
<p>valang 内置的函数如下：</p>
<table border="1" cellpadding="0" cellspacing="0" width="552">
    <tbody>
        <tr>
            <td width="156">函数</td>
            <td width="396">说明</td>
        </tr>
        <tr>
            <td width="156">length | len | size |count</td>
            <td width="396">如果传入参数是集合或数组，那么返回它们的大小；否则，返回传入参数的 toString 所返回字符串的长度。<br />
            使用例子：length(?)&lt;50</td>
        </tr>
        <tr>
            <td width="156">match | matches</td>
            <td width="396">传入参数（参数 2）的 toString 返回的字符串是否匹配给定的正则表达式（参数1）。<br />
            使用例子：match('^""d{2}$', ?) is true</td>
        </tr>
        <tr>
            <td width="156">email</td>
            <td width="396">传入参数的 toString 返回的字符串是有效的 email。<br />
            使用例子：email(?) is true</td>
        </tr>
        <tr>
            <td width="156">upper</td>
            <td width="396">将传入参数的 toString 返回的字符串转换为大写。<br />
            使用例子：upper(?)=='VALANG'</td>
        </tr>
        <tr>
            <td width="156">lower</td>
            <td width="396">将传入参数的 toString 返回的字符串转换为小写。<br />
            使用例子：lower(?)=='valang'</td>
        </tr>
        <tr>
            <td width="156">!</td>
            <td width="396">布尔值取反<br />
            使用例子：!(?) is true。<br />
            <br />
            <strong>注意：它不能内嵌表达式，如&#8220;!(length(?)&lt;= 50) is true&#8221;将会出错。</strong>
            </td>
        </tr>
        <tr>
            <td width="156">resolve</td>
            <td width="396">解析消息码。</td>
        </tr>
        <tr>
            <td width="156">inRole</td>
            <td width="396">传入参数是角色名，如果当前用户有这个角色，则返回 true。</td>
        </tr>
    </tbody>
</table>
<p>valang 对于函数的支持并不是只仅仅提供几个简单的函数，它还允许用户自定义函数。这大大的扩展了它的表现力。</p>
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/opensource/os-lo-valang/index.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1039E">自定义函数</a></p>
<p>org.springmodules.validation.valang.functions.AbstractFunction
是 valang 函数的基类，所有的内置函数都是它的子类，自定义函数也必须从它继承。AbstractFunction 有几个重要的方法需要注意：</p>
<ul>
    <li>构造函数，子类必须实现，不要忘了先调用 super。</li>
    <li>definedExactNumberOfArguments、
    definedMaxNumberOfArguments、definedMinNumberOfArguments，分别是定义函数的参数个数，函数的
    最大参数个数、函数的最小参数个数。它们在构造函数中被调用。</li>
    <li>init，在所有属性被设置后调用，负责初始化。</li>
    <li>isAutowireByName 和 isAutowireByType，分别指定了函数的属性被 BeanFactory 自动装配采用的方法。</li>
    <li>doGetResult，返回函数的结果。</li>
</ul>
<p>下面通过实现一个自定义版本的 length 函数来了解自定义函数的编写和配置：</p>
<p>函数定义：</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>public class AnotherLengthFunction extends AbstractFunction {<br />
            <br />
            public AnotherLengthFunction(Function[] functions, int line, int column) {<br />
            super(functions, line, column);<br />
            //AnotherLengthFunction只支持一个参数<br />
            definedExactNumberOfArguments(1);<br />
            }<br />
            <br />
            protected Object doGetResult(Object target) throws Exception {<br />
            //返回参数1的toString的值。如果有多个参数，那么：<br />
            //参数2：getArguments()[1].getResult(target).toString();以此类推。<br />
            //另外getArguments()的个数就是实际传入的参数个数。<br />
            String value= getArguments()[0].getResult(target).toString();<br />
            return new Integer(value.length());<br />
            }<br />
            <br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>函数配置：</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>&lt;property name="customFunctions"&gt;<br />
            &lt;map&gt;<br />
            &lt;!-- 前者是函数使用的名字，后者对应函数实现的class。<br />
            使用例子：anotherLength(?)&lt;50<br />
            --&gt;<br />
            &lt;entry key="anotherLength" value="AnotherLengthFunction"/&gt;<br />
            &lt;/map&gt;<br />
            &lt;/property&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>当自定义的函数需要外部的资源，如数据库连接、网络连接、文件等，可以通过 BeanFactory 自动装配来完成。此时需要覆盖 isAutowireByName 或 isAutowireByType，指定自动装配的类型，这两个函数不能同时覆盖。</p>
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/opensource/os-lo-valang/index.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N103CC">客户端验证</a></p>
<p>对于 Web 应用程序来说，服务器的验证固然是必须的，但如果提供了客户端验证的话，那无疑是锦上添花的事情。幸运的是，Valang Validator 已经做到了这一点。要使用这一功能，需要先了解 2 个组件：</p>
<p>
<strong>1. org.springmodules.validation.valang.javascript.taglib.ValangRulesExportInterceptor</strong>
</p>
<p>这个拦截器的作用是输出被当前请求处理器所使用的 valang 验证规则到 ModelAndView 中，使 &lt;validate&gt; 可以利用这些信息。</p>
<p>在以下情况下之一时，该拦截器不做任何事情：</p>
<ul>
    <li>当被拦截的请求处理器不是 BaseCommandController。</li>
    <li>请求处理器使用的 Validator 不是 ValangValidator 实例。</li>
    <li>请求处理器不输出一个 Command 对象到 model 中。</li>
</ul>
<p>
<strong>2. valang 标签库</strong>
</p>
<p>valang 标签库完成客户端脚本的产生，并进行实际的客户端验证。它包含了 2 个标签：</p>
<table border="1" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td width="76">标签</td>
            <td width="144">作用</td>
            <td width="128">属性</td>
            <td width="204">说明</td>
        </tr>
        <tr>
            <td rowspan="3" width="76">codebase</td>
            <td rowspan="3" width="144">产生被翻译的 javascript 验证规则执行所需要的代码。它应该每页包含一次，且在被产生的验证规则之前。</td>
            <td width="128">includeScriptTags</td>
            <td width="204">非必须，指示产生的 javascript 代码是否被 &lt;script&gt; 包含。</td>
        </tr>
        <tr>
            <td width="128">fieldErrorsIdSuffix</td>
            <td width="204">非必须，指明用来包含属性错误信息的 &lt;div&gt; 或 &lt;span&gt; 的 id 的后缀。整个 id 的最后组成：<br />
            &lt;属性名 &gt;&lt;
            fieldErrorsIdSuffix &gt;</td>
        </tr>
        <tr>
            <td width="128">globalErrorsId</td>
            <td width="204">非必须，指明用来包含全局错误信息的 &lt;div&gt; 或 &lt;span&gt; 的 id。</td>
        </tr>
        <tr>
            <td width="76">validate</td>
            <td width="144">将valang规则翻译成对应的 javascript 验证规则。</td>
            <td width="128">commandName</td>
            <td width="204">非必须，指明需要验证的 command 对象名字。</td>
        </tr>
    </tbody>
</table>
<p>接下来我们以一个例子来展示如何应用以上所学。例子本身并不复杂，就是在以上的 userValidator 例子的基础上，完成一个 Controller 和相关的 JSP 页面。例子的步骤和说明如下：</p>
<p>1. Controller 负责初始化 User 对象，在提交时取出 User 各属性的值放入到 ModelAndView 中。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>public class UserController extends SimpleFormController {<br />
            <br />
            protected Object formBackingObject(HttpServletRequest request){<br />
            User user= new User();<br />
            user.setAddress( new Address());<br />
            return user;<br />
            }<br />
            <br />
            protected ModelAndView onSubmit(Object command) throws Exception{<br />
            User user=(User)command;<br />
            ModelAndView mav= new ModelAndView(getSuccessView());<br />
            //取出user的属性，放到mav中。<br />
            mav.addObject("isSubmit", "true");//提交处理的标志。<br />
            return mav;<br />
            }<br />
            <br />
            protected void initBinder(HttpServletRequest request,<br />
            ServletRequestDataBinder binder)<br />
            throws Exception{<br />
            SimpleDateFormat format= new SimpleDateFormat("yyyy-MM-dd");<br />
            binder.registerCustomEditor(Date.class, "birthday"<br />
            , new CustomDateEditor(format, true));<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>2. JSP 页面既充当 User 的编辑页面，又是
Controller 提交后的属性显示页面。它演示了 valang 标签库的配置和使用，是本例的重头戏。本例中使用了 Spring 2.0
中提供的 Spring Form 标签，关于它的使用请参见 Spring 手册。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>&lt;%@ page contentType="text/html" %&gt;<br />
            &lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;<br />
            &lt;%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %&gt;<br />
            &lt;!-- 包含valang标签库 --&gt;<br />
            &lt;%@ taglib prefix="valang" uri="http://www.springmodules.org/tags/valang" %&gt;<br />
            <br />
            &lt;html&gt;<br />
            &lt;head&gt;<br />
            &lt;title&gt;User Detail&lt;/title&gt;<br />
            &lt;!-- 请将codebase包含在&lt;head&gt;中，确保在页面显示时，<br />
            所需要的环境已经产生 --&gt;<br />
            &lt;valang:codebase includeScriptTags="true" fieldErrorsIdSuffix="_error" <br />
            globalErrorsId="global_error"/&gt;<br />
            &lt;/head&gt;<br />
            <br />
            &lt;body&gt;<br />
            &lt;c:if test="${not empty param.isSubmit}"&gt;<br />
            &lt;!-- 显示提交之后的User各属性值 --&gt;<br />
            &lt;/c:if&gt;<br />
            &lt;c:if test="${empty param.isSubmit}"&gt;<br />
            &lt;form:form commandName="user" method="post"&gt;<br />
            &lt;!-- validate需要包含在&lt;form&gt;中，commandName<br />
            是UserController的CommandName --&gt;<br />
            &lt;valang:validate commandName="user"/&gt;<br />
            &lt;div id="global_error"&gt;<br />
            &lt;/div&gt;<br />
            &lt;table&gt;<br />
            &lt;tr&gt;<br />
            &lt;td&gt;name:&lt;/td&gt;<br />
            &lt;td&gt;<br />
            &lt;form:input path="name" /&gt;<br />
            &lt;!-- 注意id的命名。在_error之前的属性名实际是<br />
            在valang验证规则中出现的属性表达式的名字。否则，将出错。<br />
            如：以上的userValidator的例子中，如果将address相关的属性<br />
            合并到一个中：<br />
            { address: ? is not null <br />
            and address.state is not null and length(address.state)&lt;= 50<br />
            and address.town is not null and length(address.town)&lt;= 50<br />
            and address.street is not null and length(address.street)&lt;= 50<br />
            : 'Invalida address.'}<br />
            那么，下面的 address 相关的属性对应的 &lt;span&gt; 的 id 就应该是：<br />
            address_error，且只有一个。因为多个时，会引起 javascript 的错误。<br />
            --&gt;<br />
            &lt;span id="name_error"&gt;&lt;form:errors path="name"/&gt;&lt;/span&gt;<br />
            &lt;/td&gt;<br />
            &lt;/tr&gt;<br />
            &lt;tr&gt;<br />
            &lt;td&gt;age:&lt;/td&gt;<br />
            &lt;td&gt;<br />
            &lt;form:input path="age" /&gt;<br />
            &lt;span id="age_error"&gt;&lt;form:errors path="age" /&gt;&lt;/span&gt;<br />
            &lt;/td&gt;<br />
            &lt;/tr&gt;<br />
            &lt;tr&gt;<br />
            &lt;td&gt;birthday:&lt;/td&gt;<br />
            &lt;td&gt;<br />
            &lt;form:input path="birthday" /&gt;<br />
            &lt;span id="birthday_error"&gt;&lt;form:errors path="birthday" /&gt;&lt;/span&gt;<br />
            &lt;/td&gt;<br />
            &lt;/tr&gt;<br />
            &lt;tr&gt;<br />
            &lt;td&gt;state:&lt;/td&gt;<br />
            &lt;td&gt;<br />
            &lt;form:input path="address.state" /&gt;<br />
            &lt;span id="address.state_error"&gt;&lt;form:errors path="address.state" /&gt;&lt;/span&gt;<br />
            &lt;/td&gt;<br />
            &lt;/tr&gt;<br />
            &lt;tr&gt;<br />
            &lt;td&gt;town:&lt;/td&gt;<br />
            &lt;td&gt;<br />
            &lt;form:input path="address.town" /&gt;<br />
            &lt;span id="address.town_error"&gt;&lt;form:errors path="address.town" /&gt;&lt;/span&gt;<br />
            &lt;/td&gt;<br />
            &lt;/tr&gt;<br />
            &lt;tr&gt;<br />
            &lt;td&gt;street:&lt;/td&gt;<br />
            &lt;td&gt;<br />
            &lt;form:input path="address.street" /&gt;<br />
            &lt;span id="address.street_error"&gt;&lt;form:errors path="address.street" /&gt;&lt;/span&gt;<br />
            <span style="margin-top: 0pt; margin-bottom: 0pt; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 11px; color: #ff0000;">|-------10--------20--------30--------40--------50--------60--------70--------80--------9|</span><br />
            <span style="margin-top: 0pt; margin-bottom: 0pt; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 11px; color: #ff0000;">|-------- XML error:  The previous line is longer than the max of 90 characters ---------|</span><br />
            &lt;/td&gt;<br />
            &lt;/tr&gt;<br />
            &lt;tr&gt;<br />
            &lt;td colspan="2"&gt;<br />
            &lt;input type="submit" value="Submit" /&gt;<br />
            &lt;/td&gt;<br />
            &lt;/tr&gt;<br />
            &lt;/table&gt;<br />
            &lt;/form:form&gt;<br />
            &lt;/c:if&gt;<br />
            &lt;/body&gt;<br />
            &lt;/html&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>
<strong>3. 相关配置。</strong>
</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre>&lt;bean id="handlerMapping" <br />
            class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"&gt;<br />
            &lt;property name="interceptors"&gt;<br />
            &lt;list&gt;<br />
            &lt;ref bean="valangInterceptor"/&gt;<br />
            &lt;/list&gt;<br />
            &lt;/property&gt;<br />
            &lt;property name="urlMap"&gt;<br />
            &lt;map&gt;<br />
            &lt;entry key="/userDetail.htm"&gt;&lt;ref bean="userController"/&gt;&lt;/entry&gt;<br />
            &lt;/map&gt;<br />
            &lt;/property&gt;<br />
            &lt;/bean&gt;<br />
            <br />
            &lt;bean id="userController" name="/userDetail.htm" class="example.UserController"&gt;<br />
            &lt;property name="formView" value="userDetail"/&gt;<br />
            &lt;property name="successView" value="redirect:/userDetail.htm"/&gt;<br />
            &lt;property name="validator" ref="userValidator"/&gt;<br />
            &lt;property name="commandName" value="user"/&gt;<br />
            &lt;property name="commandClass" value="example.User"/&gt;<br />
            &lt;/bean&gt;<br />
            <br />
            &lt;bean id="valangInterceptor"<br />
            class="org.springmodules.validation.valang.javascript.taglib.ValangRulesExportInterceptor"/&gt;<br />
            <span style="margin-top: 0pt; margin-bottom: 0pt; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 11px; color: #ff0000;">|-------10--------20--------30--------40--------50--------60--------70--------80--------9|</span><br />
            <span style="margin-top: 0pt; margin-bottom: 0pt; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 11px; color: #ff0000;">|-------- XML error:  The previous line is longer than the max of 90 characters ---------|</span><br />
            <br />
            &lt;bean id="viewResolver"<br />
            class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt;<br />
            &lt;property name="prefix" value="/WEB-INF/jsp/"/&gt;<br />
            &lt;property name="suffix" value=".jsp"/&gt;<br />
            &lt;property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/&gt;<br />
            &lt;/bean&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/opensource/os-lo-valang/index.html#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1047B">结束语</a></p>
<p>本
文详细的介绍了 ValangValidator
的使用和相关所需的配置。作为一种新型的验证工具，它提供的验证语言（valang）具有简单、易学，易扩展等特点。由于采用验证语言，在大多数的情形
下，避免了书写 Java 验证代码的需要。这些特性，极大的简化了通常繁琐的验证实现。与 Spring Framework
结合，也对于它的推广有了很好的起点。同时，在使用时也需注意，由于目前它的 1.0 版本尚未正式的发布，API
尚不稳定。对于这一点，使用者也需有心理准备。</p>
<br />
<br />
<p><a name="resources">参考资料 </a></p>
<ul>
    <li>阅读 developerWorks 文章：<a href="http://www.ibm.com/developerworks/cn/java/j-hibval.html">Hibernate 能够满足我们的验证需求</a>。<br />
    <br />
    </li>
    <li>阅读 developerWorks 文章：<a href="http://www.ibm.com/developerworks/cn/java/l-commons-validator/index.html">通用验证系统</a>。<br />
    <br />
    </li>
    <li>阅读 developerWorks 文章：<a href="http://www.ibm.com/developerworks/cn/websphere/library/techarticles/0311_fung_yu/fung_yu2.html">协同使用 WebSphere Studio V5 与 Struts Framework――第二部分：使用 Struts 验证器</a>。<br />
    <br />
    </li>
    <li>访问：<a href="http://www.springframework.org/">Spring Framework 官方站点</a>。<br />
    <br />
    </li>
    <li>访问：<a href="https://springmodules.dev.java.net/">Spring Module 官方站点</a>。</li>
</ul>
<br />
<br />
<p><a name="author">关于作者</a></p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td colspan="3"><img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="100%" /></td>
        </tr>
        <tr align="left" valign="top">
            <td><br />
            </td>
            <td><img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="4" /></td>
            <td width="100%">
            <p>胡
            键，西安交通大学硕士，2000 年毕业后一直从事软件开发。2002 年开始使用 Java，在平时的项目开发中经常采用 OpenSource
            的工具，如 Ant、Maven、Hibernate、Struts 等，目前正在研究信息集成方面的规范和技术。可以通过<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#106;&#105;&#97;&#110;&#104;&#103;&#114;&#101;&#97;&#116;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;&#63;&#99;&#99;&#61;">jianhgreat@hotmail.com</a>与他取得联系，或访问个人blog：<a href="http://blog.donews.com/foxgem/">http://blog.donews.com/foxgem/</a>。</p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/sealyu/aggbug/266483.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-04-20 09:13 <a href="http://www.blogjava.net/sealyu/archive/2009/04/20/266483.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring Security学习总结(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/04/09/264588.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 09 Apr 2009 01:45:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/04/09/264588.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/264588.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/04/09/264588.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/264588.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/264588.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在认识SpringSecurity之前，所有的权限验证逻辑都混杂在业务逻辑中，用户的每个操作以前可能都需要对用户是否有进行该项操作的权限进行判断，来达到认证授权的目的。类似这样的权限验证逻辑代码被分散在系统的许多地方，难以维护。AOP（Aspect OrientedProgramming）和SpringSecurity为我们的应用程序很好的解决了此类问题，正如系统日志，事务管理等这...&nbsp;&nbsp;<a href='http://www.blogjava.net/sealyu/archive/2009/04/09/264588.html'>阅读全文</a><img src ="http://www.blogjava.net/sealyu/aggbug/264588.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-04-09 09:45 <a href="http://www.blogjava.net/sealyu/archive/2009/04/09/264588.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>