<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_盛世唐朝</title><subtitle type="text">收集自己正学习的资料，方便自己学习，免得到处查找资料。</subtitle><id>http://feed.cnblogs.com/blog/u/13382/rss</id><updated>2012-01-29T10:35:59Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/13382/rss"/><entry><id>http://www.cnblogs.com/dynasty/archive/2012/01/29/2331384.html</id><title type="text">FreeMarker简介及其语法</title><summary type="text">1.Freemarker简介 FreeMarker允许Java servlet保持图形设计同应用程序逻辑的分离，这是通过在模板中密封HTML完成的。模板用servlet提供的数据动态地生成 HTML。模板语言是强大的直观的，编译器速度快，输出接近静态HTML页面的速度。 FreeMarker是一个模板引擎，一个基于模板生成文本输出的通用工具，使用纯Java编写 FreeMarker被设计用来生成HTML Web页面，特别是基于MVC模式的应用程序 虽然FreeMarker具有一些编程的能力，但通常由Java程序准备要显示的数据，由FreeMarker生成页面，通过模板显示准备的数据 F...</summary><published>2012-01-29T10:36:00Z</published><updated>2012-01-29T10:36:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2331384.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2331384.html"/><content type="html">&lt;p&gt;&lt;font size="5"&gt;1.Freemarker简介&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FreeMarker允许Java servlet保持图形设计同应用程序逻辑的分离，这是通过在模板中密封HTML完成的。模板用servlet提供的数据动态地生成 HTML。模板语言是强大的直观的，编译器速度快，输出接近静态HTML页面的速度。&lt;/p&gt;&#xD;
&lt;div &gt;&lt;/div&gt;　　FreeMarker是一个模板引擎，一个基于模板生成文本输出的通用工具，使用纯Java编写&lt;br /&gt;&#xD;
&lt;div &gt;&lt;/div&gt;　　FreeMarker被设计用来生成HTML Web页面，特别是基于MVC模式的应用程序&lt;br /&gt;&#xD;
&lt;div &gt;&lt;/div&gt;　　虽然FreeMarker具有一些编程的能力，但通常由Java程序准备要显示的数据，由FreeMarker生成页面，通过模板显示准备的数据&lt;br /&gt;&#xD;
&lt;div &gt;&lt;/div&gt;　　FreeMarker不是一个Web应用框架，而适合作为Web应用框架一个组件&lt;br /&gt;&#xD;
&lt;div &gt;&lt;/div&gt;　　FreeMarker与容器无关，因为它并不知道HTTP或Servlet；FreeMarker同样可以应用于非Web应用程序环境&lt;br /&gt;&#xD;
&lt;div &gt;&lt;/div&gt;　　FreeMarker更适合作为Model2框架（如Struts）的视图组件，你也可以在模板中使用JSP标记库&lt;br /&gt;&#xD;
&lt;div &gt;&lt;/div&gt;&#xD;
&lt;p&gt;FreeMarker是免费的&lt;/p&gt;&#xD;
&lt;p&gt;&lt;font color="#00ff00"&gt; &lt;/font&gt;&lt;font color="#ff0000"&gt;模板 + 数据模型 = 输出&lt;/font&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;font color="#000000" size="5"&gt;2.Freemarker语法&lt;/font&gt;&lt;/p&gt;&lt;font color="#000000" size="2"&gt;&#xD;
&lt;div &gt;&#xD;
&lt;p&gt;FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主要由如下4个部分组成:&lt;br /&gt;&lt;strong&gt;1,文本:直接输出的部分&lt;br /&gt;2,注释:&amp;lt;#-- ... --&amp;gt;格式部分,不会输出&lt;br /&gt;3,插值:即${...}或#{...}格式的部分,将使用数据模型中的部分替代输出&lt;br /&gt;4,FTL指令:FreeMarker指定,和HTML标记类似,名字前加#予以区分,不会输出&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;下面是一个FreeMarker模板的例子,包含了以上所说的4个部分&lt;br /&gt;&amp;lt;html&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;title&amp;gt;Welcome!&amp;lt;/title&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;#-- 注释部分 --&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;#-- 下面使用插值 --&amp;gt;&lt;br /&gt;&amp;lt;h1&amp;gt;Welcome ${user} !&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;We have these animals:&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;u1&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;#-- 使用FTL指令 --&amp;gt;&lt;br /&gt;&amp;lt;#list animals as being&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;li&amp;gt;${being.name} for ${being.price} Euros&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;#list&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;u1&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: large"&gt;&lt;font size="4"&gt;1, FTL指令规则&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在FreeMarker中,使用FTL标签来使用指令,FreeMarker有3种FTL标签,这和HTML标签是完全类似的.&lt;br /&gt;1,开始标签:&amp;lt;#directivename parameter&amp;gt;&lt;br /&gt;2,结束标签:&amp;lt;/#directivename&amp;gt;&lt;br /&gt;3,空标签:&amp;lt;#directivename parameter/&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;实际上,使用标签时前面的符号#也可能变成@,如果该指令是一个用户指令而不是系统内建指令时,应将#符号改成@符号.&lt;br /&gt;使用FTL标签时,应该有正确的嵌套,而不是交叉使用,这和XML标签的用法完全一样.如果全用不存在的指令,FreeMarker不会使用模板输出,而是产生一个错误消息.FreeMarker会忽略FTL标签中的空白字符.值得注意的是&amp;lt; , /&amp;gt; 和指令之间不允许有空白字符.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: large"&gt;&lt;font size="4"&gt;2, 插值规则&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker的插值有如下两种类型:1,通用插值${expr};2,数字格式化插值:#{expr}或#{expr;format}&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;2.1 通用插值&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;对于通用插值,又可以分为以下4种情况:&lt;br /&gt;1,插值结果为字符串值:直接输出表达式结果&lt;br /&gt;2,插值结果为数字值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串函数格式化单个插值,如下面的例子:&lt;br /&gt;&amp;lt;#settion number_format="currency"/&amp;gt;&lt;br /&gt;&amp;lt;#assign answer=42/&amp;gt;&lt;br /&gt;${answer}&lt;br /&gt;${answer?string} &amp;lt;#-- the same as ${answer} --&amp;gt;&lt;br /&gt;${answer?string.number}&lt;br /&gt;${answer?string.currency}&lt;br /&gt;${answer?string.percent}&lt;br /&gt;${answer}&lt;br /&gt;输出结果是:&lt;br /&gt;$42.00&lt;br /&gt;$42.00&lt;br /&gt;42&lt;br /&gt;$42.00&lt;br /&gt;4,200%&lt;br /&gt;3,插值结果为日期值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串函数格式化单个插值,如下面的例子:&lt;br /&gt;${lastUpdated?string("yyyy-MM-dd HH:mm:ss zzzz")}&lt;br /&gt;${lastUpdated?string("EEE, MMM d, ''yy")}&lt;br /&gt;${lastUpdated?string("EEEE, MMMM dd, yyyy, hh:mm:ss a '('zzz')'")}&lt;br /&gt;输出结果是:&lt;br /&gt;2008-04-08 08:08:08 Pacific Daylight Time&lt;br /&gt;Tue, Apr 8, '03&lt;br /&gt;Tuesday, April 08, 2003, 08:08:08 PM (PDT)&lt;br /&gt;4,插值结果为布尔值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串函数格式化单个插值,如下面的例子:&lt;br /&gt;&amp;lt;#assign foo=true/&amp;gt;&lt;br /&gt;${foo?string("yes", "no")}&lt;br /&gt;输出结果是:&lt;br /&gt;yes&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;2.2 数字格式化插值&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;数字格式化插值可采用#{expr;format}形式来格式化数字,其中format可以是:&lt;br /&gt;mX:小数部分最小X位&lt;br /&gt;MX:小数部分最大X位&lt;br /&gt;如下面的例子:&lt;br /&gt;&amp;lt;#assign x=2.582/&amp;gt;&lt;br /&gt;&amp;lt;#assign y=4/&amp;gt;&lt;br /&gt;#{x; M2} &amp;lt;#-- 输出2.58 --&amp;gt;&lt;br /&gt;#{y; M2} &amp;lt;#-- 输出4 --&amp;gt;&lt;br /&gt;#{x; m2} &amp;lt;#-- 输出2.6 --&amp;gt;&lt;br /&gt;#{y; m2} &amp;lt;#-- 输出4.0 --&amp;gt;&lt;br /&gt;#{x; m1M2} &amp;lt;#-- 输出2.58 --&amp;gt;&lt;br /&gt;#{x; m1M2} &amp;lt;#-- 输出4.0 --&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: large"&gt;&lt;font size="4"&gt;3, 表达式&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;表达式是FreeMarker模板的核心功能,表达式放置在插值语法${}之中时,表明需要输出表达式的值;表达式语法也可与FreeMarker标签结合,用于控制输出.实际上FreeMarker的表达式功能非常强大,它不仅支持直接指定值,输出变量值,也支持字符串格式化输出和集合访问等功能.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.1 直接指定值&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;使用直接指定值语法让FreeMarker直接输出插值中的值,而不是输出变量值.直接指定值可以是字符串,数值,布尔值,集合和MAP对象.&lt;/p&gt;&#xD;
&lt;p&gt;1,字符串&lt;br /&gt;直接指定字符串值使用单引号或双引号限定,如果字符串值中包含特殊字符需要转义,看下面的例子:&lt;br /&gt;${"我的文件保存在C:\\盘"}&lt;br /&gt;${'我名字是\"annlee\"'}&lt;br /&gt;输出结果是: &lt;br /&gt;我的文件保存在C:\盘&lt;br /&gt;我名字是"annlee"&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker支持如下转义字符:&lt;br /&gt;\";双引号(u0022)&lt;br /&gt;\';单引号(u0027)&lt;br /&gt;\\;反斜杠(u005C)&lt;br /&gt;\n;换行(u000A)&lt;br /&gt;\r;回车(u000D)&lt;br /&gt;\t;Tab(u0009)&lt;br /&gt;\b;退格键(u0008)&lt;br /&gt;\f;Form feed(u000C)&lt;br /&gt;\l;&amp;lt;&lt;br /&gt;\g;&amp;gt;&lt;br /&gt;\a;&amp;amp;&lt;br /&gt;\{;{&lt;br /&gt;\xCode;直接通过4位的16进制数来指定Unicode码,输出该unicode码对应的字符.&lt;/p&gt;&#xD;
&lt;p&gt;如果某段文本中包含大量的特殊符号,FreeMarker提供了另一种特殊格式:可以在指定字符串内容的引号前增加r标记,在r标记后的文件将会直接输出.看如下代码:&lt;br /&gt;${r"${foo}"}&lt;br /&gt;${r"C:\foo\bar"}&lt;br /&gt;输出结果是:&lt;br /&gt;${foo}&lt;br /&gt;C:\foo\bar&lt;/p&gt;&#xD;
&lt;p&gt;2,数值&lt;br /&gt;表达式中的数值直接输出,不需要引号.小数点使用"."分隔,不能使用分组","符号.FreeMarker目前还不支持科学计数法,所以"1E3"是错误的.在FreeMarker表达式中使用数值需要注意以下几点:&lt;br /&gt;1,数值不能省略小数点前面的0,所以".5"是错误的写法&lt;br /&gt;2,数值8 , +8 , 8.00都是相同的&lt;/p&gt;&#xD;
&lt;p&gt;3,布尔值&lt;br /&gt;直接使用true和false,不使用引号.&lt;/p&gt;&#xD;
&lt;p&gt;4,集合&lt;br /&gt;集合以方括号包括,各集合元素之间以英文逗号","分隔,看如下的例子:&lt;br /&gt;&amp;lt;#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x&amp;gt;&lt;br /&gt;${x}&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;br /&gt;输出结果是:&lt;br /&gt;星期一&lt;br /&gt;星期二&lt;br /&gt;星期三&lt;br /&gt;星期四&lt;br /&gt;星期五&lt;br /&gt;星期六&lt;br /&gt;星期天&lt;/p&gt;&#xD;
&lt;p&gt;除此之外,集合元素也可以是表达式,例子如下:&lt;br /&gt;[2 + 2, [1, 2, 3, 4], "whatnot"]&lt;/p&gt;&#xD;
&lt;p&gt;还可以使用数字范围定义数字集合,如2..5等同于[2, 3, 4, 5],但是更有效率.注意,使用数字范围来定义集合时无需使用方括号,数字范围也支持反递增的数字范围,如5..2&lt;/p&gt;&#xD;
&lt;p&gt;5,Map对象&lt;br /&gt;Map对象使用花括号包括,Map中的key-value对之间以英文冒号":"分隔,多组key-value对之间以英文逗号","分隔.下面是一个例子:&lt;br /&gt;{"语文":78, "数学":80}&lt;br /&gt;Map对象的key和value都是表达式,但是key必须是字符串&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.2 输出变量值&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker的表达式输出变量时,这些变量可以是顶层变量,也可以是Map对象中的变量,还可以是集合中的变量,并可以使用点(.)语法来访问Java对象的属性.下面分别讨论这些情况&lt;/p&gt;&#xD;
&lt;p&gt;1,顶层变量&lt;br /&gt;所谓顶层变量就是直接放在数据模型中的值,例如有如下数据模型:&lt;br /&gt;Map root = new HashMap();&amp;nbsp;&amp;nbsp; //创建数据模型&lt;br /&gt;root.put("name","annlee");&amp;nbsp;&amp;nbsp; //name是一个顶层变量&lt;/p&gt;&#xD;
&lt;p&gt;对于顶层变量,直接使用${variableName}来输出变量值,变量名只能是字母,数字,下划线,$,@和#的组合,且不能以数字开头号.为了输出上面的name的值,可以使用如下语法:&lt;br /&gt;${name}&lt;/p&gt;&#xD;
&lt;p&gt;2,输出集合元素&lt;br /&gt;如果需要输出集合元素,则可以根据集合元素的索引来输出集合元素,集合元素的索引以方括号指定.假设有索引:&lt;br /&gt;["星期一","星期二","星期三","星期四","星期五","星期六","星期天"].该索引名为week,如果需要输出星期三,则可以使用如下语法:&lt;br /&gt;${week[2]}&amp;nbsp;&amp;nbsp; //输出第三个集合元素&lt;/p&gt;&#xD;
&lt;p&gt;此外,FreeMarker还支持返回集合的子集合,如果需要返回集合的子集合,则可以使用如下语法:&lt;br /&gt;week[3..5]&amp;nbsp;&amp;nbsp; //返回week集合的子集合,子集合中的元素是week集合中的第4-6个元素&lt;/p&gt;&#xD;
&lt;p&gt;3,输出Map元素&lt;br /&gt;这里的Map对象可以是直接HashMap的实例,甚至包括JavaBean实例,对于JavaBean实例而言,我们一样可以把其当成属性为key,属性值为value的Map实例.为了输出Map元素的值,可以使用点语法或方括号语法.假如有下面的数据模型:&lt;br /&gt;Map root = new HashMap();&lt;br /&gt;Book book = new Book();&lt;br /&gt;Author author = new Author();&lt;br /&gt;author.setName("annlee");&lt;br /&gt;author.setAddress("gz");&lt;br /&gt;book.setName("struts2");&lt;br /&gt;book.setAuthor(author);&lt;br /&gt;root.put("info","struts");&lt;br /&gt;root.put("book", book);&lt;/p&gt;&#xD;
&lt;p&gt;为了访问数据模型中名为struts2的书的作者的名字,可以使用如下语法:&lt;br /&gt;book.author.name&amp;nbsp;&amp;nbsp;&amp;nbsp; //全部使用点语法&lt;br /&gt;book["author"].name&lt;br /&gt;book.author["name"]&amp;nbsp;&amp;nbsp;&amp;nbsp; //混合使用点语法和方括号语法&lt;br /&gt;book["author"]["name"]&amp;nbsp;&amp;nbsp; //全部使用方括号语法&lt;/p&gt;&#xD;
&lt;p&gt;使用点语法时,变量名字有顶层变量一样的限制,但方括号语法没有该限制,因为名字可以是任意表达式的结果.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.3, 字符串操作&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker的表达式对字符串操作非常灵活,可以将字符串常量和变量连接起来,也可以返回字符串的子串等.&lt;/p&gt;&#xD;
&lt;p&gt;字符串连接有两种语法:&lt;br /&gt;1,使用${..}或#{..}在字符串常量部分插入表达式的值,从而完成字符串连接.&lt;br /&gt;2,直接使用连接运算符+来连接字符串&lt;/p&gt;&#xD;
&lt;p&gt;例如有如下数据模型:&lt;br /&gt;Map root = new HashMap(); root.put("user","annlee");&lt;br /&gt;下面将user变量和常量连接起来:&lt;br /&gt;${"hello, ${user}!"}&amp;nbsp;&amp;nbsp; //使用第一种语法来连接&lt;br /&gt;${"hello, " + user + "!"} //使用+号来连接&lt;br /&gt;上面的输出字符串都是hello,annlee!,可以看出这两种语法的效果完全一样.&lt;/p&gt;&#xD;
&lt;p&gt;值得注意的是,${..}只能用于文本部分,不能用于表达式,下面的代码是错误的:&lt;br /&gt;&amp;lt;#if ${isBig}&amp;gt;Wow!&amp;lt;/#if&amp;gt;&lt;br /&gt;&amp;lt;#if "${isBig}"&amp;gt;Wow!&amp;lt;/#if&amp;gt;&lt;br /&gt;应该写成:&amp;lt;#if isBig&amp;gt;Wow!&amp;lt;/#if&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;截取子串可以根据字符串的索引来进行,截取子串时如果只指定了一个索引值,则用于取得字符串中指定索引所对应的字符;如果指定两个索引值,则返回两个索引中间的字符串子串.假如有如下数据模型:&lt;br /&gt;Map root = new HashMap(); root.put("book","struts2,freemarker");&lt;br /&gt;可以通过如下语法来截取子串:&lt;br /&gt;${book[0]}${book[4]}&amp;nbsp;&amp;nbsp; //结果是su&lt;br /&gt;${book[1..4]}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //结果是tru&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.4 集合连接运算符&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这里所说的集合运算符是将两个集合连接成一个新的集合,连接集合的运算符是+,看如下的例子:&lt;br /&gt;&amp;lt;#list ["星期一","星期二","星期三"] + ["星期四","星期五","星期六","星期天"] as x&amp;gt;&lt;br /&gt;${x}&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;br /&gt;输出结果是:星期一 星期二 星期三 星期四 星期五 星期六 星期天&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.5 Map连接运算符&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Map对象的连接运算符也是将两个Map对象连接成一个新的Map对象,Map对象的连接运算符是+,如果两个Map对象具有相同的key,则右边的值替代左边的值.看如下的例子:&lt;br /&gt;&amp;lt;#assign scores = {"语文":86,"数学":78} + {"数学":87,"Java":93}&amp;gt;&lt;br /&gt;语文成绩是${scores.语文}&lt;br /&gt;数学成绩是${scores.数学}&lt;br /&gt;Java成绩是${scores.Java}&lt;br /&gt;输出结果是:&lt;br /&gt;语文成绩是86&lt;br /&gt;数学成绩是87&lt;br /&gt;Java成绩是93&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.6 算术运算符&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , % 看如下的代码:&lt;br /&gt;&amp;lt;#assign x=5&amp;gt;&lt;br /&gt;${ x * x - 100 }&lt;br /&gt;${ x /2 }&lt;br /&gt;${ 12 %10 }&lt;br /&gt;输出结果是:&lt;br /&gt;-75&amp;nbsp;&amp;nbsp; 2.5&amp;nbsp;&amp;nbsp; 2&lt;/p&gt;&#xD;
&lt;p&gt;在表达式中使用算术运算符时要注意以下几点:&lt;br /&gt;1,运算符两边的运算数字必须是数字&lt;br /&gt;2,使用+运算符时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串再连接,如:${3 + "5"},结果是:35&lt;/p&gt;&#xD;
&lt;p&gt;使用内建的int函数可对数值取整,如:&lt;br /&gt;&amp;lt;#assign x=5&amp;gt;&lt;br /&gt;${ (x/2)?int }&lt;br /&gt;${ 1.1?int }&lt;br /&gt;${ 1.999?int }&lt;br /&gt;${ -1.1?int }&lt;br /&gt;${ -1.999?int }&lt;br /&gt;结果是:2 1 1 -1 -1&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.7 比较运算符&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;表达式中支持的比较运算符有如下几个:&lt;br /&gt;1,=或者==:判断两个值是否相等.&lt;br /&gt;2,!=:判断两个值是否不等.&lt;br /&gt;3,&amp;gt;或者gt:判断左边值是否大于右边值&lt;br /&gt;4,&amp;gt;=或者gte:判断左边值是否大于等于右边值&lt;br /&gt;5,&amp;lt;或者lt:判断左边值是否小于右边值&lt;br /&gt;6,&amp;lt;=或者lte:判断左边值是否小于等于右边值&lt;/p&gt;&#xD;
&lt;p&gt;注意:=和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且FreeMarker是精确比较,"x","x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替&amp;gt;会有更好的效果,因为FreeMarker会把&amp;gt;解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:&amp;lt;#if (x&amp;gt;y)&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.8 逻辑运算符&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;逻辑运算符有如下几个:&lt;br /&gt;逻辑与:&amp;amp;&amp;amp;&lt;br /&gt;逻辑或:||&lt;br /&gt;逻辑非:!&lt;br /&gt;逻辑运算符只能作用于布尔值,否则将产生错误&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.9 内建函数&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker还提供了一些内建函数来转换输出,可以在任何变量后紧跟?,?后紧跟内建函数,就可以通过内建函数来轮换输出变量.下面是常用的内建的字符串函数:&lt;br /&gt;html:对字符串进行HTML编码&lt;br /&gt;cap_first:使字符串第一个字母大写&lt;br /&gt;lower_case:将字符串转换成小写&lt;br /&gt;upper_case:将字符串转换成大写&lt;br /&gt;trim:去掉字符串前后的空白字符&lt;/p&gt;&#xD;
&lt;p&gt;下面是集合的常用内建函数&lt;br /&gt;size:获取序列中元素的个数&lt;/p&gt;&#xD;
&lt;p&gt;下面是数字值的常用内建函数&lt;br /&gt;int:取得数字的整数部分,结果带符号&lt;/p&gt;&#xD;
&lt;p&gt;例如:&lt;br /&gt;&amp;lt;#assign test="Tom &amp;amp; Jerry"&amp;gt;&lt;br /&gt;${test?html}&lt;br /&gt;${test?upper_case?html}&lt;br /&gt;结果是:Tom &amp;amp;amp; Jerry&amp;nbsp;&amp;nbsp; TOM &amp;amp;amp; JERRY&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.10 空值处理运算符&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker对空值的处理非常严格,FreeMarker的变量必须有值,没有被赋值的变量就会抛出异常,因为FreeMarker未赋值的变量强制出错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误.这里所说的空值,实际上也包括那些并不存在的变量,对于一个Java的null值而言,我们认为这个变量是存在的,只是它的值为null,但对于FreeMarker模板而言,它无法理解null值,null值和不存在的变量完全相同.&lt;/p&gt;&#xD;
&lt;p&gt;为了处理缺失变量,FreeMarker提供了两个运算符:&lt;br /&gt;!:指定缺失变量的默认值&lt;br /&gt;??:判断某个变量是否存在&lt;/p&gt;&#xD;
&lt;p&gt;其中,!运算符的用法有如下两种:&lt;br /&gt;variable!或variable!defaultValue,第一种用法不给缺失的变量指定默认值,表明默认值是空字符串,长度为0的集合,或者长度为0的Map对象.&lt;/p&gt;&#xD;
&lt;p&gt;使用!指定默认值时,并不要求默认值的类型和变量类型相同.使用??运算符非常简单,它总是返回一个布尔值,用法为:variable??,如果该变量存在,返回true,否则返回false&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;3.11 运算符的优先级&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker中的运算符优先级如下(由高到低排列):&lt;br /&gt;1,一元运算符:!&lt;br /&gt;2,内建函数:?&lt;br /&gt;3,乘除法:*, / , %&lt;br /&gt;4,加减法:- , +&lt;br /&gt;5,比较:&amp;gt; , &amp;lt; , &amp;gt;= , &amp;lt;= (lt , lte , gt , gte)&lt;br /&gt;6,相等:== , = , !=&lt;br /&gt;7,逻辑与:&amp;amp;&amp;amp;&lt;br /&gt;8,逻辑或:||&lt;br /&gt;9,数字范围:..&lt;/p&gt;&#xD;
&lt;p&gt;实际上,我们在开发过程中应该使用括号来严格区分,这样的可读性好,出错少&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: large"&gt;&lt;font size="4"&gt;4 FreeMarker的常用指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;FreeMarker的FTL指令也是模板的重要组成部分,这些指令可实现对数据模型所包含数据的抚今迭代,分支控制.除此之外,还有一些重要的功能,也是通过FTL指令来实现的.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.1 if指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下:&lt;br /&gt;&amp;lt;#if condition&amp;gt;...&lt;br /&gt;&amp;lt;#elseif condition&amp;gt;...&lt;br /&gt;&amp;lt;#elseif condition&amp;gt;...&lt;br /&gt;&amp;lt;#else&amp;gt; ...&lt;br /&gt;&amp;lt;/#if&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;例子如下:&lt;br /&gt;&amp;lt;#assign age=23&amp;gt;&lt;br /&gt;&amp;lt;#if (age&amp;gt;60)&amp;gt;老年人&lt;br /&gt;&amp;lt;#elseif (age&amp;gt;40)&amp;gt;中年人&lt;br /&gt;&amp;lt;#elseif (age&amp;gt;20)&amp;gt;青年人&lt;br /&gt;&amp;lt;#else&amp;gt; 少年人&lt;br /&gt;&amp;lt;/#if&amp;gt;&lt;br /&gt;输出结果是:青年人&lt;br /&gt;上面的代码中的逻辑表达式用括号括起来主要是因为里面有&amp;gt;符号,由于FreeMarker会将&amp;gt;符号当成标签的结束字符,可能导致程序出错,为了避免这种情况,我们应该在凡是出现这些符号的地方都使用括号.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.2 switch , case , default , break指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这些指令显然是分支指令,作用类似于Java的switch语句,switch指令的语法结构如下:&lt;br /&gt;&amp;lt;#switch value&amp;gt;&lt;br /&gt;&amp;lt;#case refValue&amp;gt;...&amp;lt;#break&amp;gt;&lt;br /&gt;&amp;lt;#case refValue&amp;gt;...&amp;lt;#break&amp;gt;&lt;br /&gt;&amp;lt;#default&amp;gt;...&lt;br /&gt;&amp;lt;/#switch&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.3 list, break指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;list指令是一个迭代输出指令,用于迭代输出数据模型中的集合,list指令的语法格式如下:&lt;br /&gt;&amp;lt;#list sequence as item&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;br /&gt;上面的语法格式中,sequence就是一个集合对象,也可以是一个表达式,但该表达式将返回一个集合对象,而item是一个任意的名字,就是被迭代输出的集合元素.此外,迭代集合对象时,还包含两个特殊的循环变量:&lt;br /&gt;item_index:当前变量的索引值&lt;br /&gt;item_has_next:是否存在下一个对象&lt;br /&gt;也可以使用&amp;lt;#break&amp;gt;指令跳出迭代&lt;/p&gt;&#xD;
&lt;p&gt;例子如下:&lt;br /&gt;&amp;lt;#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x&amp;gt;&lt;br /&gt;${x_index + 1}.${x}&amp;lt;#if x_has_next&amp;gt;,&amp;lt;/if&amp;gt;&lt;br /&gt;&amp;lt;#if x="星期四"&amp;gt;&amp;lt;#break&amp;gt;&amp;lt;/#if&amp;gt;&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.4 include指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;include指令的作用类似于JSP的包含指令,用于包含指定页.include指令的语法格式如下:&lt;br /&gt;&amp;lt;#include filename [options]&amp;gt;&lt;br /&gt;在上面的语法格式中,两个参数的解释如下:&lt;br /&gt;filename:该参数指定被包含的模板文件&lt;br /&gt;options:该参数可以省略,指定包含时的选项,包含encoding和parse两个选项,其中encoding指定包含页面时所用的解码集,而parse指定被包含文件是否作为FTL文件来解析,如果省略了parse选项值,则该选项默认是true.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.5 import指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;该指令用于导入FreeMarker模板中的所有变量,并将该变量放置在指定的Map对象中,import指令的语法格式如下:&lt;br /&gt;&amp;lt;#import "/lib/common.ftl" as com&amp;gt;&lt;br /&gt;上面的代码将导入/lib/common.ftl模板文件中的所有变量,交将这些变量放置在一个名为com的Map对象中.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.6 noparse指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;noparse指令指定FreeMarker不处理该指定里包含的内容,该指令的语法格式如下:&lt;br /&gt;&amp;lt;#noparse&amp;gt;...&amp;lt;/#noparse&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;看如下的例子:&lt;br /&gt;&amp;lt;#noparse&amp;gt;&lt;br /&gt;&amp;lt;#list books as book&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;${book.name}&amp;lt;td&amp;gt;作者:${book.author}&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;br /&gt;&amp;lt;/#noparse&amp;gt;&lt;br /&gt;输出如下:&lt;br /&gt;&amp;lt;#list books as book&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;${book.name}&amp;lt;td&amp;gt;作者:${book.author}&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.7 escape , noescape指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;escape指令导致body区的插值都会被自动加上escape表达式,但不会影响字符串内的插值,只会影响到body内出现的插值,使用escape指令的语法格式如下:&lt;br /&gt;&amp;lt;#escape identifier as expression&amp;gt;...&lt;br /&gt;&amp;lt;#noescape&amp;gt;...&amp;lt;/#noescape&amp;gt;&lt;br /&gt;&amp;lt;/#escape&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;看如下的代码:&lt;br /&gt;&amp;lt;#escape x as x?html&amp;gt;&lt;br /&gt;First name:${firstName}&lt;br /&gt;Last name:${lastName}&lt;br /&gt;Maiden name:${maidenName}&lt;br /&gt;&amp;lt;/#escape&amp;gt;&lt;br /&gt;上面的代码等同于:&lt;br /&gt;First name:${firstName?html}&lt;br /&gt;Last name:${lastName?html}&lt;br /&gt;Maiden name:${maidenName?html}&lt;/p&gt;&#xD;
&lt;p&gt;escape指令在解析模板时起作用而不是在运行时起作用,除此之外,escape指令也嵌套使用,子escape继承父escape的规则,如下例子:&lt;br /&gt;&amp;lt;#escape x as x?html&amp;gt;&lt;br /&gt;Customer Name:${customerName}&lt;br /&gt;Items to ship;&lt;br /&gt;&amp;lt;#escape x as itemCodeToNameMap[x]&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; ${itemCode1}&lt;br /&gt;&amp;nbsp;&amp;nbsp; ${itemCode2}&lt;br /&gt;&amp;nbsp;&amp;nbsp; ${itemCode3}&lt;br /&gt;&amp;nbsp;&amp;nbsp; ${itemCode4}&lt;br /&gt;&amp;lt;/#escape&amp;gt;&lt;br /&gt;&amp;lt;/#escape&amp;gt;&lt;br /&gt;上面的代码类似于:&lt;br /&gt;Customer Name:${customerName?html}&lt;br /&gt;Items to ship;&lt;br /&gt;${itemCodeToNameMap[itemCode1]?html}&lt;br /&gt;${itemCodeToNameMap[itemCode2]?html}&lt;br /&gt;${itemCodeToNameMap[itemCode3]?html}&lt;br /&gt;${itemCodeToNameMap[itemCode4]?html}&lt;/p&gt;&#xD;
&lt;p&gt;对于放在escape指令中所有的插值而言,这此插值将被自动加上escape表达式,如果需要指定escape指令中某些插值无需添加escape表达式,则应该使用noescape指令,放在noescape指令中的插值将不会添加escape表达式.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.8 assign指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;assign指令在前面已经使用了多次,它用于为该模板页面创建或替换一个顶层变量,assign指令的用法有多种,包含创建或替换一个顶层变量,或者创建或替换多个变量等,它的最简单的语法如下:&amp;lt;#assign name=value [in namespacehash]&amp;gt;,这个用法用于指定一个名为name的变量,该变量的值为value,此外,FreeMarker允许在使用assign指令里增加in子句,in子句用于将创建的name变量放入namespacehash命名空间中.&lt;/p&gt;&#xD;
&lt;p&gt;assign指令还有如下用法:&amp;lt;#assign name1=value1 name2=value2 ... nameN=valueN [in namespacehash]&amp;gt;,这个语法可以同时创建或替换多个顶层变量,此外,还有一种复杂的用法,如果需要创建或替换的变量值是一个复杂的表达式,则可以使用如下语法格式:&amp;lt;#assign name [in namespacehash]&amp;gt;capture this&amp;lt;/#assign&amp;gt;,在这个语法中,是指将assign指令的内容赋值给name变量.如下例子:&lt;br /&gt;&amp;lt;#assign x&amp;gt;&lt;br /&gt;&amp;lt;#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as n&amp;gt;&lt;br /&gt;${n}&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;br /&gt;&amp;lt;/#assign&amp;gt;&lt;br /&gt;${x}&lt;br /&gt;上面的代码将产生如下输出:星期一 星期二 星期三 星期四 星期五 星期六 星期天&lt;/p&gt;&#xD;
&lt;p&gt;虽然assign指定了这种复杂变量值的用法,但是我们也不要滥用这种用法,如下例子:&amp;lt;#assign x&amp;gt;Hello ${user}!&amp;lt;/#assign&amp;gt;,以上代码改为如下写法更合适:&amp;lt;#assign x="Hello ${user}!"&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.9 setting指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;该指令用于设置FreeMarker的运行环境,该指令的语法格式如下:&amp;lt;#setting name=value&amp;gt;,在这个格式中,name的取值范围包含如下几个:&lt;br /&gt;locale:该选项指定该模板所用的国家/语言选项&lt;br /&gt;number_format:指定格式化输出数字的格式&lt;br /&gt;boolean_format:指定两个布尔值的语法格式,默认值是true,false&lt;br /&gt;date_format,time_format,datetime_format:指定格式化输出日期的格式&lt;br /&gt;time_zone:设置格式化输出日期时所使用的时区&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small"&gt;&lt;font size="2"&gt;4.10 macro , nested , return指令&lt;/font&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;macro可以用于实现自定义指令,通过使用自定义指令,可以将一段模板片段定义成一个用户指令,使用macro指令的语法格式如下:&lt;br /&gt;&amp;lt;#macro name param1 param2 ... paramN&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;#nested loopvar1, loopvar2, ..., loopvarN&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;#return&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;/#macro&amp;gt;&lt;br /&gt;在上面的格式片段中,包含了如下几个部分:&lt;br /&gt;name:name属性指定的是该自定义指令的名字,使用自定义指令时可以传入多个参数&lt;br /&gt;paramX:该属性就是指定使用自定义指令时报参数,使用该自定义指令时,必须为这些参数传入值&lt;br /&gt;nested指令:nested标签输出使用自定义指令时的中间部分&lt;br /&gt;nested指令中的循环变量:这此循环变量将由macro定义部分指定,传给使用标签的模板&lt;br /&gt;return指令:该指令可用于随时结束该自定义指令.&lt;/p&gt;&#xD;
&lt;p&gt;看如下的例子:&lt;br /&gt;&amp;lt;#macro book&amp;gt;&amp;nbsp;&amp;nbsp; //定义一个自定义指令&lt;br /&gt;j2ee&lt;br /&gt;&amp;lt;/#macro&amp;gt;&lt;br /&gt;&amp;lt;@book /&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //使用刚才定义的指令&lt;br /&gt;上面的代码输出结果为:j2ee&lt;/p&gt;&#xD;
&lt;p&gt;在上面的代码中,可能很难看出自定义标签的用处,因为我们定义的book指令所包含的内容非常简单,实际上,自定义标签可包含非常多的内容,从而可以实现更好的代码复用.此外,还可以在定义自定义指令时,为自定义指令指定参数,看如下代码:&lt;br /&gt;&amp;lt;#macro book booklist&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //定义一个自定义指令booklist是参数&lt;br /&gt;&amp;lt;#list booklist as book&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; ${book}&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;br /&gt;&amp;lt;/#macro&amp;gt;&lt;br /&gt;&amp;lt;@book booklist=["spring","j2ee"] /&amp;gt;&amp;nbsp;&amp;nbsp; //使用刚刚定义的指令&lt;br /&gt;上面的代码为book指令传入了一个参数值,上面的代码的输出结果为:spring j2ee&lt;/p&gt;&#xD;
&lt;p&gt;不仅如此,还可以在自定义指令时使用nested指令来输出自定义指令的中间部分,看如下例子:&lt;br /&gt;&amp;lt;#macro page title&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt;FreeMarker示例页面 - ${title?html}&amp;lt;/title&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;h1&amp;gt;${title?html}&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;#nested&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //用于引入用户自定义指令的标签体&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&amp;lt;/#macro&amp;gt;&lt;br /&gt;上面的代码将一个HTML页面模板定义成一个page指令,则可以在其他页面中如此page指令:&lt;br /&gt;&amp;lt;#import "/common.ftl" as com&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //假设上面的模板页面名为common.ftl,导入页面&lt;br /&gt;&amp;lt;@com.page title="book list"&amp;gt;&lt;br /&gt;&amp;lt;u1&amp;gt;&lt;br /&gt;&amp;lt;li&amp;gt;spring&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;li&amp;gt;j2ee&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;&amp;lt;&lt;a&gt;&lt;u&gt;&lt;font color="#108ac6"&gt;/@com.page&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;从上面的例子可以看出,使用macro和nested指令可以非常容易地实现页面装饰效果,此外,还可以在使用nested指令时,指定一个或多个循环变量,看如下代码:&lt;br /&gt;&amp;lt;#macro book&amp;gt;&lt;br /&gt;&amp;lt;#nested 1&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //使用book指令时指定了一个循环变量值&lt;br /&gt;&amp;lt;#nested 2&amp;gt;&lt;br /&gt;&amp;lt;/#macro&amp;gt;&lt;br /&gt;&amp;lt;@book ;x&amp;gt; ${x} .图书&amp;lt;&lt;a&gt;&lt;u&gt;&lt;font color="#108ac6"&gt;/@book&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;&amp;gt;&lt;br /&gt;当使用nested指令传入变量值时,在使用该自定义指令时,就需要使用一个占位符(如book指令后的;x).上面的代码输出文本如下:&lt;br /&gt;1 .图书&amp;nbsp;&amp;nbsp;&amp;nbsp; 2 .图书&lt;/p&gt;&#xD;
&lt;p&gt;在nested指令中使用循环变量时,可以使用多个循环变量,看如下代码:&lt;br /&gt;&amp;lt;#macro repeat count&amp;gt;&lt;br /&gt;&amp;lt;#list 1..count as x&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //使用nested指令时指定了三个循环变量&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;#nested x, x/2, x==count&amp;gt;&lt;br /&gt;&amp;lt;/#list&amp;gt;&lt;br /&gt;&amp;lt;/#macro&amp;gt;&lt;br /&gt;&amp;lt;@repeat count=4 ; c halfc last&amp;gt;&lt;br /&gt;${c}. ${halfc}&amp;lt;#if last&amp;gt; Last! &amp;lt;/#if&amp;gt;&lt;br /&gt;&amp;lt;&lt;a&gt;&lt;u&gt;&lt;font color="#108ac6"&gt;/@repeat&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;&amp;gt;&lt;br /&gt;上面的输出结果为:&lt;br /&gt;1. 0.5&amp;nbsp;&amp;nbsp; 2. 1&amp;nbsp;&amp;nbsp; 3. 1.5&amp;nbsp;&amp;nbsp; 4. 2 Last;&lt;/p&gt;&#xD;
&lt;p&gt;return指令用于结束macro指令,一旦在macro指令中执行了return指令,则FreeMarker不会继续处理macro指令里的内容,看如下代码:&lt;br /&gt;&amp;lt;#macro book&amp;gt;&lt;br /&gt;spring&lt;br /&gt;&amp;lt;#return&amp;gt;&lt;br /&gt;j2ee&lt;br /&gt;&amp;lt;/#macro&amp;gt;&lt;br /&gt;&amp;lt;@book /&amp;gt;&lt;br /&gt;上面的代码输出:spring,而j2ee位于return指令之后,不会输出.&lt;/p&gt;&lt;/div&gt;&lt;/font&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2331384.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2331384.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2012/01/29/2331359.html</id><title type="text">spring3.0注解</title><summary type="text">使用 @Repository、@Service、@Controller 和 @Component 将类标识为 BeanSpring 自 2.0 版本开始，陆续引入了一些注解用于简化 Spring 的开发。@Repository 注解便属于最先引入的一批，它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean。具体只需将该注解标注在 DAO 类上即可。同时，为了让 Spring 能够扫描类路径中的类并识别出 @Repository 注解，需要在 XML 配置文件中启用 Bean 的自动扫描功能，这可以通过 &amp;lt;context:component-scan/&amp;gt; 实现。如</summary><published>2012-01-29T10:06:00Z</published><updated>2012-01-29T10:06:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2331359.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2331359.html"/><content type="html">&lt;div &gt;&#xD;
&lt;p&gt;使用 @Repository、@Service、@Controller 和 @Component 将类标识为 Bean&lt;/p&gt;&#xD;
&lt;p&gt;Spring 自 2.0 版本开始，陆续引入了一些注解用于简化 Spring 的开发。@Repository 注解便属于最先引入的一批，它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean。具体只需将该注解标注在 DAO 类上即可。同时，为了让 Spring 能够扫描类路径中的类并识别出 @Repository 注解，需要在 XML 配置文件中启用 Bean 的自动扫描功能，这可以通过 &amp;lt;context:component-scan/&amp;gt; 实现。如下所示：&lt;/p&gt;&#xD;
&lt;p&gt;// 首先使用 @Repository 将 DAO 类声明为 Bean &lt;br /&gt;&amp;nbsp;package bookstore.dao; &lt;br /&gt;&amp;nbsp;@Repository &lt;br /&gt;&amp;nbsp;public class UserDaoImpl implements UserDao{ &amp;#8230;&amp;#8230; } &lt;/p&gt;&#xD;
&lt;p&gt;// 其次，在 XML 配置文件中启动 Spring 的自动扫描功能&lt;br /&gt;&amp;nbsp;&amp;lt;beans &amp;#8230; &amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#8230;&amp;#8230;&lt;br /&gt;&amp;nbsp;&amp;lt;context:component-scan base-package=&amp;#8221;bookstore.dao&amp;#8221; /&amp;gt; &lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;&amp;nbsp;&amp;lt;/beans&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;如此，我们就不再需要在 XML 中显式使用 &amp;lt;bean/&amp;gt; 进行 Bean 的配置。Spring 在容器初始化时将自动扫描 base-package 指定的包及其子包下的所有 class 文件，所有标注了 @Repository 的类都将被注册为 Spring Bean。&lt;/p&gt;&#xD;
&lt;p&gt;为什么 @Repository 只能标注在 DAO 类上呢？这是因为该注解的作用不只是将类识别为 Bean，同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。 Spring 本身提供了一个丰富的并且是与具体的数据访问技术无关的数据访问异常结构，用于封装不同的持久层框架抛出的异常，使得异常独立于底层的框架。&lt;/p&gt;&#xD;
&lt;p&gt;Spring 2.5 在 @Repository 的基础上增加了功能类似的额外三个注解：@Component、@Service、@Constroller，它们分别用于软件系统的不同层次：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58;&amp;#37;&amp;#69;&amp;#50;&amp;#37;&amp;#56;&amp;#48;&amp;#37;&amp;#65;&amp;#50;&amp;#64;&amp;#67;&amp;#111;&amp;#109;&amp;#112;&amp;#111;&amp;#110;&amp;#101;&amp;#110;&amp;#116;"&gt;&lt;font color="#366900"&gt;&amp;#8226;@Component&lt;/font&gt;&lt;/a&gt; 是一个泛化的概念，仅仅表示一个组件 (Bean) ，可以作用在任何层次。&lt;br /&gt;&lt;a href="&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58;&amp;#37;&amp;#69;&amp;#50;&amp;#37;&amp;#56;&amp;#48;&amp;#37;&amp;#65;&amp;#50;&amp;#64;&amp;#83;&amp;#101;&amp;#114;&amp;#118;&amp;#105;&amp;#99;&amp;#101;"&gt;&lt;font color="#366900"&gt;&amp;#8226;@Service&lt;/font&gt;&lt;/a&gt; 通常作用在业务层，但是目前该功能与 @Component 相同。&lt;br /&gt;&lt;a href="&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58;&amp;#37;&amp;#69;&amp;#50;&amp;#37;&amp;#56;&amp;#48;&amp;#37;&amp;#65;&amp;#50;&amp;#64;&amp;#67;&amp;#111;&amp;#110;&amp;#115;&amp;#116;&amp;#114;&amp;#111;&amp;#108;&amp;#108;&amp;#101;&amp;#114;"&gt;&lt;font color="#366900"&gt;&amp;#8226;@Constroller&lt;/font&gt;&lt;/a&gt; 通常作用在控制层，但是目前该功能与 @Component 相同。&lt;br /&gt;通过在类上使用 @Repository、@Component、@Service 和 @Constroller 注解，Spring 会自动创建相应的 BeanDefinition 对象，并注册到 ApplicationContext 中。这些类就成了 Spring 受管组件。这三个注解除了作用于不同软件层次的类，其使用方式与 @Repository 是完全相同的。&lt;/p&gt;&#xD;
&lt;p&gt;另外，除了上面的四个注解外，用户可以创建自定义的注解，然后在注解上标注 @Component，那么，该自定义注解便具有了与所 @Component 相同的功能。不过这个功能并不常用。&lt;/p&gt;&#xD;
&lt;p&gt;当一个 Bean 被自动检测到时，会根据那个扫描器的 BeanNameGenerator 策略生成它的 bean 名称。默认情况下，对于包含 name 属性的 @Component、@Repository、 @Service 和 @Controller，会把 name 取值作为 Bean 的名字。如果这个注解不包含 name 值或是其他被自定义过滤器发现的组件，默认 Bean 名称会是小写开头的非限定类名。如果你不想使用默认 bean 命名策略，可以提供一个自定义的命名策略。首先实现 BeanNameGenerator 接口，确认包含了一个默认的无参数构造方法。然后在配置扫描器时提供一个全限定类名，如下所示：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;beans ...&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;context:component-scan &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; base-package="a.b" name-generator="a.SimpleNameGenerator"/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;/beans&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;与通过 XML 配置的 Spring Bean 一样，通过上述注解标识的 Bean，其默认作用域是"singleton"，为了配合这四个注解，在标注 Bean 的同时能够指定 Bean 的作用域，Spring 2.5 引入了 @Scope 注解。使用该注解时只需提供作用域的名称就行了，如下所示：&lt;/p&gt;&#xD;
&lt;p&gt;@Scope("prototype") &lt;br /&gt;&amp;nbsp;@Repository &lt;br /&gt;&amp;nbsp;public class Demo { &amp;#8230; } &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;如果你想提供一个自定义的作用域解析策略而不使用基于注解的方法，只需实现 ScopeMetadataResolver 接口，确认包含一个默认的没有参数的构造方法。然后在配置扫描器时提供全限定类名：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;context:component-scan base-package="a.b"&lt;br /&gt;&amp;nbsp;scope-resolver="footmark.SimpleScopeResolver" /&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;/p&gt;&#xD;
&lt;p&gt;回页首&lt;/p&gt;&#xD;
&lt;p&gt;使用 @PostConstruct 和 @PreDestroy 指定生命周期回调方法&lt;/p&gt;&#xD;
&lt;p&gt;Spring Bean 是受 Spring IoC 容器管理，由容器进行初始化和销毁的（prototype 类型由容器初始化之后便不受容器管理），通常我们不需要关注容器对 Bean 的初始化和销毁操作，由 Spring 经过构造函数或者工厂方法创建的 Bean 就是已经初始化完成并立即可用的。然而在某些情况下，可能需要我们手工做一些额外的初始化或者销毁操作，这通常是针对一些资源的获取和释放操作。 Spring 1.x 为此提供了两种方式供用户指定执行生命周期回调的方法。&lt;/p&gt;&#xD;
&lt;p&gt;第一种方式是实现 Spring 提供的两个接口：InitializingBean 和 DisposableBean。如果希望在 Bean 初始化完成之后执行一些自定义操作，则可以让 Bean 实现 InitializingBean 接口，该接口包含一个 afterPropertiesSet() 方法，容器在为该 Bean 设置了属性之后，将自动调用该方法；如果 Bean 实现了 DisposableBean 接口，则容器在销毁该 Bean 之前，将调用该接口的 destroy() 方法。这种方式的缺点是，让 Bean 类实现 Spring 提供的接口，增加了代码与 Spring 框架的耦合度，因此不推荐使用。&lt;/p&gt;&#xD;
&lt;p&gt;第二种方式是在 XML 文件中使用 &amp;lt;bean&amp;gt; 的 init-method 和 destroy-method 属性指定初始化之后和销毁之前的回调方法，代码无需实现任何接口。这两个属性的取值是相应 Bean 类中的初始化和销毁方法，方法名任意，但是方法不能有参数。示例如下：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;bean id=&amp;#8221;userService&amp;#8221; &lt;br /&gt;&amp;nbsp;class=&amp;#8221;bookstore.service.UserService&amp;#8221; &lt;br /&gt;&amp;nbsp;init-method=&amp;#8221;init&amp;#8221; destroy-method=&amp;#8221;destroy&amp;#8221;&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#8230;&lt;br /&gt;&amp;nbsp;&amp;lt;/bean&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;Spring 2.5 在保留以上两种方式的基础上，提供了对 JSR-250 的支持。JSR-250 规范定义了两个用于指定声明周期方法的注解：@PostConstruct 和 @PreDestroy。这两个注解使用非常简单，只需分别将他们标注于初始化之后执行的回调方法或者销毁之前执行的回调方法上。由于使用了注解，因此需要配置相应的 Bean 后处理器，亦即在 XML 中增加如下一行：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;context:annotation-config /&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;比较上述三种指定生命周期回调方法的方式，第一种是不建议使用的，不但其用法不如后两种方式灵活，而且无形中增加了代码与框架的耦合度。后面两种方式开发者可以根据使用习惯选择其中一种，但是最好不要混合使用，以免增加维护的难度。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;/p&gt;&#xD;
&lt;p&gt;回页首&lt;/p&gt;&#xD;
&lt;p&gt;使用 @Required 进行 Bean 的依赖检查&lt;/p&gt;&#xD;
&lt;p&gt;依赖检查的作用是，判断给定 Bean 的相应 Setter 方法是否都在实例化的时候被调用了。而不是判断字段是否已经存在值了。Spring 进行依赖检查时，只会判断属性是否使用了 Setter 注入。如果某个属性没有使用 Setter 注入，即使是通过构造函数已经为该属性注入了值，Spring 仍然认为它没有执行注入，从而抛出异常。另外，Spring 只管是否通过 Setter 执行了注入，而对注入的值却没有任何要求，即使注入的 &amp;lt;null/&amp;gt;，Spring 也认为是执行了依赖注入。&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;bean&amp;gt; 标签提供了 dependency-check 属性用于进行依赖检查。该属性的取值包括以下几种：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;#8226;none -- 默认不执行依赖检查。可以在 &amp;lt;beans&amp;gt; 标签上使用 default-dependency-check 属性改变默认值。&lt;br /&gt;&amp;#8226;simple -- 对原始基本类型和集合类型进行检查。&lt;br /&gt;&amp;#8226;objects -- 对复杂类型进行检查（除了 simple 所检查类型之外的其他类型）。&lt;br /&gt;&amp;#8226;all -- 对所有类型进行检查。&lt;br /&gt;旧版本使用 dependency-check 在配置文件中设置，缺点是粒度较粗。使用 Spring2.0 提供的 @Required 注解，提供了更细粒度的控制。@Required 注解只能标注在 Setter 方法之上。因为依赖注入的本质是检查 Setter 方法是否被调用了，而不是真的去检查属性是否赋值了以及赋了什么样的值。如果将该注解标注在非 setXxxx() 类型的方法则被忽略。&lt;/p&gt;&#xD;
&lt;p&gt;为了让 Spring 能够处理该注解，需要激活相应的 Bean 后处理器。要激活该后处理器，只需在 XML 中增加如下一行即可。&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;context:annotation-config/&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;当某个被标注了 @Required 的 Setter 方法没有被调用，则 Spring 在解析的时候会抛出异常，以提醒开发者对相应属性进行设置。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;/p&gt;&#xD;
&lt;p&gt;回页首&lt;/p&gt;&#xD;
&lt;p&gt;使用 @Resource、@Autowired 和 @Qualifier 指定 Bean 的自动装配策略&lt;/p&gt;&#xD;
&lt;p&gt;自动装配是指，Spring 在装配 Bean 的时候，根据指定的自动装配规则，将某个 Bean 所需要引用类型的 Bean 注入进来。&amp;lt;bean&amp;gt; 元素提供了一个指定自动装配类型的 autowire 属性，该属性有如下选项：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;#8226;no -- 显式指定不使用自动装配。&lt;br /&gt;&amp;#8226;byName -- 如果存在一个和当前属性名字一致的 Bean，则使用该 Bean 进行注入。如果名称匹配但是类型不匹配，则抛出异常。如果没有匹配的类型，则什么也不做。&lt;br /&gt;&amp;#8226;byType -- 如果存在一个和当前属性类型一致的 Bean ( 相同类型或者子类型 )，则使用该 Bean 进行注入。byType 能够识别工厂方法，即能够识别 factory-method 的返回类型。如果存在多个类型一致的 Bean，则抛出异常。如果没有匹配的类型，则什么也不做。&lt;br /&gt;&amp;#8226;constructor -- 与 byType 类似，只不过它是针对构造函数注入而言的。如果当前没有与构造函数的参数类型匹配的 Bean，则抛出异常。使用该种装配模式时，优先匹配参数最多的构造函数。&lt;br /&gt;&amp;#8226;autodetect -- 根据 Bean 的自省机制决定采用 byType 还是 constructor 进行自动装配。如果 Bean 提供了默认的构造函数，则采用 byType；否则采用 constructor 进行自动装配。&lt;br /&gt;当使用 byType 或者 constructor 类型的自动装配的时候，自动装配也支持引用类型的数组或者使用了泛型的集合，这样，Spring 就会检查容器中所有类型匹配的 Bean，组成集合或者数组后执行注入。对于使用了泛型的 Map 类型，如果键是 String 类型，则 Spring 也会自动执行装配，将所有类型匹配的 Bean 作为值，Bean 的名字作为键。&lt;/p&gt;&#xD;
&lt;p&gt;我们可以给 &amp;lt;beans&amp;gt; 增加 default-autowire 属性，设置默认的自动封装策略。默认值为"no"。如果使用自动装配的同时，也指定了 property 或者 constructor-arg 标签，则显式指定的值将覆盖自动装配的值。目前的自动封装不支持简单类型，比如基本类型、String、Class，以及它们的数组类型。&lt;/p&gt;&#xD;
&lt;p&gt;在按类型匹配的时候 ( 可能是 byType、constructor、autodetect)，同一个类型可能存在多个 Bean，如果被注入的属性是数组、集合或者 Map，这可能没有问题，但是如果只是简单的引用类型，则会抛出异常。解决方法有如下几种：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;#8226;取消该 Bean 的自动装配特性，使用显式的注入。我们可能不希望某个 Bean 被当作其他 Bean 执行自动封装时的候选对象，我们可以给该 &amp;lt;bean&amp;gt; 增加 autowire-candidate="false"。(autowire-candidate 属性和 autowire 属性相互独立，互不相干。某个 Bean 可以将 autowire-candidate 设置为 false，同时使用 autowire 特性。) 另外，我们可以设置 &amp;lt;beans&amp;gt; 的 default-autowire-candidates 属性，可以在该属性中指定可以用于自动装配候选 Bean 的匹配模式，比如 default-autowire-candidates="*serv,*dao"，这表示所有名字以 serv 或者 dao 结尾的 Bean 被列为候选，其他则忽略，相当于其他 Bean 都指定为 autowire-candidate="false"，此时可以显式为 &amp;lt;bean&amp;gt; 指定 autowire-candidate="true"。在 &amp;lt;bean&amp;gt; 上指定的设置要覆盖 &amp;lt;beans&amp;gt; 上指定的设置。&lt;br /&gt;&amp;#8226;如果在多个类型相同的 Bean 中有首选的 Bean，那么可以将该 &amp;lt;bean&amp;gt; 的 primary 属性设置为 "true" ，这样自动装配时便优先使用该 Bean 进行装配。此时不能将 autowire-candidate 设为 false。&lt;br /&gt;&amp;#8226;如果使用的是 Java 5 以上版本，可以使用注解进行更细粒度的控制。&lt;br /&gt;使用 @Autowired 和 @Qualifier 注解执行自动装配&lt;/p&gt;&#xD;
&lt;p&gt;使用 @Autowired 注解进行装配，只能是根据类型进行匹配。@Autowired 注解可以用于 Setter 方法、构造函数、字段，甚至普通方法，前提是方法必须有至少一个参数。@Autowired 可以用于数组和使用泛型的集合类型。然后 Spring 会将容器中所有类型符合的 Bean 注入进来。@Autowired 标注作用于 Map 类型时，如果 Map 的 key 为 String 类型，则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来，用 Bean 的 id 或 name 作为 Map 的 key。&lt;/p&gt;&#xD;
&lt;p&gt;@Autowired 标注作用于普通方法时，会产生一个副作用，就是在容器初始化该 Bean 实例的时候就会调用该方法。当然，前提是执行了自动装配，对于不满足装配条件的情况，该方法也不会被执行。&lt;/p&gt;&#xD;
&lt;p&gt;当标注了 @Autowired 后，自动注入不能满足，则会抛出异常。我们可以给 @Autowired 标注增加一个 required=false 属性，以改变这个行为。另外，每一个类中只能有一个构造函数的 @Autowired.required() 属性为 true。否则就出问题了。如果用 @Autowired 同时标注了多个构造函数，那么，Spring 将采用贪心算法匹配构造函数 ( 构造函数最长 )。&lt;/p&gt;&#xD;
&lt;p&gt;@Autowired 还有一个作用就是，如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上，那么 Spring 会自动注入这些实现类的实例，不需要额外的操作。&lt;/p&gt;&#xD;
&lt;p&gt;当容器中存在多个 Bean 的类型与需要注入的相同时，注入将不能执行，我们可以给 @Autowired 增加一个候选值，做法是在 @Autowired 后面增加一个 @Qualifier 标注，提供一个 String 类型的值作为候选的 Bean 的名字。举例如下：&lt;/p&gt;&#xD;
&lt;p&gt;@Autowired(required=false) &lt;br /&gt;&amp;nbsp;@Qualifier("ppp") &lt;br /&gt;&amp;nbsp;public void setPerson(person p){} &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;@Qualifier 甚至可以作用于方法的参数 ( 对于方法只有一个参数的情况，我们可以将 @Qualifer 标注放置在方法声明上面，但是推荐放置在参数前面 )，举例如下：&lt;/p&gt;&#xD;
&lt;p&gt;@Autowired(required=false) &lt;br /&gt;&amp;nbsp;public void sayHello(@Qualifier("ppp")Person p,String name){} &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;我们可以在配置文件中指定某个 Bean 的 qualifier 名字，方法如下：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;bean id="person" &amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;qualifier value="ppp"/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;/bean&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;如果没有明确指定 Bean 的 qualifier 名字，那么默认名字就是 Bean 的名字。通常，qualifier 应该是有业务含义的，例如 "domain"，"persistent" 等，而不应该是类似 "person" 方式。&lt;/p&gt;&#xD;
&lt;p&gt;我们还可以将 @Qualifier 标注在集合类型上，那么所有 qualifier 名字与指定值相同的 Bean 都将被注入进来。&lt;/p&gt;&#xD;
&lt;p&gt;最后，配置文件中需要指定每一个自定义注解的属性值。我们可以使用 &amp;lt;meta&amp;gt; 标签来代替 &amp;lt;qualifier/&amp;gt; 标签，如果 &amp;lt;meta&amp;gt; 标签和 &amp;lt;qualifier/&amp;gt; 标签同时出现，那么优先使用 &amp;lt;qualifier&amp;gt; 标签。如果没有 &amp;lt;qualifier&amp;gt; 标签，那么会用 &amp;lt;meta&amp;gt; 提供的键值对来封装 &amp;lt;qualifier&amp;gt; 标签。示例如下：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;bean &amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;qualifier type="MovieQualifier"&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;attribute key="format" value="VHS"/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;attribute key="genre" value="Comedy"/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;/qualifier&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;/bean&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;bean &amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;meta key="format" value="DVD"/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;meta key="genre" value="Action"/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;/bean&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;@Autowired 注解对应的后处理注册与前面相似，只需在配置文件中增加如下一行即可：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;context:annotation-config/&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;如果 @Autowired 注入的是 BeanFactory、ApplicationContext、ResourceLoader 等系统类型，那么则不需要 @Qualifier，此时即使提供了 @Qualifier 注解，也将会被忽略；而对于自定义类型的自动装配，如果使用了 @Qualifier 注解并且没有名字与之匹配的 Bean，则自动装配匹配失败。&lt;/p&gt;&#xD;
&lt;p&gt;使用 JSR-250 中的 @Resource 和 @Qualifier 注解&lt;/p&gt;&#xD;
&lt;p&gt;如果希望根据 name 执行自动装配，那么应该使用 JSR-250 提供的 @Resource 注解，而不应该使用 @Autowired 与 @Qualifier 的组合。&lt;/p&gt;&#xD;
&lt;p&gt;@Resource 使用 byName 的方式执行自动封装。@Resource 标注可以作用于带一个参数的 Setter 方法、字段，以及带一个参数的普通方法上。@Resource 注解有一个 name 属性，用于指定 Bean 在配置文件中对应的名字。如果没有指定 name 属性，那么默认值就是字段或者属性的名字。@Resource 和 @Qualifier 的配合虽然仍然成立，但是 @Qualifier 对于 @Resource 而言，几乎与 name 属性等效。&lt;/p&gt;&#xD;
&lt;p&gt;如果 @Resource 没有指定 name 属性，那么使用 byName 匹配失败后，会退而使用 byType 继续匹配，如果再失败，则抛出异常。在没有为 @Resource 注解显式指定 name 属性的前提下，如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上，那么 Spring 会自动注入这些实现类的实例，不需要额外的操作。此时 name 属性不需要指定 ( 或者指定为"")，否则注入失败；如果使用了 @Qualifier，则该注解将被忽略。而对于用户自定义类型的注入，@Qualifier 和 name 等价，并且不被忽略。&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;bean&amp;gt; 的 primary 和 autowire-candidate 属性对 @Resource、@Autowired 仍然有效。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;/p&gt;&#xD;
&lt;p&gt;回页首&lt;/p&gt;&#xD;
&lt;p&gt;使用 @Configuration 和 @Bean 进行 Bean 的声明&lt;/p&gt;&#xD;
&lt;p&gt;虽然 2.0 版本发布以来，Spring 陆续提供了十多个注解，但是提供的这些注解只是为了在某些情况下简化 XML 的配置，并非要取代 XML 配置方式。这一点可以从 Spring IoC 容器的初始化类可以看出：ApplicationContext 接口的最常用的实现类是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext，以及面向 Portlet 的 XmlPortletApplicationContext 和面向 web 的 XmlWebApplicationContext，它们都是面向 XML 的。Spring 3.0 新增了另外两个实现类：AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext。从名字便可以看出，它们是为注解而生，直接依赖于注解作为容器配置信息来源的 IoC 容器初始化类。由于 AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 web 版本，其用法与后者相比几乎没有什么差别，因此本文将以 AnnotationConfigApplicationContext 为例进行讲解。&lt;/p&gt;&#xD;
&lt;p&gt;AnnotationConfigApplicationContext 搭配上 @Configuration 和 @Bean 注解，自此，XML 配置方式不再是 Spring IoC 容器的唯一配置方式。两者在一定范围内存在着竞争的关系，但是它们在大多数情况下还是相互协作的关系，两者的结合使得 Spring IoC 容器的配置更简单，更强大。&lt;/p&gt;&#xD;
&lt;p&gt;之前，我们将配置信息集中写在 XML 中，如今使用注解，配置信息的载体由 XML 文件转移到了 Java 类中。我们通常将用于存放配置信息的类的类名以 &amp;#8220;Config&amp;#8221; 结尾，比如 AppDaoConfig.java、AppServiceConfig.java 等等。我们需要在用于指定配置信息的类上加上 @Configuration 注解，以明确指出该类是 Bean 配置的信息源。并且 Spring 对标注 Configuration 的类有如下要求：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;#8226;配置类不能是 final 的；&lt;br /&gt;&amp;#8226;配置类不能是本地化的，亦即不能将配置类定义在其他类的方法内部；&lt;br /&gt;&amp;#8226;配置类必须有一个无参构造函数。&lt;br /&gt;AnnotationConfigApplicationContext 将配置类中标注了 @Bean 的方法的返回值识别为 Spring Bean，并注册到容器中，受 IoC 容器管理。@Bean 的作用等价于 XML 配置中的 &amp;lt;bean/&amp;gt; 标签。示例如下：&lt;/p&gt;&#xD;
&lt;p&gt;@Configuration &lt;br /&gt;&amp;nbsp;public class BookStoreDaoConfig{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Bean &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public UserDao userDao(){ return new UserDaoImpl();} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Bean &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public BookDao bookDao(){return new BookDaoImpl();} &lt;br /&gt;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;Spring 在解析到以上文件时，将识别出标注 @Bean 的所有方法，执行之，并将方法的返回值 ( 这里是 UserDaoImpl 和 BookDaoImpl 对象 ) 注册到 IoC 容器中。默认情况下，Bean 的名字即为方法名。因此，与以上配置等价的 XML 配置如下：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;bean id=&amp;#8221;userDao&amp;#8221; class=&amp;#8221;bookstore.dao.UserDaoImpl&amp;#8221;/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;bean id=&amp;#8221;bookDao&amp;#8221; class=&amp;#8221;bookstore.dao.BookDaoImpl&amp;#8221;/&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;@Bean 具有以下四个属性：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;#8226;name -- 指定一个或者多个 Bean 的名字。这等价于 XML 配置中 &amp;lt;bean&amp;gt; 的 name 属性。&lt;br /&gt;&amp;#8226;initMethod -- 容器在初始化完 Bean 之后，会调用该属性指定的方法。这等价于 XML 配置中 &amp;lt;bean&amp;gt; 的 init-method 属性。&lt;br /&gt;&amp;#8226;destroyMethod -- 该属性与 initMethod 功能相似，在容器销毁 Bean 之前，会调用该属性指定的方法。这等价于 XML 配置中 &amp;lt;bean&amp;gt; 的 destroy-method 属性。&lt;br /&gt;&amp;#8226;autowire -- 指定 Bean 属性的自动装配策略，取值是 Autowire 类型的三个静态属性。Autowire.BY_NAME，Autowire.BY_TYPE，Autowire.NO。与 XML 配置中的 autowire 属性的取值相比，这里少了 constructor，这是因为 constructor 在这里已经没有意义了。&lt;br /&gt;@Bean 没有直接提供指定作用域的属性，可以通过 @Scope 来实现该功能，关于 @Scope 的用法已在上文列举。&lt;/p&gt;&#xD;
&lt;p&gt;下面讲解基于注解的容器初始化。AnnotationConfigApplicationContext 提供了三个构造函数用于初始化容器。&lt;/p&gt;&#xD;
&lt;p&gt;&amp;#8226;AnnotationConfigApplicationContext()：该构造函数初始化一个空容器，容器不包含任何 Bean 信息，需要在稍后通过调用其 register() 方法注册配置类，并调用 refresh() 方法刷新容器。&lt;br /&gt;&amp;#8226;AnnotationConfigApplicationContext(Class&amp;lt;?&amp;gt;... annotatedClasses)：这是最常用的构造函数，通过将涉及到的配置类传递给该构造函数，以实现将相应配置类中的 Bean 自动注册到容器中。&lt;br /&gt;&amp;#8226;AnnotationConfigApplicationContext(String... basePackages)：该构造函数会自动扫描以给定的包及其子包下的所有类，并自动识别所有的 Spring Bean，将其注册到容器中。它不但识别标注 @Configuration 的配置类并正确解析，而且同样能识别使用 @Repository、@Service、@Controller、@Component 标注的类。&lt;br /&gt;除了使用上面第三种类型的构造函数让容器自动扫描 Bean 的配置信息以外，AnnotationConfigApplicationContext 还提供了 scan() 方法，其功能与上面也类似，该方法主要用在容器初始化之后动态增加 Bean 至容器中。调用了该方法以后，通常需要立即手动调用 refresh() 刷新容器，以让变更立即生效。&lt;/p&gt;&#xD;
&lt;p&gt;需要注意的是，AnnotationConfigApplicationContext 在解析配置类时，会将配置类自身注册为一个 Bean，因为 @Configuration 注解本身定义时被 @Component 标注了。因此可以说，一个 @Configuration 同时也是一个 @Component。大多数情况下，开发者用不到该 Bean，并且在理想情况下，该 Bean 应该是对开发者透明的。@Configuration 的定义如下所示：&lt;/p&gt;&#xD;
&lt;p&gt;@Target({ElementType.TYPE}) &lt;br /&gt;&amp;nbsp;@Retention(RetentionPolicy.RUNTIME) &lt;br /&gt;&amp;nbsp;@Documented &lt;br /&gt;&amp;nbsp;@Component &lt;br /&gt;&amp;nbsp;public @interface Configuration { &lt;br /&gt;&amp;nbsp;String value() default ""; &lt;br /&gt;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;在一般的项目中，为了结构清晰，通常会根据软件的模块或者结构定义多个 XML 配置文件，然后再定义一个入口的配置文件，该文件使用 &amp;lt;import/&amp;gt; 将其他的配置文件组织起来。最后只需将该文件传给 ClassPathXmlApplicationContext 的构造函数即可。针对基于注解的配置，Spring 也提供了类似的功能，只需定义一个入口配置类，并在该类上使用 @Import 注解引入其他的配置类即可，最后只需要将该入口类传递给 AnnotationConfigApplicationContext。具体示例如下：&lt;/p&gt;&#xD;
&lt;p&gt;@Configuration &lt;br /&gt;&amp;nbsp;@Import({BookStoreServiceConfig.class,BookStoreDaoConfig.class}) &lt;br /&gt;&amp;nbsp;public class BookStoreConfig{ &amp;#8230; } &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;/p&gt;&#xD;
&lt;p&gt;回页首&lt;/p&gt;&#xD;
&lt;p&gt;混合使用 XML 与注解进行 Bean 的配置&lt;/p&gt;&#xD;
&lt;p&gt;设计 @Configuration 和 @Bean 的初衷，并不是为了完全取代 XML，而是为了在 XML 之外多一种可行的选择。由于 Spring 自发布以来，Spring 开发小组便不断简化 XML 配置，使得 XML 配置方式已经非常成熟，加上 Spring 2.0 以后出现了一系列命名空间的支持，使得 XML 配置方式成为了使用简单、功能强大的 Bean 定义方式。而且，XML 配置的一些高级功能目前还没有相关注解能够直接支持。因此，在目前的多数项目中，要么使用纯粹的 XML 配置方式进行 Bean 的配置，要么使用以注解为主，XML 为辅的配置方式进行 Bean 的配置。&lt;/p&gt;&#xD;
&lt;p&gt;之所以会出现两者共存的情况，主要归结为三个原因：其一，目前绝大多数采用 Spring 进行开发的项目，几乎都是基于 XML 配置方式的，Spring 在引入注解的同时，必须保证注解能够与 XML 和谐共存，这是前提；其二，由于注解引入较晚，因此功能也没有发展多年的 XML 强大，因此，对于复杂的配置，注解还很难独当一面，在一段时间内仍然需要 XML 的配合才能解决问题。除此之外，Spring 的 Bean 的配置方式与 Spring 核心模块之间是解耦的，因此，改变配置方式对 Spring 的框架自身是透明的。Spring 可以通过使用 Bean 后处理器 (BeanPostProcessor) 非常方便的增加对于注解的支持。这在技术实现上非常容易的事情。&lt;/p&gt;&#xD;
&lt;p&gt;要使用混合配置方式，首先需要判断以哪一种配置方式为主。对这个问题的不同回答将会直接影响到实现的方式。然而大可不必为此伤脑筋，因为不论是以 XML 为主，还是以注解为主，配置方式都是简单而且容易理解的。这里不存在错误的决定，因为仅仅是表现方式不一样。我们首先假设以 XML 配置为主的情况。&lt;/p&gt;&#xD;
&lt;p&gt;对于已经存在的大型项目，可能初期是以 XML 进行 Bean 配置的，后续逐渐加入了注解的支持，这时我们只需在 XML 配置文件中将被 @Configuration 标注的类定义为普通的 &amp;lt;bean&amp;gt;，同时注册处理注解的 Bean 后处理器即可。示例如下：&lt;/p&gt;&#xD;
&lt;p&gt;// 假设存在如下的 @Configuration 类：&lt;br /&gt;&amp;nbsp;package bookstore.config; &lt;br /&gt;&amp;nbsp;import bookstore.dao.*; &lt;br /&gt;&amp;nbsp;@Configuration &lt;br /&gt;&amp;nbsp;public class MyConfig{ &lt;br /&gt;&amp;nbsp;@Bean &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public UserDao userDao(){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new UserDaoImpl(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br /&gt;&amp;nbsp;} &lt;br /&gt;此时，只需在 XML 中作如下声明即可：&lt;br /&gt;&amp;nbsp;&amp;lt;beans &amp;#8230; &amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#8230;&amp;#8230;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;context:annotation-config /&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;bean class=&amp;#8221;demo.config.MyConfig&amp;#8221;/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;/beans&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;由于启用了针对注解的 Bean 后处理器，因此在 ApplicationContext 解析到 MyConfig 类时，会发现该类标注了 @Configuration 注解，随后便会处理该类中标注 @Bean 的方法，将这些方法的返回值注册为容器总的 Bean。&lt;/p&gt;&#xD;
&lt;p&gt;对于以上的方式，如果存在多个标注了 @Configuration 的类，则需要在 XML 文件中逐一列出。另一种方式是使用前面提到的自动扫描功能，配置如下：&lt;/p&gt;&#xD;
&lt;p&gt;&amp;lt;context:component-scan base-package=&amp;#8221;bookstore.config&amp;#8221; /&amp;gt; &lt;br /&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;如此，Spring 将扫描所有 demo.config 包及其子包中的类，识别所有标记了 @Component、@Controller、@Service、@Repository 注解的类，由于 @Configuration 注解本身也用 @Component 标注了，Spring 将能够识别出 @Configuration 标注类并正确解析之。&lt;/p&gt;&#xD;
&lt;p&gt;对于以注解为中心的配置方式，只需使用 @ImportResource 注解引入存在的 XML 即可，如下所示：&lt;/p&gt;&amp;nbsp;@Configuration &lt;br /&gt;&amp;nbsp;@ImportResource(&amp;#8220;classpath:/bookstore/config/spring-beans.xml&amp;#8221;) &lt;br /&gt;&amp;nbsp;public class MyConfig{ &lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;// 容器的初始化过程和纯粹的以配置为中心的方式一致：&lt;br /&gt;&amp;nbsp;AnnotationConfigApplicationContext ctx = &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new AnnotationConfigApplicationContext(MyConfig.class); &lt;br /&gt;&amp;#8230;&amp;#8230; &lt;/div&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2331359.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2331359.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2012/01/29/2330913.html</id><title type="text">web.xml文件中的7个错误的安全配置</title><summary type="text">关于Java的web.xml文件中配置认证和授权有大 量 的 文章。本文不再去重新讲解如何配置角色、保护web资源和设置不同类型的认证，让我们来看看web.xml文件中的一些常见的安全错误配置。(1) 自定义的错误页面没有配置默认情况下，Java Web应用在发生错误时会将详细的错误信息展示出来，这将暴露服务器版本和详细的堆栈信息，在有些情况下，甚至会显示Java代码的代码片段。这些信息对为他们的病毒需找更多信息的黑客来说是一种恩惠。幸运的是，通过配置web.xml文件来展示自定义的错误页面是非常容易的。使用如下的配置后无论服务器在任何时候发生HTTP500错误，一个非常好的错误页面就会被显示</summary><published>2012-01-29T02:37:00Z</published><updated>2012-01-29T02:37:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2330913.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2330913.html"/><content type="html">&lt;p&gt;关于Java的web.xml文件中配置认证和授权有&lt;a href="http://java.dzone.com/articles/understanding-web-security" target="_blank"&gt;&lt;font color="#0066cc"&gt;大&lt;/font&gt;&lt;/a&gt; &lt;a href="http://onjava.com/pub/a/onjava/2001/08/06/webform.html" target="_blank"&gt;&lt;font color="#0066cc"&gt;量&lt;/font&gt;&lt;/a&gt; &lt;a href="http://www.cafesoft.com/products/cams/tomcat-security.html" target="_blank"&gt;&lt;font color="#0066cc"&gt;的&lt;/font&gt;&lt;/a&gt; &lt;a href="http://publib.boulder.ibm.com/infocenter/iseries/v5r4/topic/rzamy/50/sec/secaweb.htm" target="_blank"&gt;&lt;font color="#0066cc"&gt;文章&lt;/font&gt;&lt;/a&gt;。本文不再去重新讲解如何配置角色、保护web资源和设置不同类型的认证，让我们来看看web.xml文件中的一些常见的安全错误配置。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;(1) 自定义的错误页面没有配置&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;默认情况下，Java Web应用在发生错误时会将详细的错误信息展示出来，这将暴露服务器版本和详细的堆栈信息，在有些情况下，甚至会显示Java代码的代码片段。这些信息对为他们的病毒需找更多信息的黑客来说是一种恩惠。幸运的是，通过配置web.xml文件来展示自定义的错误页面是非常容易的。使用如下的配置后无论服务器在任何时候发生HTTP500错误，一个非常好的错误页面就会被显示出来。你可以为HTTP状态码添加另外的错误页面。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div  id="highlighter_517914"&gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="http://javasight.net/2011/06/serven-security-misconfiguration-in-web-dot-xml/#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table cellspacing="0" cellpadding="0" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;error-page&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;error-softwaresecurity&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;500&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;error-softwaresecurity&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;location&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;/path/to/error.jsp&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;location&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;error-page&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&#xD;
&lt;p&gt;另外，web.xml文件应该被配置以防止详细的错误堆栈信息被显示出来，我们可以通过配置&amp;lt;exception-type&amp;gt;来实现。因为Throwable是Java中所有Exception和Error的基类，下面的代码片段将很好的确保堆栈信息不被服务器显示。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span id="more-196"&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div  id="highlighter_596889"&gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="http://javasight.net/2011/06/serven-security-misconfiguration-in-web-dot-xml/#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table cellspacing="0" cellpadding="0" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;error-page&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;exception-type&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;java.lang.Throwable&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;exception-type&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;location&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;/path/to/error.jsp&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;location&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;error-page&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&#xD;
&lt;p&gt;然而，如果你采用如下的处理方式，你依然会将堆栈信息展示出来：&lt;/p&gt;&lt;pre &gt;&amp;lt;%&#xD;
try {&#xD;
        String s = null;&#xD;
        s.length();&#xD;
} catch (Exception e) {&#xD;
        // don't do this!&#xD;
        e.printStackTrace(new PrintWriter(out));&#xD;
}&#xD;
%&amp;gt;&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;这里请记住在合理配置了你的web.xml文件后，需要使用合理的&lt;a href="http://javasight.net/2011/05/clean-code-clean-logs-part1/" target="_blank"&gt;&lt;font color="#0066cc"&gt;日志记录技巧&lt;/font&gt;&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;(2)绕过认证和授权&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;下面的代码片段展示了如何设置基于web的访问控制以便所有在&amp;#8221;安全&amp;#8221;目录中的一切只能被带有&amp;#8221;admin&amp;#8221;角色的用户访问。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div  id="highlighter_179561"&gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="http://javasight.net/2011/06/serven-security-misconfiguration-in-web-dot-xml/#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table cellspacing="0" cellpadding="0" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;div &gt;5&lt;/div&gt;&#xD;
&lt;div &gt;6&lt;/div&gt;&#xD;
&lt;div &gt;7&lt;/div&gt;&#xD;
&lt;div &gt;8&lt;/div&gt;&#xD;
&lt;div &gt;9&lt;/div&gt;&#xD;
&lt;div &gt;10&lt;/div&gt;&#xD;
&lt;div &gt;11&lt;/div&gt;&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;security-constraint&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;web-resource-collection&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;web-resource-name&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;secure&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;web-resource-name&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;url-pattern&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;/secure/*&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;url-pattern&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;http-method&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;GET&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;http-method&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;http-method&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;POST&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;http-method&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;web-resource-collection&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;auth-constraint&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;role-name&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;admin&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;role-name&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;auth-constraint&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;security-constraint&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&#xD;
&lt;p&gt;从常识观点来看，指定了GET和POST的&amp;lt;http-method&amp;gt;元素限定了*只有*GET和POST请求是被允许的。事实上不是这样，任意未列举的HTTP方法实际上都是允许使用的，即采用其他的HTTP方法可以绕过认证和授权。Arshan Dabirsiaghi有一个非常好的&lt;a href="http://www.aspectsecurity.com/documents/Bypassing_VBAAC_with_HTTP_Verb_Tampering.pdf" target="_blank"&gt;&lt;font color="#0066cc"&gt;论文&lt;/font&gt;&lt;/a&gt;总结了该问题并向你展示了如何使用上述配置中未列举的任意的HTTP动词（像HEAD）和完全假冒的动词（像TEST或JUNK）来绕过web.xml中配置的认证和授权保护。&lt;/p&gt;&#xD;
&lt;p&gt;幸运的是，解决方案非常简单。仅仅需要从web.xml文件中移除&amp;lt;http-method&amp;gt;元素即可。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;（3）SSL未配置&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在所有使用敏感数据的应用中，SSL都应该被配置以保护数据传输安全。当然你可以在web服务器上配置SSL，但是一旦你的应用服务器&lt;a href="http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html" target="_blank"&gt;&lt;font color="#0066cc"&gt;设置&lt;/font&gt;&lt;/a&gt;了合适的SSL key，那么在应用急启用SSL是非常容易的。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div  id="highlighter_724671"&gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="http://javasight.net/2011/06/serven-security-misconfiguration-in-web-dot-xml/#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table cellspacing="0" cellpadding="0" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;div &gt;5&lt;/div&gt;&#xD;
&lt;div &gt;6&lt;/div&gt;&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;security-constraint&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;... &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;user-data-constraint&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;transport-guarantee&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;CONFIDENTIAL&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;transport-guarantee&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;user-data-constraint&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;security-constraint&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&#xD;
&lt;p&gt;&lt;strong&gt;(4)未使用安全标示&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;很多web站点使用SSL进行认证，但是后面或者是阻止非SSL的的后续交互或者使得一部分网站内容仍然可以通过非SSL的访问。这使得会话的cookie（也就是JSESSIONID）容易受到session劫持攻击。要阻止它，cookie可以通过添加安全标志来创建，这确保了浏览器将不会在非SSL环境下传递cookie。&lt;/p&gt;&#xD;
&lt;p&gt;在Servlet规范的旧版本中，没有标准的方式来将JSESSIONID定义为安全的。现在在Servlet3.0中，&amp;lt;cookie-config&amp;gt;元素可以用于确保这个。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div  id="highlighter_627775"&gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="http://javasight.net/2011/06/serven-security-misconfiguration-in-web-dot-xml/#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table cellspacing="0" cellpadding="0" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;div &gt;5&lt;/div&gt;&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;cookie-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;secure&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;true&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;secure&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;cookie-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&#xD;
&lt;p&gt;&lt;strong&gt;（5）未使用HttpOnly标志&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;cookie可以使用HttpOnly标志创建，这将确保cookie不能被客户端脚本访问。这帮助减轻了一些常见的&lt;a href="http://blogs.sans.org/appsecstreetfighter/2010/02/22/top-25-series-rank-1-cross-site-scripting/" target="_blank"&gt;&lt;font color="#0066cc"&gt;XSS&lt;/font&gt;&lt;/a&gt;攻击。就像Security标志一样，旧版本的Servlet规范没有提供相应的支持。在Servlet3.0中可以如下的配置它：&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div  id="highlighter_59223"&gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="http://javasight.net/2011/06/serven-security-misconfiguration-in-web-dot-xml/#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table cellspacing="0" cellpadding="0" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;div &gt;5&lt;/div&gt;&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;cookie-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;http-only&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;true&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;http-only&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;cookie-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&#xD;
&lt;p&gt;除了Servlet3.0的这种新的标准的方式，旧版本的Tomcat允许在server.xml中使用供应商特定的useHttpOnly属性来启用它。该属性在Tomcat5.5和6中默认是禁用的。在Tomcat7中，该属性默认是启用的。因此即使你在web.xml中将其设置为false，然后部署在tomcat7中，除非你修改了server.xml文件，否则你的JSESSIONID依然是HttpOnly的。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;（6） 使用URL参数来跟踪session&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Servlet3.0规范中的&amp;lt;tracking-mode&amp;gt;允许你定义JSESSIONID是存储在cookie中还是URL参数中。如果会话ID存储在URL中，那么它可能会被无意的存储在多个地方，包括浏览器历史、代理服务器日志、引用日志和web日志等。暴露了会话ID使得网站被session劫持攻击的几率大增。然而，确保JSESSIONID被存储在cookie中非常容易：&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div  id="highlighter_343479"&gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="http://javasight.net/2011/06/serven-security-misconfiguration-in-web-dot-xml/#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table cellspacing="0" cellpadding="0" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;tracking-mode&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;COOKIE&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;tracking-mode&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&#xD;
&lt;p&gt;&lt;strong&gt;（7） 未设置会话超时时间&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;用户喜欢长时间的会话因为他们很方便。黑客喜欢长时间的会话因为他们有足够的时间来实施像session劫持攻击等。安全和可用性总是会出现冲突。一旦你知道如何使得你的会话存活，你可以按如下方法来配置活动时间：&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div  id="highlighter_457987"&gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="http://javasight.net/2011/06/serven-security-misconfiguration-in-web-dot-xml/#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table cellspacing="0" cellpadding="0" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code &gt;&amp;lt;&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-timeout&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;15&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-timeout&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt; &lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;/&lt;/code&gt;&lt;code &gt;&lt;strong&gt;&lt;font color="#006699"&gt;session-config&lt;/font&gt;&lt;/strong&gt;&lt;/code&gt;&lt;code &gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&#xD;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;构建和部署安全的应用需要从不同的受益人处获取需求。环境和配置和编码自身一样重要。通过思考这些常见的安全错误配置，希望你可以创建更加安全的应用。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2330913.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2330913.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2012/01/29/2330857.html</id><title type="text">web.xml配置说明</title><summary type="text">web.xml中classpath:和classpath*:, 有什么区别?classpath：只会到你的class路径中查找找文件;classpath*：不仅包含class路径，还包括jar文件中(class路径)进行查找.XML元素不仅是大小写敏感的，而且定义它们的次序也很重要。例如，web-app元素内所有servlet元素必须位于所有servlet- mapping元素（下一小节介绍）之前，而且还要位于与过滤器或文档相关的元素（如果有的话）之前。类似地，servlet 的servlet-name子元素也必须出现在servlet-class之前。filter------------&amp;gt</summary><published>2012-01-29T02:05:00Z</published><updated>2012-01-29T02:05:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2330857.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2330857.html"/><content type="html">&lt;p&gt;web.xml中classpath:和classpath*:, 有什么区别?&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;!-- google_ad_section_end --&gt;&lt;span csdnid="titleStyle"&gt;classpath：只会到你的class路径中查找找文件;&lt;br /&gt;classpath*：不仅包含class路径，还包括jar文件中(class路径)进行查找.&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span csdnid="titleStyle"&gt;&lt;/span&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span csdnid="titleStyle"&gt;XML元素不仅是大小写敏感的，而且定义它们的次序也很重要。例如，web-app元素内所有servlet元素必须位于所有servlet- mapping元素（下一小节介绍）之前，而且还要位于与过滤器或文档相关的元素（如果有的话）之前。类似地，servlet 的servlet-name子元素也必须出现在servlet-class之前。&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span csdnid="titleStyle"&gt;&lt;/span&gt;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: red" csdnid="titleStyle"&gt;filter&lt;span style="color: red" csdnid="titleStyle"&gt;------------&amp;gt;&lt;/span&gt;&lt;span csdnid="titleStyle"&gt;&lt;span style="color: red"&gt;listener&lt;/span&gt;&lt;/span&gt;-------------&amp;gt;servlet----------------&amp;gt;welcome-file-list&lt;/span&gt;&lt;span csdnid="titleStyle"&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span csdnid="titleStyle"&gt;&amp;nbsp;context-param &lt;/p&gt;&#xD;
&lt;p&gt;description &lt;/p&gt;&#xD;
&lt;p&gt;display-name &lt;/p&gt;&#xD;
&lt;p&gt;distributable &lt;/p&gt;&#xD;
&lt;p&gt;ejb-ref &lt;/p&gt;&#xD;
&lt;p&gt;ejb-local-ref &lt;/p&gt;&#xD;
&lt;p&gt;env-entry &lt;/p&gt;&#xD;
&lt;p&gt;error-page &lt;/p&gt;&#xD;
&lt;p&gt;filter &lt;/p&gt;&#xD;
&lt;p&gt;filter-mapping &lt;/p&gt;&#xD;
&lt;p&gt;icon &lt;/p&gt;&#xD;
&lt;p&gt;listener &lt;/p&gt;&#xD;
&lt;p&gt;login-config &lt;/p&gt;&#xD;
&lt;p&gt;mime-mapping &lt;/p&gt;&#xD;
&lt;p&gt;resource-env-ref &lt;/p&gt;&#xD;
&lt;p&gt;resource-ref &lt;/p&gt;&#xD;
&lt;p&gt;security-constraint &lt;/p&gt;&#xD;
&lt;p&gt;security-role &lt;/p&gt;&#xD;
&lt;p&gt;Servlet &lt;/p&gt;&#xD;
&lt;p&gt;servlet-mapping &lt;/p&gt;&#xD;
&lt;p&gt;session-config &lt;/p&gt;&#xD;
&lt;p&gt;taglib &lt;/p&gt;&#xD;
&lt;p&gt;&lt;a title="web" href="http://soft.zdnet.com.cn/files/list-0-0-257465-1-1.htm"&gt;&lt;font color="#000000"&gt;web&lt;/font&gt;&lt;/a&gt;-app &lt;/p&gt;&#xD;
&lt;p&gt;welcome-file-list &lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;指定应用事件监听程序&lt;/p&gt;&#xD;
&lt;p&gt;应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向Web应用注册一个监听程序的web.xml的用法。&lt;br /&gt;注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内，listener-class元素列出监听程序的完整的限定类名，如下所示：&lt;/p&gt;&#xD;
&lt;p&gt;Xml代码 &lt;br /&gt;&amp;lt;listener&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;listener-class&amp;gt;package.ListenerClass&amp;lt;/listener-class&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;lt;/listener&amp;gt;&amp;nbsp;&amp;nbsp; &lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;虽然listener元素的结构很简单，但请不要忘记，必须正确地给出web-app元素内的子元素的次序。&lt;span style="color: red"&gt;listener元素位于所有的servlet 元素之前以及所有filter-mapping元素之后&lt;/span&gt;。此外，因为应用生存期监听程序是serlvet规范的2.3版本中的新内容，所以必须使用 web.xml DTD的2.3版本，而不是2.2版本。&lt;br /&gt;例如，程序清单5-20给出一个名为ContextReporter的简单的监听程序，只要Web应用的Servlet-Context建立（如装载Web应用）或消除（如服务器关闭）时，它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;程序清单5-20 ContextReporterjava&lt;/p&gt;&#xD;
&lt;p&gt;Java代码 &lt;br /&gt;package moreservlets;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;import javax.servlet.*;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;import java.util.*;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;public class ContextReporter implements ServletContextListener {&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void contextInitialized(ServletContextEvent event) {&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println("Context created on " +&amp;nbsp; new Date() + ".");&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void contextDestroyed(ServletContextEvent event) {&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println("Context destroyed on " +&amp;nbsp;&amp;nbsp; new Date() + ".");&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;}&amp;nbsp;&amp;nbsp; &lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;程序清单5-21 web.xml（声明一个监听程序的摘录）&lt;/p&gt;&#xD;
&lt;p&gt;Xml代码 &lt;br /&gt;&amp;lt;?xml version="1.0" encoding="ISO-8859-1"?&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;lt;!DOCTYPE web-app&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;"&lt;a href="http://java.sun.com/dtd/web-app_2_3.dtd"&gt;&lt;font color="#1d58d1"&gt;http://java.sun.com/dtd/web-app_2_3.dtd&lt;/font&gt;&lt;/a&gt;"&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;lt;web-app&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;lt;!-- ... --&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;filter-mapping&amp;gt; &amp;#8230; &amp;lt;/filter-mapping&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;listener&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;listener-class&amp;gt;package.ListenerClass&amp;lt;/listener-class&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/listener&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet&amp;gt; ... &amp;lt;/servlet&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;lt;!-- ... --&amp;gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/span&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2330857.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2012/01/29/2330857.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2011/07/01/2095282.html</id><title type="text">（转）JBPM案例详解六</title><summary type="text">一、该版本引入&amp;lt;fork&amp;gt;和&amp;lt;join&amp;gt;节点的使用！！----可以实现会签需求的功能。这里和同一个任务分配给多个参与者实现的最终功能效果是基本一样！！**作用：到达&amp;lt;fork&amp;gt;节点后，&amp;lt;fork&amp;gt;节点会进行分支，也就是让流程同时流向&amp;lt;fork&amp;gt;指定的一个或几个节点， 注意这里是同时流向，然后等待&amp;lt;fork&amp;gt;的分支节点审批通过后，那么流程会流向&amp;lt;join&amp;gt;节点， 等到&amp;lt;fork&amp;gt;节点所有分支均审批通过后，也就是所有分支流程都到达&amp;lt;join&amp;gt;节点后， 流程才会流向下一个节点。 **实现机制：</summary><published>2011-07-01T02:21:00Z</published><updated>2011-07-01T02:21:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095282.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095282.html"/><content type="html">&lt;p&gt;一、该版本引入&amp;lt;fork&amp;gt;和&amp;lt;join&amp;gt;节点的使用！！----可以实现会签需求的功能。这里和同一个任务分配给多个参与者实现的最终功能效果是基本一样！！&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;**作用：到达&amp;lt;fork&amp;gt;节点后，&amp;lt;fork&amp;gt;节点会进行分支，也就是让流程同时流向&amp;lt;fork&amp;gt;指定的一个或几个节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意这里是同时流向，然后等待&amp;lt;fork&amp;gt;的分支节点审批通过后，那么流程会流向&amp;lt;join&amp;gt;节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 等到&amp;lt;fork&amp;gt;节点所有分支均审批通过后，也就是所有分支流程都到达&amp;lt;join&amp;gt;节点后，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 流程才会流向下一个节点。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;**实现机制：当流程流转到&amp;lt;fork&amp;gt;节点时，token指针会停留在&amp;lt;fork&amp;gt;节点的前一个节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当流程到达&amp;lt;fork&amp;gt;节点指向的节点时，会分别产生上一个token的子节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 分别指向&amp;lt;fork&amp;gt;节点指向的节点，然后当这些分支节点都提交后会都转向&amp;lt;join&amp;gt;节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当从&amp;lt;join&amp;gt;节点流转到下一个节点时，此时指向&amp;lt;fork&amp;gt;节点的这些子节点会销毁，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是说子token的生命周期是从&amp;lt;fork&amp;gt;到&amp;lt;join&amp;gt;之间，当流向&amp;lt;join&amp;gt;指向的下一个节点后，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 此时的token是原token，也就是说原token在从进入&amp;lt;fork&amp;gt;到离开&amp;lt;join&amp;gt;之间没有移动，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 移动的是子token&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;**实现:不需要特殊的更改，和之前一样就行。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一、该版本引入"子流程"&amp;lt;process-state&amp;gt;和&amp;lt;sub-process&amp;gt;节点&lt;br /&gt;&amp;nbsp;(**使用这一套的时候应该特别注意，详细看一下这个项目的流程定义文件和下边的注释！！)&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;需求：比如当我们提交给人力资源部审批的时候，人力资源部又另有一个小流程，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 就是所有提交人力资源部的审批文件，都要先经过一个小助理审批，然后决定是否交由人力资源部经理审批&lt;br /&gt;&amp;nbsp;&amp;nbsp; 所以该人力资源部内部的这个流程就是一个子流程。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;**注意：当从process-state节点进入子流程的时候，他会忽略掉子流程中的start-state开始节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 从源码可以发现，当进入子流程之后，会直接调用subProcessInstance.signal()方法，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这样就导致流程直接流过了start-state开始节点，所以不要在子流程的start-state开始节点分配任务。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;实现时注意:对于我们的业务来说可以说成是子流程，但是在程序中子流程将作为一个新的流程出现，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 也就是子流程产生的流程对象和不流程的流程对象没有太大的关系，因为我们在父流程对象中绑定设定了很多参数变量值，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 也包括请假单对象的id，因为子流程在整个处理过程中和父流程处理的方式基本一样，但是子流程作为一个新的流程对象，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 该流程对象中并没有父流程对象中已经设置存在的那些变量参数值，如果想让子流程中也具有这些变量，那么必须做一个映射，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 只需做一个配置，也就是将父流程对象中指定的变量及其值复制到子流程对象中来，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 这样子流程就可以用这些设定在父流程对象中的变量及其值。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;具体实现：&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;process-state name="superStateName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;sub-process name="子流程定义文件的名称"&amp;gt;&amp;lt;/sub-process&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;variable name="父流程中变量的名字" access="read,write" mapped-name="映射到子流程中的变量名称"&amp;gt;&amp;lt;/variable&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="子流程完全结束后会流向这里指定的节点"&amp;gt;&amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/process-state&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; 说明：access="read,write"表示在子流程中可读也可写，所谓的可写就是在子流程对象中如果改变了变量的值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么父流程对象中的相应的变量的值也会跟着改变，如果没有指定为可读write，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么改变子流程对象中的变量值父流程对象中的值是不会改变的，当子流程结束后那么子流程对象中的变量就会销毁。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;**注意：使用的时候各个节点的状态可能会有点儿出路，用的时候再具体问题具体分析！！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;实际应用是应小心的地方：&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1.因为测试结果是进入子流程的第一个节点后，状态中不显示子流程的节点名称，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 而是显示&amp;lt;process-state name="superStateName"&amp;gt;中的name的值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2.这里注意一下，还有就是我们对请假单当遇到结束节点是让请假单的状态改为了&amp;#8220;完成审批&amp;#8221;，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 由于子流程中的结束节点也符合这个条件，所以当子流程结束后请假单也会显示审批结束，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 而其实子流程结束后会返回父流程中中接续执行，这里应该注意一下。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;二、对于&amp;lt;state&amp;gt;和&amp;lt;node&amp;gt;节点的说明：&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;state&amp;gt;节点就是一个节点，当流程流转到&amp;lt;state&amp;gt;节点的时候，流程被挂起，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 此时要我们手动调用signal方法，流程才会继续向下流转，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 该节点中不能加入&amp;lt;task&amp;gt;任务，即便是加入了也不会执行。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以该节点的用处就是可以在节点中加入&amp;lt;action&amp;gt;事件，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 事件中必须要调用signal方法，这样才能是流程继续向下一个节点流转，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果不调用，那么流程将会永远停留在该节点。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 作用：该节点的目的就是可以在&amp;lt;action&amp;gt;事件方法中除了调用signal方法外可以加入一些自己需求所用到的其他代码（例如发邮件等操作）。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;node&amp;gt;节点，流程流转到该节点的时候不会停留，而是直接进入该节点然后紧接着离开该节点，流向下一个节点。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;三、对于&amp;lt;mail-node&amp;gt;的说明：是用来发邮件的节点，就是说当流程流转到这里的时候，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; JBPM会根据指定的配置信息进行发邮件，发完邮件后流程继续向下进行，对于JBPM对这个节点的开发有点儿多余，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为发邮件的工作完全可以交给&amp;lt;action&amp;gt;来完成。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 使用方式：只能有一个&amp;lt;transition&amp;gt;元素&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;mail-node name="nodeName" to="#{可用变量来指定接收邮件的地址}" subject="邮件主题" text="邮件内容"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="flowToName"&amp;gt;&amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/mail-node&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 新版本提供的另一种方式：这种方式，首先知道&amp;lt;mail&amp;gt;不是节点，该元素可以放到&amp;lt;task&amp;gt;内部，作为&amp;lt;task&amp;gt;的一部分&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;mail actors="#{收件人地址的变量}"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;subject&amp;gt;邮件主题&amp;lt;/subject&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;text&amp;gt;邮件内容&amp;lt;/text&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/mail&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 另外在jbpm.cfg.xml配置文件中要加入对邮件服务的支持：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;string name="resource.mail.properties" value="jbpm.mail.properties"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其中properties的配置方式：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mail.host=smtp.126.com&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mail.smtp.auth=true&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58;&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#46;&amp;#102;&amp;#114;&amp;#111;&amp;#109;&amp;#46;&amp;#97;&amp;#100;&amp;#100;&amp;#114;&amp;#101;&amp;#115;&amp;#115;&amp;#61;&amp;#121;&amp;#111;&amp;#117;&amp;#114;&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#64;&amp;#49;&amp;#50;&amp;#54;&amp;#46;&amp;#99;&amp;#111;&amp;#109;"&gt;mail.from.address=yourmail@126.com&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58;&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#46;&amp;#115;&amp;#109;&amp;#116;&amp;#112;&amp;#46;&amp;#117;&amp;#115;&amp;#101;&amp;#114;&amp;#61;&amp;#121;&amp;#111;&amp;#117;&amp;#114;&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#64;&amp;#49;&amp;#50;&amp;#54;&amp;#46;&amp;#99;&amp;#111;&amp;#109;"&gt;mail.smtp.user=yourmail@126.com&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mail.smtp.password=yourpassword&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 需要注意的是，有些服务器需要发邮件人和待认证用户一致才可以发邮件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 注意：该发邮件的节点和元素只做了解，因为实际应用中不用这套机制，用&amp;lt;action&amp;gt;来代替，我们一样能实现一样的功能！！&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;四、对于&amp;lt;task&amp;gt;元素中的&amp;lt;controller&amp;gt;元素的说明，该元素可以为包含它的任务定义任务变量，&lt;br /&gt;&amp;nbsp; 也就是生命周期为任务的生命周期，&amp;lt;controller&amp;gt;里面可以通过&amp;lt;variable&amp;gt;来制定变量，&lt;br /&gt;&amp;nbsp; 这个指定变量的方法也是将流程变量映射到任务变量&lt;br /&gt;&amp;nbsp; 这和&amp;lt;process-state&amp;gt;节点中使用 &amp;lt;sub-process&amp;gt;时，为子流程映射变量差不多不过有点儿不同，&lt;br /&gt;&amp;nbsp; 不同就是，在&amp;lt;variable&amp;gt;中可以指定一个流程变量中没有的变量，&lt;br /&gt;&amp;nbsp; 这种情况下会同时也在流程变量中加入一个该变量指定的变量，至于access属性指定的值，和之前子流程中的意思相同。&lt;br /&gt;&amp;nbsp; "具体可以查看文档"这个用到的不是很多&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;五、JBPM的定时器timer的说明：(了解)&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; 1.在流程定义的节点中加入声明:这里以&amp;lt;state&amp;gt;节点为例&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;state name="nodeName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;timer name="timerName" duedate="0 seconds" repeat="10 seconds"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script&amp;gt;System.out.println("Here");&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/timer&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/state&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; **说明：该配置将在部署流程定义文件的时候存入JBPM提供的数据库表Timer&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 2.运行时当然要有一个线程来不是的监控JBPM的时间服务，来衡量什么时候启动定时器timer。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; JBPM的机制就是由Timer Runner来扫描Timer表，然后执行符合调度条件的Timer逻辑&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以还要配置一个servlet到web.xml配置文件:该servlet将会产生一个Timer Runner进行监控&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-name&amp;gt;JobExecutorServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-class&amp;gt;org.jbpm.job.executor.JobExecutorServlet&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/servlet&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-mapping&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ......&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/servlet-mapping&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;六、添加自定义的节点类型：即我们可以按照我们的实际需求定义符合我们自己的实际需求的节点类型,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为JBPM给我们提供的那些节点类型肯定不会适应所有的实际情况。(了解)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1.在org/jbpm/graph/node/node.types.xml中添加节点信息&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;node-type element="archive-node" &amp;gt;&amp;lt;/node-type&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2.编写自己的实现类(继承Node类)：主要覆盖read()方法和execute()方法！&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一、该版本将完成一个比较完整的系统&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; 1.修改上传界面，此次上传一个zip文件，包括流程定义文件、流程定义图片、流程定义文件坐标文件，后两个文件对动态查看流程的状态有效！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以处理上传的内容也有所变动，具体看deployProcessDefinition.jsp文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 上传zip的处理：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其实上传zip包后部署的时候，JBPM首先会在jbpm_moduledefinition表中保存一个FileDefinition对象&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (如果上传的不是zip格式的那么FileDefinition对象就为空)，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是说这个对象封装的是zip包中的三个文件，然后JBPM会从这个对象中将一个个的文件拆分出来，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 分别都保存到jbpm_bytearray表中，而三个文件中的数据内容并没有保存在jbpm_bytearray表中，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 而是保存到了jbpm_byteblock表中，这样就完成了对zip格式的文件部署！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (当然也会在jbpm_processdefinition表中保存流程定义文件的内容)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **注意：其实流程定义文件不是必须要存储到数据库的，默认是存到数据库，如果不想存到数据库，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么可以在jbpm.cfg.xml配置文件中加一个配置来指定将流程定义文件保存到哪个文件夹！！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;string name="jbpm.files.dir" value="d:/" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 通过value指定保存的目录名称！&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 2.我们之前的每个界面都要先通过JbpmConfiguration.getInstance().createJbpmContext()来获得JbpmContext对象，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 而且要在最后调用JbpmContext.close()方法来关闭JbpmContext对象&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 解决麻烦:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 为了方便JBPM为我们提供了一个Filter，对于指定的映射地址的请求，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 该Servlet会通过先通过JbpmConfiguration对象产生一个JbpmContext对象，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当请求结束后JBPM会自动关闭刚刚创建产生的JbpmContext对象，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这样就省去我们自己createJbpmContext()了，也省去了我们自己close()了。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 配置方法：加入到web.xml文件(可看源代码，很简单)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;filter&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;filter-name&amp;gt;JbpmContextFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;filter-class&amp;gt;org.jbpm.web.JbpmContextFilter&amp;lt;/filter-class&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/servlet&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;filter-mapping&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;filter-name&amp;gt;JbpmContextFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;url-pattern&amp;gt;*.jsp&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/filter-mapping&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 说明：这里是对所有的.jsp的请求做处理，这个映射只是为了适应我的当前项目。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是当请求所有的jsp的时候JBPM会先自动创建一个JbpmContext对象，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当jsp响应结束(可认为jsp页面关闭时)后JBPM会自动关闭JbpmContext对象&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 获得JBPM创建好的JbpmContext对象的方法：这样就得到了JBPM自己可以维护的JbpmContext对象！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JbpmContext jbpmContext = JbpmConfiguration.getInstance().getCurrentJbpmContext();&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 3.因为考虑到JbpmConfiguration这个对象比较耗费资源，所以在应用关闭的时候应该把JbpmConfiguration对象同时也要关闭&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 解决方法：为此JBPM为我们提供了一个Servlet类，该类内部非常简单(可看源码)，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 只有destroy()方法中加入了对JbpmConfiguration对象的close()方法。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以该servlet没有必要配置映射地址，即不须指定&amp;lt;servlet-mapping&amp;gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为只需要应用启动时同时启动该Servlet，当应用关闭是会调用Servlet的destroy()方法，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这样就同时关闭了JbpmConfiguration对象&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 实现方法：配置到web.xml文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-name&amp;gt;CloseJbpmConfigurationServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-class&amp;gt;org.jbpm.web.CloseJbpmConfigurationServlet&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/servlet&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 4.该项目中可能不止只有一个流程定义，所以要有一个界面来显示所有不同的流程定义类别，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是相当于我们办理某个流程的时候要先选择适合我们需要的流程。见listLatestProcessDefinitions.jsp文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 通过JbpmContext.getGraphSession().findLatestProcessDefinitions()方法得到所有的不同的流程定义文件的最新版本！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 5.导出流程定义的功能：当显示指定的流程定义的所有版本的时候，会有一个导出的操作。此操作会要一个Servlet的支持！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是当点击导出的时候去请求指定的Servlet&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Servlet的作用：根据流程定义的id值把流程定义取出来，然后取出每个文件(共三个)，然后打包，最后下载&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意：详情见ExportProcessDefinition.java和web.xml中的配置&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 记住要设置响应给浏览器的内容类型response.setContentType("application/x-zip-compressed");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 还要记住设置使用下载对话框的头信息response.setHeader("Content-Disposition", "attachment;filename=\"" + zipFileName +"\"");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 6.加一个查看流程图(状态)的功能：还是在listProcessDefinitionVersions.jsp中加一个链接，然后请求一个Servlet。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以要添加一个GetProcessImageServlet.java&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Servlet的作用：和ExportProcessDefinition的功能差不多，只不过这里是只拿出流程定义图片，然后输出到浏览器&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意：记得要设置响应给浏览器的内容类型response.setContentType("image/jpeg");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 7.增加删除流程定义功能，还是在listProcessDefinitionVersions.jsp中加一个链接，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 请求removeProcessDefinition.jsp页面&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意：&lt;br /&gt;&amp;nbsp;&amp;nbsp; 这里如果删除了指定的流程定义记录那么代表着JBPM也会同时删除已经通过该流程定义文件启动的所有的流程实例对象，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 所以该操作应该慎重操作，以免造成不必要的麻烦后果，按理说是如果存在以该流程定义文件启动的流程(流程对象)，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 那么该流程定义文件记录就不能删除！我们可以加入一些判断来实现。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 8.我们现在的查看流程定义是查看所有的最新的流程定义列表，我们也可以通过一个流程定义名称来查看指定的流程定义信息，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们可以自己写查询语句到JBPM提供的数据库中查询。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 例如"from ProcessDefinition where name = ?"等，项目中没有做，不过我们可以自己再实现一下！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 9.根据流程定义查询出所有的已经存在的对应的流程实例对象列表！！还是在listProcessDefinitionVersions.jsp中加一个链接，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 具体见listProcessInstances.jsp&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; **10.流程跟踪的实现：一定要仔细阅读ProcessImageTag、jbpm.tld和ProcessImageServlet这三个文件中的内容！！&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JBPM提供的支持源文件：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可到jbpm-starters-kit-3.1.2.zip包中的文件夹中拷贝源文件到项目中，用来代替我们自己的！我们可以自己修改！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 从jbpm\src\java.webapp\org\jbpm\webapp\tag中拷贝ProcessImageTag.java文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 从jbpm\src\resources\jbpm.war\WEB-INF中拷贝对ProcessImageTag标签类的支持配置文件jbpm.tld文件到WEB-INF目录&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 从jbpm-starters-kit-3.1.2\jbpm\src\java.webapp\org\jbpm\webapp\servlet中拷贝ProcessImageServlet.java文件&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 功能解释：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 就是对于没有结束的流程实例，通过流程跟踪可以查看当前流程实例的流程流转到了那个节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 通过流程定义图片和流程定义坐标文件实现，我们可以在图上清晰的查看到流程实例的流程状态，即实现了流程跟踪！！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 具体实现：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;1&amp;gt;首先将拷贝的Servlet配置到项目的web.xml配置文件中，以便Servlet起作用&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-name&amp;gt;ProcessImageServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-class&amp;gt;自己的包名.ProcessImageServlet&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/servlet&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-mapping&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;servlet-name&amp;gt;ProcessImageServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--该映射地址不能变，因为这是JBPM实现时的配置，当然如果知道怎么回事可以自己修改&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 不过应该还要修改ProcessImageTag和jbpm.tld文件--&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;url-pattern&amp;gt;/processimage&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/servlet-mapping&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;2&amp;gt;页面中首先用&amp;lt;taglib&amp;gt;导入标签uri------taskInstanceId是当前任务就是要用红框框起来的任务所在节点！！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 然后使用&amp;lt;jbpm:processImage task="${taskBean.taskInstanceId}"/&amp;gt;就会把流程图显示在当前位置&lt;/p&gt;&#xD;
&lt;p&gt;实现是会遇到的问题：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;1&amp;gt;因为JBPM自己实现时使用了JbpmContextFilter来创建和关闭JbpmContext对象，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以ProcessImageTag和ProcessImageServlet中得到JbpmContext对象的方式&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 都是调用的JbpmConfiguration.getInstance().getCurrentJbpmContext()方法。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 问题：如果我们使用了Spring和JBPM集成，那么这两个文件获得JbpmContext对象的方式都要进行修改！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 解决方案：将JbpmContext的获取方式做一下修改就可以！改成从Spring中获得！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;2&amp;gt;乱码问题:因为流程定义文件不能很好的支持中文，尤其是gpd.xml文件，这个是流程定义坐标文件，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果流程定义文件中使用了中文，那么跟踪定位时可能会出错也可能会出乱码！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 例如：如果你的流程定义中用中文字符，数据库字符集为utf-8，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 并且数据库中也确实为正确的utf-8内容，也可能会乱码错误。出乱码的文件更可能是gpd.xml文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 分析：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; A.可能会出现空指针异常，是因为上下文的Element root中的字符为乱码。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 出错的行为：result[0] = Integer.valueOf(node.attribute("x").getValue());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 解决方案：将值用utf-8转码！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 源码：Element rootDiagramElement = DocumentHelper.parseText(new String(gpdBytes)).getRootElement(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 修改：Element rootDiagramElement = DocumentHelper.parseText(new String(gpdBytes, "utf-8")).getRootElement(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; B.要注意ProcessImageTag.java文件中有一个请求ProcessImageServlet的操作并且传过去一个definitionId参数值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们要注意的就是该请求是"相对路径"还是"绝对路径"，这要和我们的需求的具体情况核对后然后才能修改！！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意：ProcessImageTag是一个java文件，那么从他中发送请求的相对路径又是相对与谁说的呢？&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这里我们要特别注意，因为这是一个标签类，所以肯定是在一个jsp页面中使用该标签类对应的标签，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当标签运行的时候，该类中的代码才会运行，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以相当于ProcessImageTag类中的代码是在那个使用标签的jsp页面中运行一样，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 并且运行造成的结果也将会对那个使用标签的jsp页面有效，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以所谓的相对路径就是那个使用对应该标签类的标签的jsp页面的相对路径，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 即相对路径就是那个使用标签的jsp页面所在的路径！！（注意理解）&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 11.可以加入显示任务实例对象的列表(类似显示流程实例对象列表)，也就是再加一个页面，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 来根据指定的&amp;#8220;流程名称&amp;#8221;、&amp;#8220;任务名字&amp;#8221;或者&amp;#8220;任务的参与者即actor-id&amp;#8221;等值进行查询，当然还是到jbpm_taskinstance表中查询&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (具体我没有实现，因为不太复杂，这里提供个思路，以后用到的话应该很容易能够实现)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 12.我们可以通过对任务记录的create和start和end字段的值进行有价值的利用，就是我们可以对登陆人员添加"代办任务列表"和"代签收任务列表"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所谓代办任务列表就是end字段的值为空的任务记录，代签收任务列表的意思就是create字段的值已经生成，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 而start字段中的值却没有，当点击签收的时候此时向start字段加入值也就是调用taskInstance的start()方法。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 具体获得数据列表的方法:我们可以通过Hibernate的hql语句自己定制需求，到JBPM提供我们的数据库表中查得能满足我们需求的记录列表&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (该项目中目前没有实现该功能，如果实现并不难)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; 13.如果想看更具体的内部实现可以常识看源代码：&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我看过的类有：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JbpmConfiguration&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JbpmContext&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProcessDefinition&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProcessInstance&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Services&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Service&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TaskInstance&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GraphSession&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TaskMgmtSession&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Node&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Token&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ExecutionContext&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 等等&amp;#8230;&amp;#8230;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 经过这十一个小项目，大家应该基本能够理解JBPM的内核实现及原理！如有发现项目中的错误或不恰当的地方欢迎留言批评。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2095282.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095282.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2011/07/01/2095280.html</id><title type="text">（转）JBPM案例详解五</title><summary type="text">一、该版本中使用了Pooled Actor机制，而不是actor-id。**解释：就是说通过Pooled Actor可以将一个任务一次分配给一组(多个)参与者， 注意这和之前不一样，之前是针对同一个任务，然后产生多个任务实例对象， 然后每个实例对象对应一个任务的参与者，其实还是相当于一个任务对应了一个参与者； 现在说的使用Pooled Actor是将一个任务的一个任务实例对象分配给一组参与者。 **运行原理： 当把一个任务的一个任务实例对象分配给一组参与者之后， 那么该组参与者的每个成员登陆系统后，都可以看到该任务列表 (此时调用显示用户代办列表的方法较之前有区别，是带有pooled字符串的方</summary><published>2011-07-01T02:19:00Z</published><updated>2011-07-01T02:19:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095280.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095280.html"/><content type="html">&lt;p&gt;一、该版本中使用了Pooled Actor机制，而不是actor-id。&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;**解释：就是说通过Pooled Actor可以将一个任务一次分配给一组(多个)参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意这和之前不一样，之前是针对同一个任务，然后产生多个任务实例对象，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 然后每个实例对象对应一个任务的参与者，其实还是相当于一个任务对应了一个参与者；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 现在说的使用Pooled Actor是将一个任务的一个任务实例对象分配给一组参与者。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;**运行原理：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当把一个任务的一个任务实例对象分配给一组参与者之后，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么该组参与者的每个成员登陆系统后，都可以看到该任务列表&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (此时调用显示用户代办列表的方法较之前有区别，是带有pooled字符串的方法)，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是当其中的一个参与者开启了任务(调用了start()方法，不论之后是否也调用了end()处理任务的方法)，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么此时该组的其他参与者就不能再看到该任务了，因为任务已经得到了处理；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 换句话说就是任务只有一个，而处理任务的参与者却有多个，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是最终任务的处理只能被一个参与者处理，任务处理完之后就可以进入下一个节点了。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;****具体解释：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其实当JBPM流转到包含有分配一组参与者的任务&amp;lt;task&amp;gt;的节点&amp;lt;task-node&amp;gt;的时候，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 和之前一样，仍然会自动创建任务实例对象，此处因为使用的是PooledActor为任务分配的参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以JBPM对任务实例对象做了特殊处理，我们会在数据库表中看到，虽然创建了任务实例，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是对象的表记录中的actorId是null，根本就没有值，这里的原因就是因为是一个任务分给了一组，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是最后必须有一个参与者处理，该actorId字段的值，是在该任务实例对象调用start()开始方法的时候，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 通过代码指定一个要处理该任务的参与者编号(当然这个参与者必须属于组之中的一个)，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这样该任务就只对调用start()方法时指定了参与者的这个参与者可见，不管该参与者是不是进行处理该任务，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 即是否调用end()方法，只要它在启动任务，即调用start()方法时指定了自己作为任务的处理者，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么对于组中的其他参与者就对该任务都不可见了。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;**具体实现：三个大步骤！！！&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; 1.在流程定义文件中为任务指定一个类&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task-node name="taskNodeName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name="taskName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;assignment /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="flowToName"&amp;gt;&amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task-node&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 2.分配参与者的类的具体实现：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class PooledAssignmentHandler implements AssignmentHandler{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //一组参与者的编号ID&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String[] actors = {"1","2","3"};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //为任务分配一组参与者&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; assignable.setPooledActors(actors);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 3.获取登陆人员代办任务列表的时候和之前有点区别：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //根据当前登陆的用户ID获得任务列表&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //估计这样只能获取通过setPooledActors()方法分配的任务，之前那种估计还得用之前的方式，自己试验后确实是这样&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //所以下边获得的都是通过setPooledActors()这种方法分配的参与者的任务列表&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; List list = jbpmContext.getTaskMgmtSession().findPooledTaskInstances(userId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //循环各任务&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; for(int i = 0; i &amp;lt; list.size(); i++){&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //得到一个任务&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TaskInstance taskInstance = (TaskInstance)list.get(i);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /*启动任务，因为上面说过创建任务的时候actor-id字段是没有参与者的值的，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以启动流程的时候要把启动流程的参与者(即自己)设置为处理该任务的参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是说这样的话actor-id字段中的值就是userId了，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么此时不论userId参与者是否调用end()方法来处理该任务，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么改组的其他参与者都将不能再看到该任务了*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; taskInstance.start(userId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **提示：获得任务列表后对于这种使用PooledActor的方式的处理，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一般不立即就start()任务，因为如果立即启动的话那么意味着谁先登陆谁先看到这个任务那么这个任务就由谁办理；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以一般都是先显示，当点击"办理"等一个动作后，然后再调用start()方法，并且传入当前参与者的ID，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 表示当前用户要对任务进行处理。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一、该版本引入&amp;lt;decision&amp;gt;节点的使用：又称决策节点或路由节点&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; 作用：就是流程到达该节点之后，可以通过自己的需求，可以通过一定的运算法则动态的选择接下来要流转到那个节点&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; 使用方法：有两种使用方式&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 1.可以在&amp;lt;decision&amp;gt;节点的&amp;lt;transition&amp;gt;元素中通过&amp;lt;condition&amp;gt;元素指定一定的条件，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果条件满足，那么就流向包含该&amp;lt;condition&amp;gt;元素的&amp;lt;transition&amp;gt;元素指向的节点&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果条件不满足，则不流向包含该&amp;lt;condition&amp;gt;元素的&amp;lt;transition&amp;gt;元素指向的节点&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这里可能会有在一个节点中出现多个&amp;lt;transition&amp;gt;的情况，此时选择第一个满足条件的&amp;lt;transition&amp;gt;转向作为流程的转向&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;decision name="nodeName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="flowToName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--注意小于号&amp;lt;不能直接写，因为这是在xml文件中，要遵从xml的约束--&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;condition&amp;gt;#{days lt 5}&amp;lt;/condition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="flowToName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--注意大于号&amp;gt;不能直接写，因为这是在xml文件中，要遵从xml的约束--&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;condition&amp;gt;#{days gt 5}&amp;lt;/condition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/decision&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; **说明：#{days lt 5}中的days是变量名，所以当流程流转到该处之前要为该变量赋值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 赋值方式和之前在&amp;lt;assignment&amp;gt;中为actor-id指定的变量赋值一样。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; **注意：如果要用上边这种方式，有的时候会因为&amp;lt;condition&amp;gt;中的表达式不正确而出现问题，所以不推荐，推荐使用下边这种方法！！！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 2.可以通过&amp;lt;decision&amp;gt;节点的&amp;lt;handler&amp;gt;来指定一个实现DecisionHandler接口的类，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 实现类的指定方法会返回一个字符串，这个字符串就是该&amp;lt;decision&amp;gt;节点中的&amp;lt;transition&amp;gt;元素的name属性的值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 即&amp;lt;transition&amp;gt;的名称，也就是返回的字符串与哪个&amp;lt;transition&amp;gt;名称对应那么就转向那个&amp;lt;transition&amp;gt;指定的节点&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;decision name="nodeName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--指定处理分流的类全名--&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;handler &amp;gt;&amp;lt;/handler&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="flowToName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="flowToName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/decision&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //实现类&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class Decision implements DecisionHandler{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String decide(ExecutionContext executionContext) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int days = executionContext.getContextInstance().getVariable("days").intValue();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(days &amp;gt; 1000){&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return "transitionName";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }else{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return "transitionName";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意：上边的处理类中获取了一个流程变量，所以流程流转到&amp;lt;decision&amp;gt;节点之前还是要将流程变量先进行赋值！！&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2095280.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095280.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2011/07/01/2095278.html</id><title type="text">（转）JBPM案例详解(四)</title><summary type="text">一、该版本中应用了在start-state节点中加入task元素，之前的项目中则没有用到， 所以之前的项目在创建流程实例是都会调用processInstance.signal()方法来立刻从start-state节点流转到下一个节点， 然后才开始进入正常，本来我们的效果应该是，当申请人提交请假表单之后，就等同于提交了， 不应该在申请的代办列表中再进行一次提交，该项目解决了该问题。 **注意：在start-state节点中使用task节点的化和在&amp;lt;task-node&amp;gt;节点中的使用方法有点区别。 1.start-state节点中的task不能指定assignment的actor-id，</summary><published>2011-07-01T02:16:00Z</published><updated>2011-07-01T02:16:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095278.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095278.html"/><content type="html">&lt;p&gt;一、该版本中应用了在start-state节点中加入task元素，之前的项目中则没有用到，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以之前的项目在创建流程实例是都会调用processInstance.signal()方法来立刻从start-state节点流转到下一个节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 然后才开始进入正常，本来我们的效果应该是，当申请人提交请假表单之后，就等同于提交了，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 不应该在申请的代办列表中再进行一次提交，该项目解决了该问题。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; **注意：在start-state节点中使用task节点的化和在&amp;lt;task-node&amp;gt;节点中的使用方法有点区别。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1.start-state节点中的task不能指定assignment的actor-id，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为TaskMgmtInstance中的createStartTaskInstance()方法中会设置actor-id的值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以即便是在task节点中设置了actor-id的值也会被覆盖&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2.当创建流程实例的时候，token指向start-state节点，但是不会直接进入task任务节点，这和在task-node中的task不同。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以我们要手动通过代码进入task节点，即启动任务，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 此时要调用TaskMgmtInstance.createStartTaskInstance()方法来手动启动task任务。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3.启动任务之前我们要注意，因为actor-id 还没有哦赋值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 虽然TaskMgmtInstance.createStartTaskInstance()方法内部给actor-id赋值了，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是它的赋值方式是taskInstance.setActorId(SecurityHelper.getAuthenticatedActorId());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 很明显SecurityHelper.getAuthenticatedActorId()是用来获取actor-id的值，所以我们要向这里存放actor-id的值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是说在调用TaskMgmtInstance.createStartTaskInstance()方法之前，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 要调用JbpmContext.setActorId()方法来设置actor-id的值，以便顺利将start-state节点中的task任务分配给参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为是开始节点，所以该任务的参与者肯定是登陆者，也就是填写请假单的人。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **这样就可以达到预计需求的效果了！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **注意：上面提过了start-state中的task不能指定actor-id值，但是它可以指定swimlane，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可以借助swimlane来为任务分配参与者(实现见下一个项目)&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;**注意：上面提过了start-state中的task不能指定actor-id值，但是它可以指定swimlane，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可以借助swimlane来为任务分配参与者(本项目就是这样实现)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **细节提示：这里使用swimlane的原理和不是用actor-id的原理差不多，因为虽然我们可以指定swimlane，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是并没有为swimlane指定assignment进而制定actor-id，因为使用swimlane的时候，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JBPM内部也会通过创建任务的时候自动的为actor-id赋值，仍然和上一个项目中一样，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也是先获得actor-id的值，然后付给start-state的task任务节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这样的目的就是能很好的和登陆人员，即申请人进行绑定。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一、该版本实现了会签功能！！&lt;/p&gt;&#xD;
&lt;p&gt;二、在一个节点&amp;lt;task-node&amp;gt;中加入多个任务&amp;lt;task&amp;gt;，每个任务指定一个参与者，这样当流程流转到该节点后，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 只有所有的任务都审批通过后才能流向下一个节点&amp;lt;task-node&amp;gt;,这样也可以实现会签的功能。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 形式：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task-node name="nodeName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name="taskName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;assignment actor-id="1"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name="taskName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;assignment actor-id="2"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name="taskName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;assignment actor-id="3"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ..........&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="flowToNode"&amp;gt;&amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task-node&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 解释：就是当节点流转到该节点后，首先由1参与者审批，然后由2参与者审批，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 然后由3参与着审批，知道所有任务结束，那么才会向下一个节点&amp;lt;task-node&amp;gt;流转&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 限制：这样有很大的限制，这样的应用的前提是事先要知道到底有多少个参与者要参与该节点的任务，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 只有知道参与者都包括谁后才能应用这种方法，但是实际的应用中，我们知道很多公司的人员都不是一沉不变的，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一般都不能确定参与者的个数，比如可能经常参与的这个人请假或怎么了，如果用这种方案就要该程序了。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (这里同样也有下边那种方案的缺点)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 改善方法：一般都是动态获得参与者的个数的，所以可以用下边的把一个任务分配给多个参与者。&lt;/p&gt;&#xD;
&lt;p&gt;三、这种方案使用了把把一个任务分配给多个参与者，这样可以实现一种类似会签的效果，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是节点流转到了下一个节点，流转到的这个节点(task-node)中有一个任务，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是这个任务有点儿特别，我们不让JBPM给我们自动创建这个任务的实例，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是说回想我们在start-state节点中的task任务中的处理，这个任务的处理是我们手动创建的任务实例，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 进而对其启动、结束。这里我们仍然采用这让的方案，然后创建的时候我们动态为该任务分配多个不同的参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 此时任务表中会同时产生分配的参与者个数个记录，但是他们的end字段都是空，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 所以这样的意思就是等待这些参与者分别进行审批后才能流向该任务所在节点的下一个节点。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这样就实现了会签的功能即等待所有参与者审批都结束后才会流向下一个节点。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 缺点：这样将一个任务分配给多个不同的参与者，虽然间接实现了会签的功能，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是有一个不好的地方就是这些参与者是有顺序的，比如：分配的时候是共有3个参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 分配时是0，1，2这三个ID值都作为该任务的actor-id，那么就是说要等到这三个人都审批后才能想下一个节点流转，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是这三个参与者的审批过程必须是0先审批，然后1，最后2审批，因为分配给这三个参与者后在数据库中保存的记录是从上到下的，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 而顺据就是分配的0，1，2这样的顺序，之后先让0审批后，那条对应的记录的end字段才会有值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 只有此时1参与者才会在代办列表中看到有审批的任务。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; **注意：经过亲身试验，该缺点不存在，没有顺序的限制，也就是所有的参与者不论谁的编号排在前排在后，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这些参与者登陆后都可以看到任务列表，并且都可以审核，而且不论谁先审核谁后审核，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 都将是所有的审核结束后才会流转到下一个节点。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这正是会签的需求所在。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 实现：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 放弃让JBPM自动创建任务实例对象&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 就像使用创建start-state节点中的任务实例一样；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 而不是像之前那样流转到节点后让JBPM自动创建任务，如果这样我们就只负责任务的结束了，如果这样不能实现给任务分配多个参与者&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task-node name="nodeName" create-tasks="false"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;event type="node-enter"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action  /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/event&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name="taskName"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition name="transitionName" to="flowToName"&amp;gt;&amp;lt;/transition&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task-node&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; **说明：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * create-tasks="false"表示当流程进入该节点后JBPM不会自动给我们创建任务实例对象(TaskInstance对象)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * &amp;lt;event&amp;gt;是一个事件，type="node-enter"表示流程进入节点后触发该事件，会调用&amp;lt;action&amp;gt;指定的类中的方法&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * 我们让&amp;lt;action&amp;gt;指定的类必须实现ActionHandler类，在该类中我们要为&amp;lt;task&amp;gt;分配一个或多个的任务参与者&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * 就是因为&amp;lt;action&amp;gt;中会分配任务的参与者，所以&amp;lt;task&amp;gt;中不用显示的指定分配参与者的标签元素&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; **细节：&amp;lt;action&amp;gt;指定的类的实现：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class UserActionHandler implements ActionHandler{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void execute(ExecutionContext executionContext) throws Exception{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //得到任务管理对象&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //得到当前的节点对象&amp;lt;task-node&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TaskNode taskNode = (TaskNode)executionContext.getNode();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //得到我们要把任务分配个的参与者，可以是多个，如果是多个可以用逗号分割&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String userIds = (String)executionContext.getContextInstance().getVariable("userId");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //取出每个参与者&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(userIds != null &amp;amp;&amp;amp; !"".equals(userIds)){&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String[] ids = userIds.split(",");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //循环，这样的效果将根据一个任务声明，创建多个任务实例对象，但每个任务对象的参与者不同&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for(int i = 0; i &amp;lt; ids.length; i++){&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //创建任务对象，因为JBPM已经不给我们自动创建了&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TaskInstance ti = tmi.createTaskInstance(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //要创建的任务的名称&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; taskNode.getTask("taskName"),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //当前的token指向&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; executionContext.getToken()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //为任务指派参与者&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ti.setActorId(ids[i]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //如果没有指定任何参与者，代表也没有创建任何任务对象那么此时JBPM会认为&amp;lt;task-node&amp;gt;中没有任务，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //那么将会直接流转到下一个节点！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //所以这样的if语句加上之后最后再加一个else的处理情况，这样比较完整&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意：在ActionHandler的实现类中只能调用executionContext.leaveNode()方法&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 离开当前&amp;lt;action&amp;gt;所在节点，而不能调用signal()方法离开节点&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; **一、&amp;lt;event&amp;gt;事件中通过&amp;lt;script&amp;gt;脚本来处理一些任务的准备等业务逻辑！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script&amp;gt;中是用的是BeanShell脚本，靠bsh.jar包的支持，可以看看BeanShell的文档，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可以到JBPM的文档中看一下JBPM为BeanShell脚本提供了什么扩展&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们可以在脚本中使用流程变量，这样我们再项目完成后，如果需要添加新流程，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么新流程中就可以通过脚本来改变一些程序内部的是，这样就不需要添加流程后需要修改源代码，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们把代码加入到这里边来用脚本实现就行了。(可参看userguide文档)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 四、有时我们会碰到这样的需求：就是像上面一样把同一个任务分配给多个参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是等到审批的时候呢，我不想用会签的机制，而是所有的参与者，只要有一个审批提交了，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么整个任务就等于结束了，就立刻让流程流向下一个节点&amp;lt;task-node&amp;gt;，而不是像上面那样，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 必须等待所有的参与者都审批提交后才算任务结束，才能让流程向下一个节点流转。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 解决方法：JBPM为我们提供了一个机制手段，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 就是可以通过设置节点(&amp;lt;task-node&amp;gt;)元素signal属性，来完成这种需求。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 例：&amp;lt;task-node name="taskNodeName" signal="{unsynchronized|never|first|first-wait|last|last-wait}"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ....&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task-node&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; **说明:signal的默认值是last。signal指定了任务的完成对流程执行继续的影响。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; **为了帮助理解task-node节点的signal属性，这里举例如下：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task-node name='a'&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name='laundry' /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name='dishes' /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name='change nappy' /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transition to='b' /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/task-node&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; a)这里没有定义signal属性的值，这就表明当节点中的三个任务都完成后，流程才进入后面的节点&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; b)当&amp;lt;task-node name='a' signal='unsynchronized'&amp;gt;表明token不会在本节点(不是任务，注意区别)停留，而是直接到后面的节点&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c)当&amp;lt;task-node name='a' signal='never'&amp;gt;表明三个任务都完成后，token仍然不会指向后面的节点；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 需要自己手动调用processInstance.signal()才会驱动流程到下面的节点&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; d)当&amp;lt;task-node name='a' signal='first'&amp;gt;表明只要有一个任务完成后，token就指向后面的节点(这就是上面的需求)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **注意：这个方案确实能实现需求，但是会有个不好的地方，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 虽然参与者中有一个审批通过后就会使流程向下一个节点流转，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是其他没审核的参与者那里仍然可以看到任务列表，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为数据库表中这些没有审核提交的参与者的任务实例对象记录的end字段还没有值，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 只不过此时他们是否再提交审批已经没有意义了，因为流程已经流向了下一个节点，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果他们继续审批提交，造成的影响就是在数据库表中的end字段中填入了数据，对流程将不会起到什么作用。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; e)当&amp;lt;task-node name='a' signal='first-wait'&amp;gt;表明当第一个任务实例完成时继续执行；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当在a节点入口处没有任务创建时，token在a任务节点处等待，直到任务被创建或完成。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; f)当&amp;lt;task-node name='a' signal='last'&amp;gt;时，这是默认值，和不设置signal属性的情况相同。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; g)当&amp;lt;task-node name='a' signal='last-wait'&amp;gt;时，当最后一个任务实例完成时候继续执行下去。 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当a这个任务节点没有任务被建立时,任务节点等待直到任务被建立。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2095278.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095278.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2011/07/01/2095271.html</id><title type="text">（转）JBPM案例详解(三)</title><summary type="text">一、因为加入了不同的分支流向，所以当审批不通过要驳回时JBPM应该能够得到当初提交任务的参与者， 而这个工作通过直接用actor-id=#{}来实现并不合适，因为如果有很多这样的指定的话， 可能会乱套，JBPM提供了一个专门能解决该问题的机制。 解决方法：使用泳道，它是定义流程中的多个任务由相同参与者完成的一种机制，这使的泳道正好能够完成任务。 在第一个任务实例为指定泳道创建后，参与者将被流程记住，以被在同一泳道中的后续任务所使用。 泳道有一个分配，因此所有引用泳道的任务不需要再指定分配。 **注意：可以在&amp;lt;start-state&amp;gt;开始节点添加一个任务&amp;lt;task&amp;gt;，用s</summary><published>2011-07-01T02:13:00Z</published><updated>2011-07-01T02:13:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095271.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095271.html"/><content type="html">&lt;p&gt;一、因为加入了不同的分支流向，所以当审批不通过要驳回时JBPM应该能够得到当初提交任务的参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 而这个工作通过直接用actor-id=#{}来实现并不合适，因为如果有很多这样的指定的话，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 可能会乱套，JBPM提供了一个专门能解决该问题的机制。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 解决方法：使用泳道，它是定义流程中的多个任务由相同参与者完成的一种机制，这使的泳道正好能够完成任务。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在第一个任务实例为指定泳道创建后，参与者将被流程记住，以被在同一泳道中的后续任务所使用。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 泳道有一个分配，因此所有引用泳道的任务不需要再指定分配。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **注意：可以在&amp;lt;start-state&amp;gt;开始节点添加一个任务&amp;lt;task&amp;gt;，用swimlane机制来记录流程的启动者&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **实现：这里实现了泳道机制&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 用法:一般放到流程定义文件顶部的位置定义&amp;lt;swimlane&amp;gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;swimlane name="swimlane名字供task应用"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;assignment /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/swimlane&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 或&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;swimlane name="swimlane名字供task应用"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--当然这里也可以写死，即actor-id="username"--&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;assignment actor-id="#{变量}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/swimlane&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //应用后就不用再为当前的task分配参与者了，分配任务交给swimlane完成&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;task name="任务名字" swimlane="上面定义的swimlane名字"&amp;gt;&amp;lt;/task&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **项目中为了使用swimlane而做的更改有两处：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1.processdefinition.xml文件中加入了swimlane元素，用它来完成分配任务的参与者&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2.在startProcessInstance.jsp文件中，为参与者变量赋值的那句代码发生了改变，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 改为为swimlane中指定的任务参与者赋值：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; processInstance.getContextInstance().setVariable("starter",userId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;二、项目复杂度增加，流程定义文件加入两个节点，一个是"通知申请人审批"，另一个是"人力资源部审批"，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其中"通知申请人审批"仍然由表单提交者，即申请人来完成任务。为此我们正好利用了泳道swimlane的好处。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- 这样写的意思是当总经理审批通过之后，申请人还要到人力资源那里咨询一下，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当然咨询的任务由申请人去做,这就是使用swimlane的好处，当流程中有多个任务是同一个参与者参与的时候，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 使用swimlane只需指定swimlane分配的任务参与者的引用即可 --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于"人力资源部审批"，我们又加入了一个人力资源经理，也就是登陆界面加入了一个下拉框选项，这样增加一个参与者。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;三、对流程定义中的分配任务参与者机制再次修改：将部门经理审批中通过actor-id写死的值，改成通过class类来动态指定actor-id的值，&lt;/p&gt;&#xD;
&lt;p&gt;即指定一个类名，然后该类要实现AssignmentHandler接口，实现类中的方法会在执行到对应的任务之前被调用，以便顺利分配任务&lt;/p&gt;&#xD;
&lt;p&gt;的参与者&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2095271.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095271.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2011/07/01/2095268.html</id><title type="text">（转）JBPM案例详解(二)</title><summary type="text">1.加入文件上传功能，即上传流程部署文件 - 导入commons-fileupload.jar - 导入commons-io.jar2.加入请假单实体对象及映射文件，将流程实例与请假单对象绑定3.加入动态为任务分配(通过设定流程变量)4.加入登陆界面5.流程定义中加入不同的流向，即可以通过审批或者驳回审批重新填写(多流向)，具体见startTask.jsp文件6.因为加入了不同的分支流向，所以当审批不通过要驳回时JBPM应该能够得到当初提交任务的参与者， 而这个工作通过直接用actor-id=#{}来实现并不合适，因为如果有很多这样的指定的话， 可能会乱套，JBPM提供了一个专门能解决该问题的</summary><published>2011-07-01T02:11:00Z</published><updated>2011-07-01T02:11:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095268.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095268.html"/><content type="html">&lt;p&gt;1.加入文件上传功能，即上传流程部署文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 导入commons-fileupload.jar&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; - 导入commons-io.jar&lt;br /&gt;&amp;nbsp;2.加入请假单实体对象及映射文件，将流程实例与请假单对象绑定&lt;br /&gt;&amp;nbsp;3.加入动态为任务分配(通过设定流程变量)&lt;br /&gt;&amp;nbsp;4.加入登陆界面&lt;br /&gt;&amp;nbsp;5.流程定义中加入不同的流向，即可以通过审批或者驳回审批重新填写(多流向)，具体见startTask.jsp文件&lt;br /&gt;&amp;nbsp;6.因为加入了不同的分支流向，所以当审批不通过要驳回时JBPM应该能够得到当初提交任务的参与者，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 而这个工作通过直接用actor-id=#{}来实现并不合适，因为如果有很多这样的指定的话，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 可能会乱套，JBPM提供了一个专门能解决该问题的机制----泳道swimlane。&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; 解决方法：使用泳道，它是定义流程中的多个任务由相同参与者完成的一种机制，这使的泳道正好能够完成上面要解决的任务。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 形象的说，在为第一个任务实例指定泳道后，参与者将被流程记住，以备在同一泳道中的后续任务所使用。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 泳道有一个分配，因此所有引用泳道的任务不需要再指定分配。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **注意：可以在&amp;lt;start-state&amp;gt;开始节点添加一个任务&amp;lt;task&amp;gt;，用swimlane机制来记录流程的启动者&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **实现：见下一个项目有泳道的实现&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; **该项目缺点：缺点就是解决分支流程的时候不是使用的swimlane，但是没有出现错误，比较成功，swimlane的使用见下一个项目。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2095268.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095268.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/dynasty/archive/2011/07/01/2095259.html</id><title type="text">（转）JBPM案例详解(一)</title><summary type="text">入门篇 本文章针对有一定JBPM基础的朋友，文章内容将不会讲解太多内容，具体内容可以下载项目，部署后源码中会有清晰的说明，希望对有需要的朋友有所帮助，有关于项目中的错误和不妥当的地方还望大家留言指正！1.到官网下载JBPM开发包，解压后拷贝相关的依赖包到lib目录(虽然JBPM是靠Hibernate支持持久层，但是不需要拷贝Hibernate的其他依赖包，因为JBPM只是借助了Hibernate的功能，然后进一步进行了自己的封装)*导入解压目录中lib下的jar包就可以了2.拷贝配置文件*修改hibernate.cfg.xml文件(拷贝config目录中的hibernate.cfg.xml文件</summary><published>2011-07-01T02:06:00Z</published><updated>2011-07-01T02:06:00Z</updated><author><name>唐朝</name><uri>http://www.cnblogs.com/dynasty/</uri></author><link rel="alternate" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095259.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095259.html"/><content type="html">&lt;h2 style="text-align: center"&gt;入门篇&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;本文章针对有一定JBPM基础的朋友，文章内容将不会讲解太多内容，具体内容可以下载项目，部署后源码中会有清晰的说明，希望对有需要的朋友有所帮助，有关于项目中的错误和不妥当的地方还望大家留言指正！&lt;/p&gt;&#xD;
&lt;p&gt;1.到官网下载JBPM开发包，解压后拷贝相关的依赖包到lib目录(虽然JBPM是靠Hibernate支持持久层，但是不需要拷贝Hibernate的其他依赖包，因为JBPM只是借助了Hibernate的功能，然后进一步进行了自己的封装)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;*导入解压目录中lib下的jar包就可以了&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;2.拷贝配置文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;*修改hibernate.cfg.xml文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;(拷贝config目录中的hibernate.cfg.xml文件进行修改即可，用于数据库表的创建，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;默认里边有很多hbm文件，都是JBPM的类库中提供的所以不需要我们自己创建，这是JBPM提供的实体对象及映射文件)&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;*processdefinition.xml流程定义文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;(当部署的时候JBPM会先解析流程定义文件，将对应的数据保存到JBPM提供的特定数据库表)&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;*jbpm.cfg.xml文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;(这是JBPM的配置文件，默认的文件中有很多服务配置可有可无)&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;*log4j.properties文件&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;(支持log4j的使用)&lt;/p&gt;&#xD;
&lt;p&gt;** 说明：这里只是简单的介绍了一下使用，具体的实现项目中写的非常清楚。由于CSDN发表文章不能上传附件，所以只能将项目附件上传到了CSDN的资源中，可以到CSDN我的下载频道进行下载，给大家带来的不便实在抱歉，下载不需要积分。&lt;/p&gt;&#xD;
&lt;p&gt;** 注意：项目的实现是采用在JSP中编写代码，这样是一个不好的编程方式，我之所以这样做完全是为了节省时间，主要目的就是为了演示JBPM的使用，这方面给大家带来的不便还望谅解。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/dynasty/aggbug/2095259.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/dynasty/archive/2011/07/01/2095259.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
