@aspect
public class businesshistory {
/* 连接点(定义一个类包含以下方法):
* public string list(string u){
//this.
system.out.println("list(string u)");
return "return list(string u)";
}*/
/*切面(定义一个类):
*
*(1)切入点:
*@pointcut("execution(* com.tb.app.cf.servicex.emp.*.* (..))") public void
* pointcuta(){ }
*
*切入点表达式(详见spring开发手册核心技术部分):
a. "execution(* com.tb.app.cf.servicex.emp.*.*(..))" emp包下的所有方法
b. "execution(* com.cfdimissionlogmanager.*(..))" cfdimissionlogmanager类的所有方法
c. "execution(* com.service.save(..))" service包下方法名为save,参数个数任意
d. "execution(* org.service.*.*())" 限定service包中任意类任意不带参数的方法名
e. "execution(* org.service.*.*(..)) && args(n,..)
限定service包中任意类任意方法名任意参数个数,并且传入通知的第一个参数名为n,作为通知的参数名n.
注:execution(* org.service.*.*(..)) && args(n,..) 报错,因为args限定有参,但execution限定无参.所以冲突.
f. @afterreturning(value="execution(* org.springside.helloworld.service.usermanager.list(..))&& args(u1)",
argnames="u", //注:尽量不要使用argnames,采用args(..)
returning="retval")
*
*(2)通知:
/*@afterreturning(value="execution(* org.service.*.*(..))"
returning="retval") //限定service包中,任意类任意方法名任意参数个数,但须有返回值的方法
//参数retval必须与returning值retval一致,但retval可与连接点返回值类型一致,名字可不同
public void advicea(string retval) {
system.err.println(retval);
}*/
@afterreturning(value="execution(* org.springside.helloworld.service.usermanager.list(..))&& args(u1)",
returning="retval")
public void advicea(string retval,string u1) {
system.err.println("return:" retval);
system.err.println("param:" u1);
}
}
在action-service.xml中
在sp-service.xml中
在applicationcontext中
这样也实现了一种分层的思想,易于维护,看起来多爽。最后在web.xml中将其关联
注意两点不能写成要不然只能在本文件中找
还有每个配置文件的名称空间一定要写
我们来分析一下上面的配置。我们要把一个服务对象(
'fooservice'
bean)做成事务性的。我们想施加的事务语义封装在定义中。
“把所有以
'get'
开头的方法看做执行在只读事务上下文中,其余的方法执行在默认语义的事务上下文中”。 其中的'transaction-manager'
属性被设置为一个指向platformtransactionmanager
bean的名字(这里指'txmanager'
),该bean将实际上实施事务管理。配置中最后一段是的定义,它确保由
'txadvice'
bean定义的事务通知在应用中合适的点被执行。首先我们定义了 一个切面,它匹配fooservice
接口定义的所有操作,我们把该切面叫做'fooserviceoperation'
。然后我们用一个通知器(advisor)把这个切面与'txadvice'
绑定在一起,表示当'fooserviceoperation'
执行时,'txadvice'
定义的通知逻辑将被执行。一个普遍性的需求是让整个服务层成为事务性的。满足该需求的最好方式是让切面表达式匹配服务层的所有操作方法。例如:
版本:v2.3 (2008-4-13) 作者: 转载请注明
30分钟内让你明白正则表达式是什么,并对它有一些基本的了解,让你可以在自己的程序或网页里使用它。
最重要的是——请给我30分钟,如果你没有使用正则表达式的经验,请不要试图在30秒内入门——除非你是超人 :)
别被下面那些复杂的表达式吓倒,只要跟着我一步一步来,你会发现正则表达式其实并没有你想像中的那么困难。当然,如果你看完了这篇教程之后,发现自己明白了很多,却又几乎什么都记不得,那也是很正常的——我认为,没接触过正则表达式的人在看完这篇教程后,能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理,以后你还需要多练习,多使用,才能熟练掌握正则表达式。
除了作为入门教程之外,本文还试图成为可以在日常工作中使用的正则表达式语法参考手册。就作者本人的经历来说,这个目标还是完成得不错的——你看,我自己也没能把所有的东西记下来,不是吗?
文本格式约定:专业术语 元字符/语法格式 正则表达式 正则表达式中的一部分(用于分析) 对其进行匹配的源字符串 对正则表达式或其中一部分的说明
本文右边有一些注释,主要是用来提供一些相关信息,或者给没有程序员背景的读者解释一些基本概念,通常可以忽略。
字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
很可能你使用过windows/dos下用于文件查找的通配符(wildcard),也就是*和?。如果你想查找某个目录下的所有的word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-12345678或0376-7654321)。
学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子进行修改,实验。下面给出了不少简单的例子,并对它们作了详细的说明。
假设你在一篇英文小说里查找hi,你可以使用正则表达式hi。
这几乎是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,hi,hi,hi这四种情况中的任意一种。
不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b。
\b是正则表达式规定的一个特殊代码(好吧,某些人叫它元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是\b并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置。
如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w。
假如你要找的是hi后面不远处跟着一个lucy,你应该用\bhi\b.*\blucy\b。
这里,.是另一个元字符,匹配除了换行符以外的任意字符。*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复出现任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\blucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是lucy这个单词。
换行符就是'\n',ascii编码为10(十六进制0x0a)的字符。
如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:
0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。
这里的\d是个新的元字符,匹配一位数字(0,或1,或2,或……)。-不是元字符,只匹配它本身——连字符或者减号。
为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}。 这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)。
其它可用的测试工具:
如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人。正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以找一种工具对正则表达式进行测试是很有必要的。
由于在不同的环境下正则表达式的一些细节是不相同的,本教程介绍的是微软 .net framework 2.0下正则表达式的行为,所以,我向你介绍一个.net下的工具。首先你确保已经安装了,然后。这是个绿色软件,下载完后打开压缩包,直接运行regextester.exe就可以了。
下面是regex tester运行时的截图:
现在你已经知道几个很有用的元字符了,如\b,.,*,还有\d.正则表达式里还有更多的元字符,比如\s匹配任意的空白符,包括空格,制表符(tab),换行符,中文全角空格等。\w匹配字母或数字或下划线或汉字等。
对中文/汉字的特殊处理是由.net提供的正则表达式引擎支持的,其它环境下的具体情况请查看相关文档。
下面来看看更多的例子:
\ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。
好吧,现在我们说说正则表达式里的单词是什么意思吧:就是多于一个的连续的\w。不错,这与学习英文时要背的成千上万个同名的东西的确关系不大 :)
\d 匹配1个或更多连续的数字。这里的是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而则匹配重复1次或更多次。
\b\w{6}\b 匹配刚好6个字母/数字的单词。
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
元字符^(和数字6在同一个键位上的符号)和$都匹配一个位置,这和\b有点类似。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的qq号必须为5位到12位数字时,可以使用:^\d{5,12}$。
这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}匹配只能不多不少重复2次,{5,12}则是重复的次数不能少于5次,不能多于12次,否则都不匹配。
因为使用了^和$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的qq号能匹配这个正则表达式的话,那就符合要求了。
和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^和$的意义就变成了匹配行的开始处和结束处。
如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用\.和\*。当然,要查找\本身,你也得用\\.
例如:unibetter\.com匹配unibetter.com,c:\\windows匹配c:\windows。
你已经看过了前面的*,,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码,例如*,{5,12}等):
代码/语法 | 说明 |
---|---|
* | 重复零次或更多次 |
重复一次或更多次 | |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
下面是一些使用重复的例子:
windows\d 匹配windows后面跟1个或更多数字
^\w 匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思得看选项设置)
要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。
我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字;同理[a-z0-9a-z_]也完全等同于\w(如果只考虑英文的话)。
下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}。
“(”和“)”也是元字符,后面的里会提到,所以在这里需要使用。
这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。
不幸的是,刚才那个表达式也能匹配010)12345678或(022-87654321这样的“不正确”的格式。要解决这个问题,我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。听不明白?没关系,看例子:
0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。
\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。
\d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。
我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。
(\d{1,3}\.){3}\d{1,3}是一个简单的ip地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字,(\d{1,3}\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。
ip地址中每个数字都不能大于255,大家千万不要被《24》第三季的编剧给忽悠了...
不幸的是,它也将匹配256.300.888.999这种不可能存在的ip地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的ip地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)。
理解这个表达式的关键是理解2[0-4]\d|25[0-5]|[01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。
有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:
代码/语法 | 说明 |
---|---|
\w | 匹配任意不是字母,数字,下划线,汉字的字符 |
\s | 匹配任意不是空白符的字符 |
\d | 匹配任意非数字的字符 |
\b | 匹配不是单词开头或结束的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou这几个字母以外的任意字符 |
例子:\s 匹配不包含空白符的字符串。
] >匹配用尖括号括起来的以a开头的字符串。
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。难以理解?请看示例:
\b(\w )\b\s \1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w )\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s ),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。
你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?
使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:
分类 | 代码/语法 | 说明 |
---|---|---|
捕获 | (exp) | 匹配exp,并捕获文本到自动命名的组里 |
(? |
匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp) | |
(?:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号 | |
零宽断言 | (?=exp) | 匹配exp前面的位置 |
(?<=exp) | 匹配exp后面的位置 | |
(?!exp) | 匹配后面跟的不是exp的位置 | |
(? | 匹配前面不是exp的位置 | |
注释 | (?#comment) | 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读 |
我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号。
地球人,是不是觉得这些术语名称太复杂,太难记了?我也和你一样。知道有这么一种东西就行了,它叫什么,随它去吧!“无名,万物之始...”
接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。最好还是拿例子来说明吧:
断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。
(?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w (?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找i'm singing while you're dancing.时,它会匹配sing和danc。
(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w \b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。
假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})*\b,用它对1234567890进行查找时结果是234567890。
下面这个例子同时使用了这两种断言:(?<=\s)\d (?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)。
前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:
\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像iraq,benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。
零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w) \b匹配不包含连续字符串abc的单词。
同理,我们可以用(?,零宽度正回顾后发断言来断言此位置的前面不能匹配表达式exp:(?匹配前面不是小写字母的七位数字。
请详细分析表达式(?<=<(\w )>).*(?=<\/\1>),这个表达式最能表现零宽断言的真正用途。
一个更复杂的例子:(?<=<(\w )>).*(?=<\/\1>)匹配不包含属性的简单html标签内里的内容。()指定了这样的前缀:被尖括号括起来的单词(比如可能是),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w )匹配的内容,这样如果前缀实际上是的话,后缀就是了。整个表达式匹配的是和之间的内容(再次提醒,不包括前缀和后缀本身)。
小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。
要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:
(?<= # 断言要匹配的文本的前缀
<(\w )> # 查找尖括号括起来的字母或数字(即html/xml标签)
) # 前缀结束
.* # 匹配任意文本
(?= # 断言要匹配的文本的后缀
<\/\1> # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签
) # 后缀结束
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。考虑这个表达式:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:
a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。
为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——the match that begins earliest wins。
代码/语法 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
在c#中,你可以使用来设置正则表达式的处理选项。如:regex regex = new regex("\ba\w{6}\b", regexoptions.ignorecase);
上面介绍了几个选项如忽略大小写,处理多行等,这些选项能用来改变处理正则表达式的方式。下面是.net中常用的正则表达式选项:
名称 | 说明 |
---|---|
ignorecase(忽略大小写) | 匹配时不区分大小写。 |
multiline(多行模式) | 更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.) |
singleline(单行模式) | 更改.的含义,使它与每一个字符匹配(包括换行符\n)。 |
ignorepatternwhitespace(忽略空白) | 忽略表达式中的非转义空白并启用由#标记的注释。 |
righttoleft(从右向左查找) | 匹配从右向左而不是从左向右进行。 |
explicitcapture(显式捕获) | 仅捕获已被显式命名的组。 |
ecmascript(javascript兼容模式) | 使表达式的行为与它在javascript里的行为一致。 |
一个经常被问到的问题是:是不是只能同时使用多行模式和单行模式中的一种?答案是:不是。这两个选项之间没有任何关系,除了它们的名字比较相似(以至于让人感到疑惑)以外。
这里介绍的平衡组语法是由.net framework支持的;其它语言/库不一定支持这种功能,或者支持此功能但需要使用不同的语法。
有时我们需要匹配像( 100 * ( 50 15 ) )这样的可嵌套的层次性结构,这时简单地使用\(. \)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?
为了避免(和\(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx
这里需要用到以下的语法构造:
如果你不是一个程序员(或者你自称程序员但是不知道堆栈是什么东西),你就这样理解上面的三种语法吧:第一个就是在黑板上写一个"group",第二个就是从黑板上擦掉一个"group",第三个就是看黑板上写的还有没有"group",如果有就继续匹配yes部分,否则就匹配no部分。
我们需要做的是每碰到了左括号,就在压入一个"open",每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空--如果不为空那就证明左括号比右括号多,那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。
< #最外层的左括号
[^<>]* #最外层的左括号后面的不是括号的内容
(
(
(?'open'<) #碰到了左括号,在黑板上写一个"open"
[^<>]* #匹配左括号后面的不是括号的内容
)
(
(?'-open'>) #碰到了右括号,擦掉一个"open"
[^<>]* #匹配右括号后面不是括号的内容
)
)*
(?(open)(?!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"open";如果还有,则匹配失败
> #最外层的右括号
平衡组的一个最常见的应用就是匹配html,下面这个例子可以匹配嵌套的
我已经描述了构造正则表达式的大量元素,还有一些我没有提到的东西。下面是未提到的元素的列表,包含语法和简单的说明。你可以在网上找到更详细的参考资料来学习它们--当你需要用到它们的时候。如果你安装了msdn library,你也可以在里面找到关于.net下正则表达式详细的文档。
代码/语法 | 说明 |
---|---|
\a | 报警字符(打印它的效果是电脑嘀一声) |
\b | 通常是单词分界位置,但如果在字符类里使用代表退格 |
\t | 制表符,tab |
\r | 回车 |
\v | 竖向制表符 |
\f | 换页符 |
\n | 换行符 |
\e | escape |
\0nn | ascii代码中八进制代码为nn的字符 |
\xnn | ascii代码中十六进制代码为nn的字符 |
\unnnn | unicode代码中十六进制代码为nnnn的字符 |
\cn | ascii控制字符。比如\cc代表ctrl c |
\a | 字符串开头(类似^,但不受处理多行选项的影响) |
\z | 字符串结尾或行尾(不受处理多行选项的影响) |
\z | 字符串结尾(类似$,但不受处理多行选项的影响) |
\g | 当前搜索的开头 |
\p{name} | unicode中命名为name的字符类,例如\p{isgreek} |
(?>exp) | 贪婪子表达式 |
(? |
平衡组 |
(?im-nsx:exp) | 在子表达式exp中改变处理选项 |
(?im-nsx) | 为表达式后面的部分改变处理选项 |
(?(exp)yes|no) | 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no |
(?(exp)yes) | 同上,只是使用空表达式作为no |
(?(name)yes|no) | 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no |
(?(name)yes) | 同上,只是使用空表达式作为no |
好吧,我承认,我骗了你,读到这里你肯定花了不止30分钟.相信我,这是我的错,而不是因为你太笨.我之所以说"30分钟",是为了让你有信心,有耐心继续下去.既然你看到了这里,那证明我的阴谋成功了.被忽悠的感觉很爽吧?
要投诉我,或者觉得我其实可以做得更好,或者有任何其它问题,欢迎来让我知道.
----------------------http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm
error [main] (cgliblazyinitializer.java:130) - cglib enhancement failed: item.common.hibernate.query.partsum.partsum
java.lang.nosuchmethoderror: org.objectweb.asm.classvisitor.visit(iiljava/lang/string;ljava/lang/string;[ljava/lang/string;ljava/lang/string;)v
warn [main] (pojoentitytuplizer.java:173) - could not create proxy factory for: item.common.hibernate.query.partsum.partsum
org.hibernate.hibernateexception: cglib enhancement failed: item.common.hibernate.query.partsum.partsum
caused by: java.lang.nosuchmethoderror: org.objectweb.asm.classvisitor.visit(iiljava/lang/string;ljava/lang/string;[ljava/lang/string;ljava/lang/string;)v
///////////////////////////////////////////////////////////////////////////////////////////
error [main] (cgliblazyinitializer.java:130) - cglib enhancement failed: item.common.hibernate.fourm.fourm
java.lang.noclassdeffounderror
warn [main] (pojoentitytuplizer.java:173) - could not create proxy factory for:item.common.hibernate.fourm.fourm
org.hibernate.hibernateexception: cglib enhancement failed: item.common.hibernate.fourm.fourm
caused by: java.lang.noclassdeffounderror
at org.hibernate.proxy.cgliblazyinitializer.getproxyfactory(cgliblazyinitializer.java:116)
////////////////////////////////////////////////////////////////////////////////////////////
error [main] (cgliblazyinitializer.java:130) - cglib enhancement failed: item.common.hibernate.picture
java.lang.noclassdeffounderror
warn [main] (pojoentitytuplizer.java:173) - could not create proxy factory for:item.common.hibernate.picture
org.hibernate.hibernateexception: cglib enhancement failed: item.common.hibernate.picture
caused by: java.lang.noclassdeffounderror
at org.hibernate.proxy.cgliblazyinitializer.getproxyfactory(cgliblazyinitializer.java:116)
凯发天生赢家一触即发官网的解决方案:
spring 和 hibernate 共用的一些 jar 文件发生了版本冲突, 删除 web-inf/lib/asm-2.2.3.jar 然后重启 tomcat.
四.错误提示:
org.springframework.beans.factory.beancreationexception: error creating bean with name 'sessionfactory' defined in class path resource [applicationcontext.xml]: invocation of init method failed; nested exception is java.lang.nosuchmethoderror: org.objectweb.asm.classvisitor.visit(iiljava/lang/string;ljava/lang/string;[ljava/lang/string;ljava/lang/string;)v
...
caused by: java.lang.nosuchmethoderror: org.objectweb.asm.classvisitor.visit(iiljava/lang/string;ljava/lang/string;[ljava/lang/string;ljava/lang/string;)v
解决方法:
并不是真正的bean name错了,也不是找不到他的方法.是asm-2.2.3.jar与asm.jar冲突.
正确的删除asm-2.2.3.jar方法是到项目的发布文件夹\web-inf\lib中删除,不能用myeclipse里删除.因为他在发布时仍没删除.
五。
严重: exception sending context initialized event to listener instance of class
org.springframework.web.context.contextloaderlistener
org.springframework.beans.factory.beancreationexception: error creating bean with name 'sessionfactory' defined in
servletcontext resource [/web-inf/applicationcontext.xml]: invocation of init method failed; nested exception is
org.hibernate.duplicatemappingexception: duplicate class/entity mapping check
at org.springframework.beans.factory.support.abstractautowirecapablebeanfactory.initializebean
(abstractautowirecapablebeanfactory.java:1336)
... 39 more
2009-8-21 11:52:37 org.apache.catalina.core.standardcontext start
六。
严重: exception sending context initialized event to listener instance of class
org.springframework.web.context.contextloaderlistener
org.springframework.beans.factory.beancreationexception: error creating bean with name 'sessionfactory' defined in
servletcontext resource [/web-inf/applicationcontext.xml]: invocation of init method failed; nested exception is
org.hibernate.mappingexception: entity class not found: check
at org.springframework.beans.factory.support.abstractautowirecapablebeanfactory.initializebean
... 52 more
2009-8-21 11:56:45 org.apache.catalina.core.standardcontext start
int i=integer.parseint(str);
如何将字串 string 转换成integer ?
integer integer=integer.valueof(str);
如何将整数 int 转换成字串 string ?
1.) string s = string.valueof(i);
2.) string s = integer.tostring(i);
3.) string s = "" i;
如何将整数 int 转换成integer ?
integer integer=new integer(i);
如何将integer 转换成字串 string ?
integer integer=string
如何将integer 转换成 int ?
int num=integer.intvalue();
如何将string转换成 bigdecimal ?
bigdecimal d_id = new bigdecimal(str);
如何将 string 转换成 char ?
char[] ca="123".tochararray();
如何将char转换成string?
string s=ca.tostring(); //任何类型都可以采用tostring()转换成string类型
//-----------------日期-------------------------
calendar calendar=calendar.getinstance();
int year=calendar.get(calendar.year);
int month=calendar.get(calendar.month) 1;
int day=calendar.get(calendar.date);
获取今天的日期字符串
string today=java.text.dateformat.getdateinstance().format(new java.util.date());
获取今天的日期
new java.sql.date(system.currenttimemillis())
//获取applicationcontext.xml文件中定义的bean
pictureservice ps=(pictureservice)ac.getbean("pictureservice");
(3)faces-config.xml
(4)设置过滤器,解决乱码问题
详见:第二条
以上配置完成后.jsf就可以访问spring管理的bean.
四.spring hiberante集成时,asm-2.2.3.jar与asm.jar冲突:
org.springframework.beans.factory.beancreationexception: error creating bean with name 'sessionfactory' defined in class path resource [applicationcontext.xml]: invocation of init method failed; nested exception is java.lang.nosuchmethoderror: org.objectweb.asm.classvisitor.visit(iiljava/lang/string;ljava/lang/string;[ljava/lang/string;ljava/lang/string;)v
...
caused by: java.lang.nosuchmethoderror: org.objectweb.asm.classvisitor.visit(iiljava/lang/string;ljava/lang/string;[ljava/lang/string;ljava/lang/string;)v
正确的解决方法:
是到项目的发布文件夹\web-inf\lib下删除asm-2.2.3.jar,不能用myeclipse里删除.因为他在发布时仍没删除.
五.创建进行分类的包.
com.dao(数据访问层), com.service(业务层), com.web(表示层)
六.创建类.(完成model层设计).
*****************************(也可以利用myeclipse反向orm功能自动产生,但应注意自动配置引起的"类完全名"的错误)*******************
(一)数据访问层(dao)开发:
spring 配置文件(附加)
(1)配置数据源 <bean id="datasource" ...> //myeclipse hibernate自动反转工具自动配置
(2)配置会话工厂 <bean id="sessionfactory" ...> //myeclipse hibernate自动反转工具自动配置
(3)配置事务管理器
(5)数据访问层bean作为属性注入业务层property中. (注:不要用new创建数据访问层对象,否则得不到servletcontext而报错.)
例:
/**数据访问层bean(myeclipse hibernate自动反转工具配置)*/
/**业务层bean(myeclipse spring配置文档中可以配置,但需先定义好相关类)*/
userdao" />
1. 创建 数据访问层接口: 必须创建,详见:
public interface 数据访问层接口 { //在类已存在的情况下,添加其接口:refactor(重构)->extract interfaces提取接口
public string add...();
public string save...();
...... //update, delete, load
}
2 .创建数据访问层实现类 //orm反向功能自动配置
public class 数据访问层实现类 extends hibernatedaosupport implements 数据访问层接口 {
...... //gethibernatetemplate().save(user)
}
在实现类的方法(throw dataaccessexception)中封装 gethibernatetemplate().save(user) 等spring已封装的持久化方法.(注:如果使用hibernate api 要用try{...}catch{...}自行处理导常.)
3.注意事项: could not initialize proxy - no session 异常, 可能是没有联表查询.详见
*****************************(也可以利用myeclipse反向orm功能自动产生)***********************************
(二)业务逻辑层(service)开发:
1.创建业务逻辑层接口.
package com.service;
import com.dao.checkdao;
public interface icheckservice {
public abstract checkdao getcheckdao();
public abstract void setcheckdao(checkdao checkdao);
public abstract string searchcheck();
}
2.创建业务逻辑层实现类
public class 业务逻辑层实现类 implements 业务逻辑层接口.
在实现类中. 引用的数据访问层对象实例采用di的设值方式注入
在业务逻辑方法(throw dataaccessexception)中,使用数据访问层对象实例来访问数据库.
package com.service;
import java.util.list;
import org.json.simple.jsonarray;
import org.json.simple.jsonobject;
import com.dao.check;
import com.dao.checkdao;
import com.dao.icheckdao;
public class checkservice implements icheckservice {
private checkdao checkdao;
public checkdao getcheckdao() {
return checkdao;
}
public void setcheckdao(checkdao checkdao) {
this.checkdao = checkdao;
}
public string searchcheck(){
list
jsonarray ja=new jsonarray();
jsonobject jo=new jsonobject();
for(check c:l){
// jo.put("department", c.getdepartment());
// jo.put("checkid", c.getcheckid());
// jo.put("nam", c.getnam());
// jo.put("num", c.getnum());
// jo.put("ondutytime", c.getondutytime());
// jo.put("offdutytime", c.getoffdutytime1());
// jo.put("registerdate", c.getregisterdate());
// jo.put("overtime", c.getovertime());
// ja.add(jo.clone());
// jo.clear();
}
return "";
}
}
3.数据访问层bean作为属性注入业务逻辑层实现类的property中. (注:不要用new创建数据访问层对象,否则得不到servletcontext而报错.)
例:
/**数据访问层bean(hibernate自动反转工具自动配置)*/
/**业务逻辑层bean(spring配置文档中手动配置,但需先定义好相关类)*/
userdao" />
(三).表示层开发
(1)在 faces-config.xml 中配置托管bean.(实现控制层层设计)
业务层bean作为属性注入control层中.
package com.web;
import com.service.icheckservice;
public class searchall {
private icheckservice searchservice;
private string searchdata;
public searchall() {
}
public icheckservice getsearchservice() {
return searchservice;
}
public void setsearchservice(icheckservice searchservice) {
this.searchservice = searchservice;
}
public string getsearchdata() {
searchdata = searchservice.searchcheck();
return searchdata;
}
public void setsearchdata(string searchdata) {
this.searchdata = searchdata;
}
}
(2)bean访问客户端传来的参数值
httpservletrequest request=(httpservletrequest) facescontext.getcurrentinstance().getexternalcontext().getrequest();
string s=request.getparameter("webnam");
七.页面设计.(实现view层设计)
index.faces:(共二行)
<%@ page language="java" pageencoding="utf-8"%> //解决extjs 接收数据中有中文出现"?????"等乱码
${searchall.searchdata} //解决js接收数据.ext.store({url:index.faces,...})
页面中访问bean的二种方法
方法一:直接在.faces页面中使用${userbean.property} (推荐) //property:get/set methord
注,不能使用${userbean.method}
名稱1: ${userbean.searchdata} //正确
//#{userbean.searchdata} //报错
名稱2:
//
// //报错
名稱3: //正确,而且仍然可以使用jsf的managedbean
jsf表达式#{...}只能用在jsf标签里,jsp表达式${...}也只能用jsp页面里.
jsp el的语法格式:${...} 不能放在<%...%>中.
方法二: 在.jsp页面中,直接获取业务层定义的bean
<%@ page import="javax.servlet.servletcontext" %>
<%@ page import="org.springframework.context.applicationcontext" %>
<%@ page import="org.springframework.web.context.support.webapplicationcontextutils" %>
<%
//获取applicationcontext对象
servletcontext servletcontext = request.getsession().getservletcontext();
applicationcontext ac = webapplicationcontextutils.getwebapplicationcontext(servletcontext);
//获取applicationcontext.xml文件中定义的bean
userserviceps=(userservice)ac.getbean("userservice");
八.导出war
file->export->j2ee->war file
引用代理bean:
(二)org.springframework.aop.framework.autoproxy.beannameautoproxycreator: 根据beanname自动创建事务代理
org.springframework.transaction.interceptor.transactioninterceptor: 拦截器
class = "org.springframework.transaction.interceptor.transactionproxyfactorybean" >
< property name = "transactionmanager" >
< ref bean = "transactionmanager" />
property >
< property name = "target" >
< ref local = "userdao" />
property >
bean >
1 、用 local 属性指定目标 bean 可以利用 xml 解析器的能力在同一个 xml配置文件中验证 xml id 引用,没有匹配的元素,xml 解析器就会产生一个 error, 所以如果引用的 bean 在同一个 xml配置 文件中 , 那么用 local 形式是最好的选择 .
2 、可以这么说, 是寻找所有 xml配置文件中的 bean; 是寻找本 xml 文件中的 bean.
spring 中进行事务管理的通常方式是利用aop(面向切片编程)的方式,为普通java类封装事务控制,它是通过动态代理实现的,由于接口是延迟实例化的, spring在这段时间内通过拦截器,加载事务切片。原理就是这样,具体细节请参考jdk中有关动态代理的文档。本文主要讲解如何在spring中进行事务控制。
动态代理的一个重要特征是,它是针对接口的,所以我们的dao要通过动态代理来让spring接管事务,就必须在dao前面抽象出一个接口,当然如果没有这样的接口,那么spring会使用cglib来解决问题,但这不是spring推荐的方式,所以不做讨论.
大多数spring用户选择声明式事务管理。这是最少影响应用代码的选择, 因而这是和非侵入性的轻量级容器的观念是一致的。
从考虑ejb cmt和spring声明式事务管理的相似以及不同之处出发是很有益的。 它们的基本方法是相似的:都可以指定事务管理到单独的方法;如果需要可以在事务上 下文调用setrollbackonly()方法。不同之处如下:
不象ejb cmt绑定在jta上,spring声明式事务管理可以在任何环境下使用。 只需更改配置文件,它就可以和jdbc、jdo、hibernate或其他的事务机制一起工作
spring 的声明式事务管理可以应用到普通java对象,不仅仅是特殊的类,如ejb
spring提供声明式回滚规则:ejb没有对应的特性, 我们将在下面讨论这个特性。回滚可以声明式控制,不仅仅是编程式的
spring允许你通过aop定制事务行为。例如,如果需要,你可以在事务 回滚中插入定制的行为。你也可以增加任意的通知,就象事务通知一样。使用 ejb cmt,除了使用setrollbackonly(),你没有办法能 够影响容器的事务管理
spring不提供高端应用服务器提供的跨越远程调用的事务上下文传播。如果你需要这些特性,我们推荐你使用ejb。然而,不要轻易使用这些特性。通常我们并不希望事务跨越远程调用
回滚规则的概念是很重要的:它们使得我们可以指定哪些异常应该发起自动回滚。我们在配置文件中,而不是java代码中,以声明的方式指定。因此,虽然我们仍然可以编程调用transactionstatus对象的 setrollbackonly()方法来回滚当前事务,多数时候我们可以指定规则,如myapplicationexception应该导致回滚。 这有显著的优点,业务对象不需要依赖事务基础设施。例如,它们通常不需要引入任何spring api,事务或其他任何东西。
ejb的默认行为是遇到系统异常(通常是运行时异常), ejb容器自动回滚事务。ejb cmt遇到应用程序异常 (除了java.rmi.remoteexception外的checked异常)时不会自动回滚事务。虽然spring声明式事务管理沿用ejb的约定(遇到unchecked 异常自动回滚事务),但是这是可以定制的。
按照我们的测试,spring声明式事务管理的性能要胜过ejb cmt。
通常通过transactionproxyfactorybean设置spring事务代理。我们需要一个目标对象包装在事务代理中。这个目标对象一般是一个普通java对象的bean。当我们定义transactionproxyfactorybean时,必须提供一个相关的 platformtransactionmanager的引用和事务属性。 事务属性含有上面描述的事务定义。
class="org.springframework.transaction.interceptor.transactionproxyfactorybean">
propagation_required,-mycheckedexception
propagation_required
propagation_required,readonly
事务代理会实现目标对象的接口:这里是id为petstoretarget的bean。(使用 cglib也可以实现具体类的代理。只要设置proxytargetclass属性为true就可以。如果目标对象没有实现任何接口,这将自动设置该属性为true。通常,我们希望面向接口而不是类编程。)使用proxyinterfaces属性来限定事务代理来代 理指定接口也是可以的(一般来说是个好想法)。也可以通过从 org.springframework.aop.framework.proxyconfig继承或所有aop代理工厂共享 的属性来定制transactionproxyfactorybean的行为。
这里的transactionattributes属性定义在 org.springframework.transaction.interceptor.namematchtransactionattributesource 中的属性格式来设置。这个包括通配符的方法名称映射是很直观的。注意 insert*的映射的值包括回滚规则。添加的-mycheckedexception 指定如果方法抛出mycheckedexception或它的子类,事务将 会自动回滚。可以用逗号分隔定义多个回滚规则。-前缀强制回滚, 前缀指定提交(这允许即使抛出unchecked异常时也可以提交事务,当然你自己要明白自己 在做什么)。
transactionproxyfactorybean允许你通过 “preinterceptors”和“postinterceptors”属性设置“前”或“后”通知来提供额外的 拦截行为。可以设置任意数量的“前”和“后”通知,它们的类型可以是 advisor(可以包含一个切入点), methodinterceptor或被当前spring配置支持的通知类型 (例如throwadvice, afterreturningtadvice或beforeadvice, 这些都是默认支持的)。这些通知必须支持实例共享模式。如果你需要高级aop特 性来使用事务,如有状态的maxin,那最好使用通用的 org.springframework.aop.framework.proxyfactorybean, 而不是transactionproxyfactorybean实用代理创建者。
也可以设置自动代理:配置aop框架,不需要单独的代理定义类就可以生成类的 代理。
附两个spring的事务配置例子: /**
* 使用 hql 语句进行操作
* @param hql hsql 查询语句
* @param offset 开始取数据的下标
* @param length 读取数据记录数
* @return list 结果集
*/
public list getlistforpage ( final string hql , final int offset , final int length ) {
list list = gethibernatetemplate().executefind ( new hibernatecallback ( ) {
public object doinhibernate ( session session ) throws hibernateexception, sqlexception {
query query = session.createquery ( hql ) ;
query.setfirstresult ( offset ) ;
query.setmaxresults ( length ) ;
list list = query.list ( ) ;
return list ;
}
}) ;
return list ;
}
//---------------------------------------------------------------------------
final string querystring = "from fourm"; //必须定义final类型
list l= gethibernatetemplate().executefind(new hibernatecallback(){
public object doinhibernate ( session session ) throws hibernateexception, sqlexception {
query query = session.createquery ( querystring ) ;
query.setfirstresult ( 0 ) ;
query.setmaxresults ( 10 ) ;
list list = query.list ( ) ;
return list ;
}
//------------------------------------------------------------
为什么不使用getsession(),而使用回调的详细说明:
紫蝶∏飛揚↗
听个spring hibernate开发的问题.
spring想调用hibernate的方法有两种,一种是使用getsession();一种是:hibernatecallback回调.
他们有什么不同?
海绵♂宝宝
前者没有事务管控,spring把hibernate最原始的session给你!但是它不被声明式事务管理的服务!
紫蝶∏飛揚↗
...声明式事务不会对他进行事务管理?
海绵♂宝宝
后者也是把session返给你,但是通过闭包回调的形式给你使用,spring会在给你之前做一下手脚,例如:添加声明式事务管理中声明的事务属性!
海绵♂宝宝
因为你写的方法通过匿名函数闭包,然后由spring去负责执行闭包体内的方法!
海绵♂宝宝
java里的借口回调基本都是借用匿名内部类实现的闭包!
海绵♂宝宝
你看一下spring的hibernatedaosupport的源代码就知道运行过程了!
海绵♂宝宝
直接使用getsession()还不享有spring提供的模板支持,也就是说你得手动关闭连接,否则会一直挂在那儿,直到超时连接池才会回收这个连接!
海绵♂宝宝
这样很容器连接池连接数量溢出!你懂了啊?
紫蝶∏飛揚↗
嗯.
紫蝶∏飛揚↗
new hibernatecallback(){
public object doinhibernate ( session session ) throws hibernateexception, sqlexception {
必须这样的方式吗?
海绵♂宝宝
建议补习一下编程中的闭包、回调的知识,再看看spring的那段代码就可以彻底理解了!
海绵♂宝宝
恩!
紫蝶∏飛揚↗
哦.明白了.
海绵♂宝宝
如果你觉得gethibernatetemplate()提供的方法不足以满足你的要求!你就得这么使用!
紫蝶∏飛揚↗
明白.不够用的情况下用回调.
海绵♂宝宝
闭包javascript支持的比较好,因为它是以函数为单元的,java过于强调面向对象,所以只能用匿名内部类来有限的支持闭包!标准的回调都是方法回调,也就是闭包到方法中,而不是类中!所以java中的闭包和其他的语言有一些区别的,例如访问外部变量必须是final的!
海绵♂宝宝
据说jdk1.7会加入对函数闭包调用的支持!
紫蝶∏飛揚↗
...历害.
紫蝶∏飛揚↗
偶只知道这样用,不知道为什么这样用.
海绵♂宝宝
那可不行啊!呵呵!我最一开始也不大明白,后来仔细研究了闭包、回调的知识就懂了!
其实这种用法在swing编程里天天都用啊!把一个匿名的监听器实现类注册到事件源,只不过那个时候没有注意到这是闭包和回调罢了!
紫蝶∏飛揚↗
看来有时间得好好看看了...
dependency inversion在下面這篇文章中有了清楚的解釋:
簡單的說,在模組設計時,高層的抽象模組通常是與業務相關的模組,它應該具有重用性,而不依賴於低層的實作模組,例如如果低層模組原先是軟碟存取模式,而高層模組是個存檔備份的需求,如果高層模組直接叫用低層模組的函式,則就對其產生了依賴關係。
舉個例子,例如下面這個程式:
#include.... void save() { .... savetofloppy() } }
由於save()程式依賴於savetofloppy(),如果今天要更換低層的存儲模組為usb碟,則這個程式沒有辦法重用,必須加以修改才行,低層模組的更動造成了高層模組也必須跟著更動,這不是一個好的設計方式,我們希望模組都依賴於模組的抽象,這樣才可以重用高層的業務設計。
如果以物件導向的方式來設計,依賴反轉(dependency inversion)的解釋變為程式不應依賴實作,而是依賴於抽象,實作必須依賴於抽象。我們來看看下面這個java程式:
public class businessobject { private floppywriter writer = new floppywriter(); .... public void save() { ... writer.savetofloppy(); } }
在這個程式中,businessobject的存檔依賴於實際的floppywriter,如果今天我們想要將存檔改為存至usb碟,我們必須修改或繼承businessobject進行擴展,而無法直接使用businessobject。
如果透過介面的宣告,可以改進此一情況,例如:
public interface idevicewriter { public void savetodevice(); } public class businessobject { private idevicewriter writer; public void setdevicewriter(idevicewriter writer) { this.writer = writer; } public void save() { .... writer.savetodevice(); } }
這樣一來,businessobject就是可重用的,如果今天我有存儲至floppy或usb碟的需求,我只要實作idevicewriter即可,而不用修改businessobject:
public class floppywriter implement idevicewriter { public void savetodevice() { .... // 實際儲存至floppy的程式碼 } } public class usbdiskwriter implement idevicewriter { public void savetodevice() { .... // 實際儲存至usbdisk的程式碼 } }
從這個角度來看,dependency inversion的意思即是程式不依賴於實作,而是程式與實作都要依賴於抽象。
ioc的control是控制的意思,其實其背後的意義也是一種依賴關係的轉移,如果a依賴於b,其意義即是b擁有控制權,我們要轉移這種關係,所以依賴關係的反轉即是控制關係的反轉,藉由控制關係的轉移,我們可以獲得元件的可重用性,在上面的java程式中,整個控制權從實際的 floppywriter轉移至抽象的idevicewriter介面上,使得businessobject、floppywriter、 usbdiskwriter這幾個實現依賴於抽象的idevicewriter介面。
從容器(container)的角度,程式的業務邏輯部份應是可以重用的,不應受到所使用框架或容器的影響,因為我們可能轉移整個業務邏輯至其它的框架或容器,如果業務邏輯過於依賴容器,則轉移至其它的框架或容器時,就會發生困難。
ioc在容器的角度,可以用這麼一句好萊塢名言來代表:"don't call me, i'll call you." 以程式的術語來說的話,就是「不要向容器要求您所需要的(物件)資源,容器會自動將這些物件給您!」。ioc要求的是容器不侵入應用程式本身,應用程式本身提供好介面,容器可以透過這些介面將所需的資源注至程式中,應用程式不向容器主動要求資源,故而不會依賴於容器的元件,應用程式本身不會意識到正被容器使用,可以隨時從容器中脫離轉移而不用作任何的修改,而這個特性正是一些業務邏輯中間件最需要的。
-------------[转 added by , last edited by on aug 13, 2005 () ]