`
zjfgf
  • 浏览: 12009 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论
收藏列表
标题 标签 来源
StringUtils java
org.apache.commons.lang.StringUtils类 
(转)StringUtils 方法的操作对象是 java.lang.String 类型的对象,是 JDK 提供的 String 类型操作方法的补充,并且是 null 安全的(即如果输入参数 String 为 null 则不会抛出 NullPointerException ,而是做了相应处理,例如,如果输入为 null 则返回也是 null 等,具体可以查看源代码)。 
除了构造器,StringUtils 中一共有130多个方法,并且都是 static 的,所以我们可以这样调用 StringUtils.xxx() 
下面分别对一些常用方法做简要介绍: 
1. public static boolean isEmpty(String str) 
判断某字符串是否为空,为空的标准是 str==null 或 str.length()==0 
下面是 StringUtils 判断是否为空的示例: 

StringUtils.isEmpty(null) = true 
StringUtils.isEmpty("") = true 
StringUtils.isEmpty(" ") = false //注意在 StringUtils 中空格作非空处理 
StringUtils.isEmpty(" ") = false 
StringUtils.isEmpty("bob") = false 
StringUtils.isEmpty(" bob ") = false 

2. public static boolean isNotEmpty(String str) 
判断某字符串是否非空,等于 !isEmpty(String str) 
下面是示例: 

StringUtils.isNotEmpty(null) = false 
StringUtils.isNotEmpty("") = false 
StringUtils.isNotEmpty(" ") = true 
StringUtils.isNotEmpty(" ") = true 
StringUtils.isNotEmpty("bob") = true 
StringUtils.isNotEmpty(" bob ") = true 
3. public static boolean isBlank(String str) 
判断某字符串是否为空或长度为0或由空白符(whitespace) 构成 
下面是示例: 
StringUtils.isBlank(null) = true 
StringUtils.isBlank("") = true 
StringUtils.isBlank(" ") = true 
StringUtils.isBlank(" ") = true 
StringUtils.isBlank("\t \n \f \r") = true //对于制表符、换行符、换页符和回车符 

StringUtils.isBlank() //均识为空白符 
StringUtils.isBlank("\b") = false //"\b"为单词边界符 
StringUtils.isBlank("bob") = false 
StringUtils.isBlank(" bob ") = false 
4. public static boolean isNotBlank(String str) 
判断某字符串是否不为空且长度不为0且不由空白符(whitespace) 构成,等于 !isBlank(String str) 
下面是示例: 

StringUtils.isNotBlank(null) = false 
StringUtils.isNotBlank("") = false 
StringUtils.isNotBlank(" ") = false 
StringUtils.isNotBlank(" ") = false 
StringUtils.isNotBlank("\t \n \f \r") = false 
StringUtils.isNotBlank("\b") = true 
StringUtils.isNotBlank("bob") = true 
StringUtils.isNotBlank(" bob ") = true 
5. public static String trim(String str) 
去掉字符串两端的控制符(control characters, char <= 32) , 如果输入为 null 则返回null 
下面是示例: 
StringUtils.trim(null) = null 
StringUtils.trim("") = "" 
StringUtils.trim(" ") = "" 
StringUtils.trim(" \b \t \n \f \r ") = "" 
StringUtils.trim(" \n\tss \b") = "ss" 
StringUtils.trim(" d d dd ") = "d d dd" 
StringUtils.trim("dd ") = "dd" 
StringUtils.trim(" dd ") = "dd" 
6. public static String trimToNull(String str) 
去掉字符串两端的控制符(control characters, char <= 32) ,如果变为 null 或"",则返回 null 
下面是示例: 
StringUtils.trimToNull(null) = null 
StringUtils.trimToNull("") = null 
StringUtils.trimToNull(" ") = null 
StringUtils.trimToNull(" \b \t \n \f \r ") = null 
StringUtils.trimToNull(" \n\tss \b") = "ss" 
StringUtils.trimToNull(" d d dd ") = "d d dd" 
StringUtils.trimToNull("dd ") = "dd" 
StringUtils.trimToNull(" dd ") = "dd" 
7. public static String trimToEmpty(String str) 
去掉字符串两端的控制符(control characters, char <= 32) ,如果变为 null 或 "" ,则返回 "" 
下面是示例: 
StringUtils.trimToEmpty(null) = "" 
StringUtils.trimToEmpty("") = "" 
StringUtils.trimToEmpty(" ") = "" 
StringUtils.trimToEmpty(" \b \t \n \f \r ") = "" 
StringUtils.trimToEmpty(" \n\tss \b") = "ss" 
StringUtils.trimToEmpty(" d d dd ") = "d d dd" 
StringUtils.trimToEmpty("dd ") = "dd" 
StringUtils.trimToEmpty(" dd ") = "dd" 
8. public static String strip(String str) 
去掉字符串两端的空白符(whitespace) ,如果输入为 null 则返回 null 
下面是示例(注意和 trim() 的区别): 
StringUtils.strip(null) = null 
StringUtils.strip("") = "" 
StringUtils.strip(" ") = "" 
StringUtils.strip(" \b \t \n \f \r ") = "\b" 
StringUtils.strip(" \n\tss \b") = "ss \b" 
StringUtils.strip(" d d dd ") = "d d dd" 
StringUtils.strip("dd ") = "dd" 
StringUtils.strip(" dd ") = "dd" 
9. public static String stripToNull(String str) 
去掉字符串两端的空白符(whitespace) ,如果变为 null 或"",则返回 null 
下面是示例(注意和 trimToNull() 的区别): 
StringUtils.stripToNull(null) = null 
StringUtils.stripToNull("") = null 
StringUtils.stripToNull(" ") = null 
StringUtils.stripToNull(" \b \t \n \f \r ") = "\b" 
StringUtils.stripToNull(" \n\tss \b") = "ss \b" 
StringUtils.stripToNull(" d d dd ") = "d d dd" 
StringUtils.stripToNull("dd ") = "dd" 
StringUtils.stripToNull(" dd ") = "dd" 
10. public static String stripToEmpty(String str) 
去掉字符串两端的空白符(whitespace) ,如果变为 null 或"" ,则返回"" 
下面是示例(注意和 trimToEmpty() 的区别): 
StringUtils.stripToNull(null) = "" 
StringUtils.stripToNull("") = "" 
StringUtils.stripToNull(" ") = "" 
StringUtils.stripToNull(" \b \t \n \f \r ") = "\b" 
StringUtils.stripToNull(" \n\tss \b") = "ss \b" 
StringUtils.stripToNull(" d d dd ") = "d d dd" 
StringUtils.stripToNull("dd ") = "dd" 
StringUtils.stripToNull(" dd ") = "dd" 

以下方法只介绍其功能,不再举例: 
11. public static String strip(String str, String stripChars) 
去掉 str 两端的在 stripChars 中的字符。 
如果 str 为 null 或等于"" ,则返回它本身; 
如果 stripChars 为 null 或"" ,则返回 strip(String str) 。 
12. public static String stripStart(String str, String stripChars) 
和11相似,去掉 str 前端的在 stripChars 中的字符。 
13. public static String stripEnd(String str, String stripChars) 
和11相似,去掉 str 末端的在 stripChars 中的字符。 
14. public static String[] stripAll(String[] strs) 
对字符串数组中的每个字符串进行 strip(String str) ,然后返回。 
如果 strs 为 null 或 strs 长度为0,则返回 strs 本身 
15. public static String[] stripAll(String[] strs, String stripChars) 
对字符串数组中的每个字符串进行 strip(String str, String stripChars) ,然后返回。 
如果 strs 为 null 或 strs 长度为0,则返回 strs 本身 
16. public static boolean equals(String str1, String str2) 
比较两个字符串是否相等,如果两个均为空则也认为相等。 
17. public static boolean equalsIgnoreCase(String str1, String str2) 
比较两个字符串是否相等,不区分大小写,如果两个均为空则也认为相等。 
18. public static int indexOf(String str, char searchChar) 
返回字符 searchChar 在字符串 str 中第一次出现的位置。 
如果 searchChar 没有在 str 中出现则返回-1, 
如果 str 为 null 或 "" ,则也返回-1 
19. public static int indexOf(String str, char searchChar, int startPos) 
返回字符 searchChar 从 startPos 开始在字符串 str 中第一次出现的位置。 
如果从 startPos 开始 searchChar 没有在 str 中出现则返回-1, 
如果 str 为 null 或 "" ,则也返回-1 
20. public static int indexOf(String str, String searchStr) 
返回字符串 searchStr 在字符串 str 中第一次出现的位置。 
如果 str 为 null 或 searchStr 为 null 则返回-1, 
如果 searchStr 为 "" ,且 str 为不为 null ,则返回0, 
如果 searchStr 不在 str 中,则返回-1 
21. public static int ordinalIndexOf(String str, String searchStr, int ordinal) 
返回字符串 searchStr 在字符串 str 中第 ordinal 次出现的位置。 
如果 str=null 或 searchStr=null 或 ordinal<=0 则返回-1 
举例(*代表任意字符串): 
StringUtils.ordinalIndexOf(null, *, *) = -1 
StringUtils.ordinalIndexOf(*, null, *) = -1 
StringUtils.ordinalIndexOf("", "", *) = 0 
StringUtils.ordinalIndexOf("aabaabaa", "a", 1) = 0 
StringUtils.ordinalIndexOf("aabaabaa", "a", 2) = 1 
StringUtils.ordinalIndexOf("aabaabaa", "b", 1) = 2 
StringUtils.ordinalIndexOf("aabaabaa", "b", 2) = 5 
StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1 
StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4 
StringUtils.ordinalIndexOf("aabaabaa", "bc", 1) = -1 
StringUtils.ordinalIndexOf("aabaabaa", "", 1) = 0 
StringUtils.ordinalIndexOf("aabaabaa", "", 2) = 0 
22. public static int indexOf(String str, String searchStr, int startPos) 
返回字符串 searchStr 从 startPos 开始在字符串 str 中第一次出现的位置。 
举例(*代表任意字符串): 
StringUtils.indexOf(null, *, *) = -1 
StringUtils.indexOf(*, null, *) = -1 
StringUtils.indexOf("", "", 0) = 0 
StringUtils.indexOf("aabaabaa", "a", 0) = 0 
StringUtils.indexOf("aabaabaa", "b", 0) = 2 
StringUtils.indexOf("aabaabaa", "ab", 0) = 1 
StringUtils.indexOf("aabaabaa", "b", 3) = 5 
StringUtils.indexOf("aabaabaa", "b", 9) = -1 
StringUtils.indexOf("aabaabaa", "b", -1) = 2 
StringUtils.indexOf("aabaabaa", "", 2) = 2 
StringUtils.indexOf("abc", "", 9) = 3 
23. public static int lastIndexOf(String str, char searchChar) 
基本原理同18 
24. public static int lastIndexOf(String str, char searchChar, int startPos) 
基本原理同19 
25. public static int lastIndexOf(String str, String searchStr) 
基本原理同20 
26. public static int lastIndexOf(String str, String searchStr, int startPos) 
基本原理同22 
另附: 
String 的 split(String regex) 方法的用法 
如果我们需要把某个字符串拆分为字符串数组,则通常用 split(String regex) 来实现。 
例如: 
Java代码 复制代码 

1. String str = "aa,bb,cc,dd"; 
2. String[] strArray = str.split(","); 
3. System.out.println(strArray.length); 
4. for (int i = 0; i < strArray.length; i++) { 
5. System.out.println(strArray[i]); 
6. } 

String str = "aa,bb,cc,dd"; String[] strArray = str.split(","); System.out.println(strArray.length); for (int i = 0; i < strArray.length; i++) { System.out.println(strArray[i]); } 



结果为: 
4 
aa 
bb 
cc 
dd 
如果, 
String str = "aa.bb.cc.dd"; 
String[] strArray = str.split("."); 
则结果为:0 
为什么结果不是我们所想的呢,原因是参数 String regex 是正则表达式 (regular expression) 而不是普通字符串,而 "." 在正则表达式中有特殊含义,表示匹配所有单个字符。如果要那样拆分,我们必须给 "." 进行转义,String[] strArray = str.split(".") 修改为 String[] strArray = str.split("\\.") 即可。 



另外收藏几个实用的方法:

org.apache.commons.lang.StringUtils中提供许多有用的字符串操作方法,了解这些方法,我们可以避免许多不必要的重复工作。下面介绍其中比较有用的几个方法:

检查空字符串:

01.StringUtils.isBlank(String str);  
02.StringUtils.isNotBlank(String str);  




缩写字符串: 

String test  =   " This is a test of the abbreviation. "   
02.System.out.println( StringUtils.abbreviate( test,  10  ) );  
03.  
04.[Console输出]  
05.This is   






查找嵌套字符串: 01. String htmlContent  =   " <html>/n "   +   
02.                      "   <head>/n "   +   
03.                      "     <title>Test Page</title>/n "   +   
04.                      "   </head>/n "   +   
05.                      "   <body>/n "   +   
06.                      "     <p>This is a TEST!</p>/n "   +   
07.                      "   </body>/n "   +   
08.                      " </html> " ;  
09.  
10. //  Extract the title from this XHTML content     
11. String title  =  StringUtils.substringBetween(htmlContent,  " <title> " ,  " </title> " );  
12.System.out.println(  " Title:  "   +  title );  
13.  
14.[Console输出]  
15.Title: Test Page   



验证字符串:
01. String test1  =   " ORANGE " ;  
02.  
03.String test2  =   " ICE9 " ;  
04.  
05.String test3  =   " ICE CREAM " ;  
06.  
07.String test4  =   " 820B Judson Avenue " ;  
08.  
09.//判断字母   
10. boolean  t1val  =  StringUtils.isAlpha( test1 );  //  returns true    
11. //判断是不是字母数字   
12. boolean  t2val  =  StringUtils.isAlphanumeric( test2 );  //  returns true    
13. //字母空格   
14. boolean  t3val  =  StringUtils.isAlphaSpace( test3 );  //  returns true    
15. //字母数字空格   
16. boolean  t4val  =    
17.  
18.    StringUtils.isAlphanumericSpace( test4 );  //  returns true   


计算字符串出现频率: 
01. File manuscriptFile  =   new  File( " manuscript.txt " );  
02.  
03.Reader reader  =   new  FileReader( manuscriptFile );  
04.  
05.StringWriter stringWriter  =   new  StringWriter( );  
06.  
07. while ( reader.ready( ) )   { writer.write( reader.read( ) ); }   
08.   
09.String manuscript  =  stringWriter.toString( );  
10.  
11. //  Convert string to lowercase    
12.   
13.manuscript  =  StringUtils.lowerCase(manuscript);  
14.  
15. //  count the occurrences of "futility"    
16.   
17. int  numFutility  =  StringUtils.countMatches( manuscript,  " futility "  );   



比较不同字符串: 01. int  dist  =  StringUtils.getLevenshteinDistance(  " Word " ,  " World "  );  
02.  
03.String diff  =  StringUtils.difference(  " Word " ,  " World "  );  
04.  
05. int  index  =  StringUtils.indexOfDifference(  " Word " ,  " World "  );  
06.  
07.System.out.println(  " Edit Distance:  "   +  dist );  
08.  
09.System.out.println(  " Difference:  "   +  diff );  
10.  
11.System.out.println(  " Diff Index:  "   +  index );  
12.  
13.[Console输出]  
14.Edit Distance:  2   
15.   
16.Difference: ld  
17.  
18.Diff Index:  3   
jsonObject测试代码 java
http://json-lib.sourceforge.net/xref-test/net/sf/json/TestJSONObject.html
hibernate集合映射inverse和cascade详解 1、到底在哪用cascade="..."? java http://www.cnblogs.com/amboyna/archive/2008/02/18/1072260.html
hibernate集合映射inverse和cascade详解 1、到底在哪用cascade="..."?

cascade属性并不是多对多关系一定要用的,有了它只是让我们在插入或删除对像时更方便一些,只要在cascade的源头上插入或是删除,所有cascade的关系就会被自己动的插入或是删除。便是为了能正确的cascade,unsaved-value是个很重要的属性。Hibernate通过这个属性来判断一个对象应该save还是update,如果这个对象的id是unsaved-value的话,那说明这个对象不是persistence object要save(insert);如果id是非unsaved-value的话,那说明这个对象是persistence object(数据库中已存在),只要update就行了。saveOrUpdate方法用的也是这个机制。

2、到底在哪用inverse="ture"?

inverse属性默认是false的,就是说关系的两端都来维护关系。这个意思就是说,如有一个Student, Teacher和TeacherStudent表,Student和Teacher是多对多对多关系,这个关系由TeacherStudent这个表来表现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢?在用hibernate时,我们不会显示的对TeacherStudent表做操作。对TeacherStudent的操作是hibernate帮我们做的。hibernate就是看hbm文件中指定的是"谁"维护关系,那个在插入或删除"谁"时,就会处发对关系表的操作。前提是"谁"这个对象已经知道这个关系了,就是说关系另一头的对象已经set或是add到"谁"这个对象里来了。前面说过inverse默认是false,就是关系的两端都维护关系,对其中任一个操作都会处发对表系表的操作。当在关系的一头,如Student中的bag或set中用了inverse="true"时,那就代表关系是由另一关维护的(Teacher)。就是说当这插入Student时,不会操作TeacherStudent表,即使Student已经知道了关系。只有当Teacher插入或删除时才会处发对关系表的操作。所以,当关系的两头都用inverse="true"是不对的,就会导致任何操作都不处发对关系表的操作。当两端都是inverse="false"或是default值是,在代码对关系显示的维护也是不对的,会导致在关系表中插入两次关系。

在一对多关系中inverse就更有意义了。在多对多中,在哪端inverse="true"效果差不多(在效率上)。但是在一对多中,如果要一方维护关系,就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。而如果让"多"方面维护关系时就不会有update操作,因为关系就是在多方的对象中的,直指插入或是删除多方对象就行了。当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管怎样说,还是让"多"方维护关系更直观一些。

3、cascade和inverse有什么区别?

可以这样理解,cascade定义的是关系两端对象到对象的级联关系;而inverse定义的是关系和对象的级联关系。



all : 所有情况下均进行关联操作。 
none:所有情况下均不进行关联操作。这是默认值。 
save-update:在执行save/update/saveOrUpdate时进行关联操作。 
delete:在执行delete时进行关联操作。 
 

all的意思是save-update + delete 
all-delete-orphan 的意思是当对象图中产生孤儿节点时,在数据库中删除该节点 
all比较好理解,举个例子说一下all-delete-orphan: 
Category与Item是一对多的关系,也就是说Category类中有个Set类型的变量items. 
举个例子,现items中存两个Item, item1,item2,如果定义关系为all-delete-orphan 
当items中删除掉一个item(比如用remove()方法删除item1),那么被删除的Item类实例 
将变成孤儿节点,当执行category.update(),或session.flush()时 
hibernate同步缓存和数据库,会把数据库中item1对应的记录删掉 

//////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////

4. hibernate如何根据pojo来更新数据库

4.0  在commit/flush之前,hibernate不会对pojo对象作神秘的处理。
4.0.1 在select查询出pojo时,hibernate根据“字段--属性”的对应关系,用字段的值填充pojo的属性;
然后根据“关系标记”生成sql语句从relationTable中查询出满足条件的relationPojo,并把这些relatinPojo
放到“关系属性”中。这个过程是机械的。

4.0.2 在pojo对象被查出来后,到commit(或flush)之前,它将是一个普通的java对象,hibernate不会做额外的手脚。
比如,不会限制你设置一个属性的值为null或其它任何值
在集合类Set的add(object)操作时, 不会改变object的值,不会检查参数object是否是一个pojo对象
设置mainPojo的一个“桥属性”的值,不会自动设置relationPojo的对应的“桥属性”的值。
执行session.delete(pojo)时,pojo本身没有变化,他的属性值也没有变化。
执行session.save(pojo)时,如果pojo的id不是hibernate或数据库生成,则它的值没有变化。
  如果pojo的id是hibernate或数据库生成,则hibernate会把id给pojo设上去。

extend: 对lazy=true的set,hibernate在进行set的操作(调用java.util.Set中声明的方法)时
会先inialize这个set,仅此而已。而inialize仅仅是从数据库中捞出set的数据。 
如果一个set已经被inialize了,那么对它进行的操作就是java.util.Set接口中定义的语义。

另外,如果id由hibernate来生成,那么在save(pojo)时,hibernate会改变该pojo,会设置它的id,这
可能改变该pojo的hashCode,详细地讨论见帖《》

mapping文件中标记的某些属性及pojo对象的操作会对数据库操作产生影响,这些影响都是在commit时才会起作用。
而在commit前pojo的状态不受它们的影响。

不过,待commit之时,将由hibernate完全掌控,它好像知道pojo对象从创建到commit这中间的所有变化。


4.01. 关联更新
"关系标记"对应的属性是一个pojo或一个pojo的集合,修改“关系属性”的值能会导致更新mainTable表,也可能会更新relationTable表。

这种更新暂叫“关联更新”。


4.1.inverse属性的作用(假定没有设置cascade属性) 
4.1.1 “只有集合标记(set/map/list/array/bag)才有inverse属性”。
————不妨以标记set为例,具体为“一个地区(Address表)的学校(School表)” -- address.schoolSet。

4.1.2 “set的inverse属性决定是否把对set的改动反映到数据库中去。
inverse=false————反映;inverse=true————不反映”
inverse属性默认为false

对<one-to-many>和<many-to-many>子标记,这两条都适用。
不管是对set做什么操作,4.1.2都适用。

4.1.3当inverse=false时,hibernate如何将对set的改动反映到数据库中:

对set的操作主要有:(1)新增元素 address.getSchoolSet().add(oneSchool);
(2)删除元素 address.getSchoolSet().remove(oneSchool);
(3)删除set  address.setSchoolSet(null);
(4)设新set  address.setSchoolSet( newSchoolSet);
(5)转移set  otherSchoolSet = otherAddress.getSchoolSet();
  otherAddress.setSchoolSet(null);
  address.setSchoolSet(otherSchoolSet);
(6)改变set中元素的属性的值  如果是改变key属性,这会导致异常
  如果改变的是普通的属性,则hibernate认为set没有变化(在后面可以看出缘由)。
  所以这种情形不予考虑。
  
改变set后,hibernate对数据库的操作根据是<one-to-many>关系还是<many-to-many>关系而有不同。

对one-to-many,对school set的改动,会改变表SCHOOL中的数据:
  #SCHOOL_ID是school表的主键,SCHOOL_ADDRESS是school表中的地址栏位
  #表School的外键为SCHOOL_ADDRESS,它对应表Address的主键ADDRESS_ID
(11)insert oneSchool———— sqlInsertRowString: 
update SCHOOL set SCHOOL_ADDRESS=? where SCHOOL_ID=? 
(仅仅update foreign-key的值。)
(22)delete oneSchool———— sqlDeleteRowString: 
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ID=?
(很奇怪,把foreign-key设置为null不知道有什么实际意义?)
(33)delete 属于某一address的所有school ————sqlDeleteString:
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ADDRESS=?
(44)update ————sqlUpdateRowString:"", no need

对many-to-many,对school set的改动,会改变关系表ADDRESS_SCHOOL中的数据:
#“地区————学校”的关系为多对多的关系有点牵强,只是为了方便与上面的one-to-many作比较
#假设有一个关系表ADDRESS_SCHOOL,有两个字段ADDRESS_ID, SCHOOL_ID,
#这两个字段分别对应ADDRESS和SCHOOL两表的key
(11)insert的SQL语句为: insert into ADDRESS_SCHOOL(ADDRESS_ID, SCHOOL_ID) 
values(?,?)
(22)delete的SQL语句为: delete from ADDRESS_SCHOOL 
where ADDRESS_ID=? AND SCHOOL_ID=?
(33)delete all的SQL语句为: delete from ADDRESS_SCHOOL
where ADDRESS_ID=?
(44)update的sql语句为 ————sqlUpdateRowString:
update ADDRESS_SCHOOL set ADDRESS_ID=?
where ADDRESS_ID=? AND SCHOOL_ID=?

对set的操作(1),hibernate会执行(11)sqlInsertRowString
对set的操作(2),hibernate会执行(22)sqlDeleteRowString
对set的操作(3),hibernate会执行(33)sqlDeleteString
对set的操作(4),老的schoolSet因为没有所属的address,所以被全部delete掉,即先执行(33)sqlDeleteString
然后新增新的schoolSet,即再执行sqlInsertRowString
对set的操作(5),实际上就是将set从一个pojo转移到另一pojo:
首先,执行sqlDeleteString,删除掉otherAddress所属的school
然后,执行sqlDeleteString,删除掉address原先的school
最后,执行sqlInsertRowString,将otherSchoolSet新增给address

总结:(1)对one-to-many而言,改变set,会让hibernate执行一系列的update语句, 不会delete/insert数据
(2)对many-to-many而言,改变set,只修改关系表的数据,不会影响many-to-many的另一方。
(3)虽然one-to-many和many-to-many的数据库操作不一样,但目的都是一个:维护数据的一致性。执行的sql都
只涉及到“桥字段”,不会考虑或改变其他的字段,所以对set的操作(6)是没有效果地。
extend:对list,可能还会维护index字段。

4.1.4 “inverse与cascade没有什么关系,互无牵扯。”
commit后,这两个属性发挥作用的时机不同,hibernate会根据对pojo对象的改动,及cascade属性的设置,
生成一系列的Action,比如UpdateAction,DeleteAction,InsertAction等,每个Action都有execute方法以执行对应的sql语句。
待所有这些Action都生成好了后,hibernate再一起执行它们,在执行sql前,inverse属性起作用,
当inverse=true时,不执行sql;当inverse=false时,执行sql。

4.1.5 inverse的默认值为false,所以inverse属性默认会进行“关联更新”。

4.1.6 建议:只对set + many-to-many设置inverse=false,其他的标记不考虑inverse属性。
   糟糕的是,不设置inverse属性时,inverse默认为false。

4.2. 级联(cascade)属性的作用: 
4.2.1 只有“关系标记”才有cascade属性:many-to-one,one-to-one ,any, 
set(map, bag, idbag, list, array) + one-to-many(many-to-many)

4.2.2 级联指的是当主控方执行操作时,关联对象(被动方)是否同步执行同一操作。
pojo和它的关系属性的关系就是“主控方 -- 被动方”的关系,如果关系属性是一个set,那么被动方就是set中的一个一个元素,。
比如:学校(School)有三个属性:地区(Address),校长(TheMaster)和学生(Set, 元素为Student)
执行session.delete(school)时,级联决定是否执行session.delete(Address),session.delete(theMaster),
是否对每个aStudent执行session.delete(aStudent)。

extend:这点和inverse属性是有区别的。见4.3.

4.2.3 一个操作因级联cascade可能触发多个关联操作。前一个操作叫“主控操作”,后一个操作叫“关联操作”。
cascade属性的可选值:
all : 所有情况下均进行关联操作。
none:所有情况下均不进行关联操作。这是默认值。
save-update:在执行save/update/saveOrUpdate时进行关联操作。
delete:在执行delete时进行关联操作。 

具体执行什么“关联操作”是根据“主控操作”来的:
  “主控操作”         “关联操作”
session.saveOrUpdate --> session.saveOrUpdate (执行saveOrUpdate实际上会执行save或者update)
session.save ----> session.saveOrUpdate
session.udpate --> session.saveOrUpdate
session.delete --> session.delete

4.2.4 主控操作和关联操作的先后顺序是“先保存one,再保存many;先删除many,再删除one;先update主控方,再update被动方”
对于one-to-one,当其属性constrained="false"(默认值)时,它可看作one-to-many关系;
   当其属性constrained="true"时,它可看作many-to-one关系;
对many-to-many,它可看作one-to-many。

比如:学校(School)有三个属性:地区(Address),校长(TheMaster,其constrained="false")和学生(Set, 元素为Student) 
当执行session.save(school)时,
实际的执行顺序为:session.save(Address);
session.save(school);
session.save(theMaster);
for( 对每一个student ){
session.save(aStudent);
}

当执行session.delete(school)时,
实际的执行顺序为:session.delete(theMaster);
for( 对每一个student ){
session.delete(aStudent);
}
session.delete(school);
session.delete(Address);

当执行session.update(school)时,
实际的执行顺序为:session.update(school);
session.saveOrUpdate(Address);
session.saveOrUpdate(theMaster);
for( 对每一个student ){
session.saveOrUpdate(aStudent);
}
注意:update操作因级联引发的关联操作为saveOrUpdate操作,而不是update操作。
saveOrUpdate与update的区别是:前者根据操作对象是保存了还是没有保存,而决定执行update还是save

extends: 实际中,删除学校不会删除地区,即地区的cascade一般设为false
另外,many-to-many关系很少设置cascade=true,而是设置inverse=false。这个反映了cascade和inverse的区别。见4.3

4.2.6 cascade的默认值为false,所以inverse属性默认会进行“关联更新”。

4.2.7 总结:级联(cascade)就是操作一个对象时,对它的属性(其cascade=true)也进行这个操作。


4.3 inverse和cascade的比较
这两个属性本身互不影响,但起的作用有些类似,都能引发对关系表的更新。

4.3.1 inverse只对set+one-to-many(或many-to-many)有效,对many-to-one, one-to-one无效。
  cascade对关系标记都有效。
  
4.3.2 inverse对集合对象整体起作用,cascade对集合对象中的一个一个元素起作用,如果集合为空,那么cascade不会引发关联操作。
比如将集合对象置为null, school.setStudentSet(null)
inverse导致hibernate执行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=?
cascade则不会执行对STUDENT表的关联更新, 因为集合中没有元素。

再比新增一个school, session.save(school)
inverse导致hibernate执行:
for( 对(school的每一个student ){
udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //将学生的school_id改为新的school的id
}
cascade导致hibernate执行:
for( 对school的每一个student ){
session.save(aStudent); //对学生执行save操作
}

extends:如果改变集合中的部分元素(比如新增一个元素),
inverse: hibernate先判断哪些元素改变了,对改变的元素执行相应的sql
cascade: 它总是对集合中的每个元素执行关联操作。
(在关联操作中,hibernate会判断操作的对象是否改变)

4.3.2 两个起作用的时机不同:
cascade:在对主控方操作时,级联发生。
inverse: 在flush时(commit会自动执行flush),对session中的所有set,hibernate判断每个set是否有变化,
对有变化的set执行相应的sql,执行之前,会有个判断:if( inverse == true ) return;

可以看出cascade在先,inverse在后。

4.3.3 inverse 对set + one-to-many 和 set + many-to-many 起的作用不同。hibernate生成的sql不同。
  对one-to-many,hibernate对many方的数据库表执行update语句。
  对many-to-many, hibernate对关系表执行insert/update/delte语句,注意不是对many方的数据库表而是关系表。
  
  cascase 对set都是一致的,不管one-to-many还是many-to-many。都简单地把操作传递到set中的每个元素。所以它总是更新many
方的数据库表。

4.3.4 建议:只对set + many-to-many设置inverse=false,其他的标记不考虑inverse属性,都设为inverse=true。
   
   对cascade,一般对many-to-one,many-to-many,constrained=true的one-to-one 不设置级联删除。
hibernate3的cascade导致deleted object would be re-saved by cascade与 A collection with java http://blog.csdn.net/koyo25/article/details/5618515
hibernate3的cascade导致deleted object would be re-saved by cascade与 A collection with cascade="all-delete-orphan" was no longer ref 
分类: SSH架构 2010-05-23 20:16 507人阅读 评论(0) 收藏 举报 
我在处理数据库的级联关系时,老是碰到题目中的两个异常:deleted object would be re-saved by cascade和A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance。这两个问题,删除cascade这一项会解决一个问题但是也带来了另一个问题,删除cascade以后呢,就相反,总有问题。后来发现原来是hibernate3不支持cascade=“all-delete-orphan”的值,只要把set里面的cascade改成cascade="save-update,delete"就可以了

ThreadPoolExecutor运转机制详解 java ThreadPoolExecutor运转机制详解
最近发现几起对ThreadPoolExecutor的误用,其中包括自己,发现都是因为没有仔细看注释和内部运转机制,想当然的揣测参数导致,先看一下新建一个ThreadPoolExecutor的构建参数:

public ThreadPoolExecutor(int corePoolSize,   
                          int maximumPoolSize,   
                          long keepAliveTime,   
                          TimeUnit unit,   
                          BlockingQueue<Runnable> workQueue,   
                          ThreadFactory threadFactory,   
                          RejectedExecutionHandler handler)  

看这个参数很容易让人以为是线程池里保持corePoolSize个线程,如果不够用,就加线程入池直至maximumPoolSize大小,如果还不够就往workQueue里加,如果workQueue也不够就用RejectedExecutionHandler来做拒绝处理。

但实际情况不是这样,具体流程如下:

1)当池子大小小于corePoolSize就新建线程,并处理请求

2)当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理

3)当workQueue放不下新入的任务时,新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来做拒绝处理

4)另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁

内部结构如下所示:




从中可以发现ThreadPoolExecutor就是依靠BlockingQueue的阻塞机制来维持线程池,当池子里的线程无事可干的时候就通过workQueue.take()阻塞住。

其实可以通过Executes来学学几种特殊的ThreadPoolExecutor是如何构建的。

public static ExecutorService newFixedThreadPool(int nThreads) {   
    return new ThreadPoolExecutor(nThreads, nThreads,   
                                  0L, TimeUnit.MILLISECONDS,   
                                  new LinkedBlockingQueue<Runnable>());   
}  

newFixedThreadPool就是一个固定大小的ThreadPool

public static ExecutorService newCachedThreadPool() {   
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,   
                                  60L, TimeUnit.SECONDS,   
                                  new SynchronousQueue<Runnable>());   
}  

newCachedThreadPool比较适合没有固定大小并且比较快速就能完成的小任务,没必要维持一个Pool,这比直接new Thread来处理的好处是能在60秒内重用已创建的线程。

其他类型的ThreadPool看看构建参数再结合上面所说的特性就大致知道它的特性
Java简单实现Ping功能 java http://www.micmiu.com/lang/java/java-ping/
下面将给出上述的两种实现的详细过程:

一、直接调用服务器本身的ping命令

TestPingCmd.java


package michael.net;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @blog http://www.micmiu.com
 * @author Michael
 */
public class TestPingCmd {

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {

        // 读取txt文件中的IP列表
         TestPingCmd pinger = new TestPingCmd();
        List<String> iplist = pinger
                .getIpListFromTxt("d:/test/idc_ping_ip.txt");

        // List<String> iplist = new ArrayList<String>();
        // iplist.add("222.*.*.*");
        // iplist.add("222.*.*.*");
        // iplist.add("222.*.*.*");
        // iplist.add("222.*.*.*");
        // iplist.add("222.*.*.*");
        ThreadPoolExecutor executorPool = new ThreadPoolExecutor(50, 60, 60,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50),
                new ThreadPoolExecutor.CallerRunsPolicy());
        long startTime = System.currentTimeMillis();
        final int maxCount = 4;
        for (final String ip : iplist) {
            executorPool.execute(new Runnable() {
                public void run() {
                    TestPingCmd pinger = new TestPingCmd();
                    Integer countSucce = pinger.doPingCmd(ip, maxCount);
                    if (null != countSucce) {
                        System.out.println("host:[ " + ip + " ] ping cout: "
                                + maxCount + " success: " + countSucce);

                    } else {
                        System.out
                                .println("host:[ " + ip + " ] ping cout null");
                    }
                }

            });
        }
        while (executorPool.getActiveCount() > 0) {
            Thread.sleep(100);
        }
        System.out.println("complete ping jobs count = " + iplist.size()
                + " , total used time(ms) = "
                + (System.currentTimeMillis() - startTime));
        executorPool.shutdown();
    }

    /**
     * @param destIp
     * @param maxCount
     * @return
     */
    public Integer doPingCmd(String destIp, int maxCount) {
        LineNumberReader input = null;
        try {
            String osName = System.getProperties().getProperty("os.name");
            String pingCmd = null;
            if (osName.startsWith("Windows")) {
                pingCmd = "cmd /c ping -n {0} {1}";
                pingCmd = MessageFormat.format(pingCmd, maxCount, destIp);
            } else if (osName.startsWith("Linux")) {
                pingCmd = "ping -c {0} {1}";
                pingCmd = MessageFormat.format(pingCmd, maxCount, destIp);
            } else {
                System.out.println("not support OS");
                return null;
            }
            Process process = Runtime.getRuntime().exec(pingCmd);
            InputStreamReader ir = new InputStreamReader(process
                    .getInputStream());
            input = new LineNumberReader(ir);
            String line;
            List<String> reponse = new ArrayList<String>();

            while ((line = input.readLine()) != null) {
                if (!"".equals(line)) {
                    reponse.add(line);
                    // System.out.println("====:" + line);
                }
            }
            if (osName.startsWith("Windows")) {
                return parseWindowsMsg(reponse, maxCount);
            } else if (osName.startsWith("Linux")) {
                return parseLinuxMsg(reponse, maxCount);
            }

        } catch (IOException e) {
            System.out.println("IOException   " + e.getMessage());

        } finally {
            if (null != input) {
                try {
                    input.close();
                } catch (IOException ex) {
                    System.out.println("close error:" + ex.getMessage());

                }
            }
        }
        return null;
    }

    private int parseWindowsMsg(List<String> reponse, int total) {
        int countTrue = 0;
        int countFalse = 0;
        for (String str : reponse) {
            if (str.startsWith("来自") || str.startsWith("Reply from")) {
                countTrue++;
            }
            if (str.startsWith("请求超时") || str.startsWith("Request timed out")) {
                countFalse++;
            }
        }
        return countTrue;
    }

    private int parseLinuxMsg(List<String> reponse, int total) {
        int countTrue = 0;
        for (String str : reponse) {
            if (str.contains("bytes from") && str.contains("icmp_seq=")) {
                countTrue++;
            }
        }
        return countTrue;
    }

    /**
     * @param filepath
     * @return list
     */
    public List<String> getIpListFromTxt(String filepath) {
        BufferedReader br = null;
        List<String> iplist = new ArrayList<String>();
        try {
            File file = new File(filepath);
            br = new BufferedReader(new FileReader(file));
            while (br.ready()) {
                String line = br.readLine();
                if (null != line && !"".equals(line)) {
                    iplist.add(line);
                }
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);

        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (Exception ex) {
                    ex.printStackTrace(System.out);
                }
            }
        }
        return iplist;
    }

}
二、通过指定服务器去ping测试

主要思路:利用Mina在指定的第三方服务器上运行server端,然后实现客户端和 第三方 服务器建立socket连接,发送ping任务的消息给第三方服务器,第三方服务器再把执行结果实时反馈给客户端。

代码包括四个类:

服务端:PingServerIoHandler.java PingServer.java 
客户端:PingClientIoHandler.java PingClient.java 


package michael.mina.ping;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.text.MessageFormat;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

/**
 * @blog http://www.micmiu.com
 * @author Michael
 */
public class PingServerIoHandler extends IoHandlerAdapter {
    private String logId = "SERVER:: ";
    private int msgCount = 0;

    @Override
    public void exceptionCaught(IoSession pSession, Throwable pCause)
            throws Exception {
        System.out.println(logId + "发生异常:" + pCause.getLocalizedMessage());
    }

    @Override
    public void messageReceived(IoSession pSession, Object pMessage)
            throws Exception {
        String msg = String.valueOf(pMessage);
        msgCount++;
        System.out.println(logId + "收到客户端第 " + msgCount + " 条消息:" + msg);
        pSession.write(msgCount);

        if (msg.startsWith("ping")) {
            String destIp = msg.split(" ")[1];
            doPingCmd(pSession, destIp);
        }

    }

    @Override
    public void messageSent(IoSession pSession, Object pMessage)
            throws Exception {
        System.out.println(logId + "发出消息:" + pMessage);
    }

    @Override
    public void sessionClosed(IoSession pSession) throws Exception {
        System.out.println(logId + "one client closed ");
    }

    @Override
    public void sessionCreated(IoSession pSession) throws Exception {
        System.out.println(logId + "sessionCreated ");
    }

    @Override
    public void sessionIdle(IoSession pSession, IdleStatus pStatus)
            throws Exception {
        super.sessionIdle(pSession, pStatus);
    }

    @Override
    public void sessionOpened(IoSession pSession) throws Exception {
        System.out.println(logId + "sessionOpened ");
    }

    private Integer doPingCmd(IoSession pSession, String destIp) {
        LineNumberReader input = null;
        int maxCount = 4;
        try {
            String osName = System.getProperties().getProperty("os.name");
            String pingCmd = null;
            if (osName.startsWith("Windows")) {
                pingCmd = "cmd /c ping -n {0} {1}";
                pingCmd = MessageFormat.format(pingCmd, maxCount, destIp);
            } else if (osName.startsWith("Linux")) {
                pingCmd = "ping -c {0} {1}";
                pingCmd = MessageFormat.format(pingCmd, maxCount, destIp);
            } else {
                System.out.println("not support OS");
                return null;
            }
            Process process = Runtime.getRuntime().exec(pingCmd);
            InputStreamReader ir = new InputStreamReader(process
                    .getInputStream());
            input = new LineNumberReader(ir);
            String line;

            while ((line = input.readLine()) != null) {
                if (!"".equals(line)) {
                    pSession.write(line);
                }
            }
        } catch (IOException e) {
            System.out.println("IOException   " + e.getMessage());

        } finally {
            if (null != input) {
                try {
                    input.close();
                } catch (IOException ex) {
                    System.out.println("close error:" + ex.getMessage());

                }
            }
        }
        return null;
    }
}
图片压缩 java
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
 * 图片处理工具类:<br>
 * 功能:缩放图像、切割图像、图像类型转换、彩色转黑白、文字水印、图片水印等
 * @author Administrator
 */
public class ImageUtils {
    /**
     * 几种常见的图片格式
     */
    public static String IMAGE_TYPE_GIF = "gif";// 图形交换格式
    public static String IMAGE_TYPE_JPG = "jpg";// 联合照片专家组
    public static String IMAGE_TYPE_JPEG = "jpeg";// 联合照片专家组
    public static String IMAGE_TYPE_BMP = "bmp";// 英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式
    public static String IMAGE_TYPE_PNG = "png";// 可移植网络图形
    public static String IMAGE_TYPE_PSD = "psd";// Photoshop的专用格式Photoshop
    /**
     * 程序入口:用于测试
     * @param args
     */
    public static void main(String[] args) {
        // 1-缩放图像:
        // 方法一:按比例缩放
        ImageUtils.scale("e:/abc.jpg", "e:/abc_scale.jpg", 2, true);//测试OK
        // 方法二:按高度和宽度缩放
        ImageUtils.scale2("e:/abc.jpg", "e:/abc_scale2.jpg", 500, 300, true);//测试OK
        // 2-切割图像:
        // 方法一:按指定起点坐标和宽高切割
        ImageUtils.cut("e:/abc.jpg", "e:/abc_cut.jpg", 0, 0, 400, 400 );//测试OK
        // 方法二:指定切片的行数和列数
        ImageUtils.cut2("e:/abc.jpg", "e:/", 2, 2 );//测试OK
        // 方法三:指定切片的宽度和高度
        ImageUtils.cut3("e:/abc.jpg", "e:/", 300, 300 );//测试OK
        // 3-图像类型转换:
        ImageUtils.convert("e:/abc.jpg", "GIF", "e:/abc_convert.gif");//测试OK
        // 4-彩色转黑白:
        ImageUtils.gray("e:/abc.jpg", "e:/abc_gray.jpg");//测试OK
        // 5-给图片添加文字水印:
        // 方法一:
        ImageUtils.pressText("我是水印文字","e:/abc.jpg","e:/abc_pressText.jpg","宋体",Font.BOLD,Color.white,80, 0, 0, 0.5f);//测试OK
        // 方法二:
        ImageUtils.pressText2("我也是水印文字", "e:/abc.jpg","e:/abc_pressText2.jpg", "黑体", 36, Color.white, 80, 0, 0, 0.5f);//测试OK
        
        // 6-给图片添加图片水印:
        ImageUtils.pressImage("e:/abc2.jpg", "e:/abc.jpg","e:/abc_pressImage.jpg", 0, 0, 0.5f);//测试OK
    }
    /**
     * 缩放图像(按比例缩放)
     * @param srcImageFile 源图像文件地址
     * @param result 缩放后的图像地址
     * @param scale 缩放比例
     * @param flag 缩放选择:true 放大; false 缩小;
     */
    public final static void scale(String srcImageFile, String result,
            int scale, boolean flag) {
        try {
            BufferedImage src = ImageIO.read(new File(srcImageFile)); // 读入文件
            int width = src.getWidth(); // 得到源图宽
            int height = src.getHeight(); // 得到源图长
            if (flag) {// 放大
                width = width * scale;
                height = height * scale;
            } else {// 缩小
                width = width / scale;
                height = height / scale;
            }
            Image image = src.getScaledInstance(width, height,
                    Image.SCALE_DEFAULT);
            BufferedImage tag = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics g = tag.getGraphics();
            g.drawImage(image, 0, 0, null); // 绘制缩小后的图
            g.dispose();
            ImageIO.write(tag, "JPEG", new File(result));// 输出到文件流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 缩放图像(按高度和宽度缩放)
     * @param srcImageFile 源图像文件地址
     * @param result 缩放后的图像地址
     * @param height 缩放后的高度
     * @param width 缩放后的宽度
     * @param bb 比例不对时是否需要补白:true为补白; false为不补白;
     */
    public final static void scale2(String srcImageFile, String result, int height, int width, boolean bb) {
        try {
            double ratio = 0.0; // 缩放比例
            File f = new File(srcImageFile);
            BufferedImage bi = ImageIO.read(f);
            Image itemp = bi.getScaledInstance(width, height, bi.SCALE_SMOOTH);
            // 计算比例
            if ((bi.getHeight() > height) || (bi.getWidth() > width)) {
                if (bi.getHeight() > bi.getWidth()) {
                    ratio = (new Integer(height)).doubleValue()
                            / bi.getHeight();
                } else {
                    ratio = (new Integer(width)).doubleValue() / bi.getWidth();
                }
                AffineTransformOp op = new AffineTransformOp(AffineTransform
                        .getScaleInstance(ratio, ratio), null);
                itemp = op.filter(bi, null);
            }
            if (bb) {//补白
                BufferedImage image = new BufferedImage(width, height,
                        BufferedImage.TYPE_INT_RGB);
                Graphics2D g = image.createGraphics();
                g.setColor(Color.white);
                g.fillRect(0, 0, width, height);
                if (width == itemp.getWidth(null))
                    g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2,
                            itemp.getWidth(null), itemp.getHeight(null),
                            Color.white, null);
                else
                    g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0,
                            itemp.getWidth(null), itemp.getHeight(null),
                            Color.white, null);
                g.dispose();
                itemp = image;
            }
            ImageIO.write((BufferedImage) itemp, "JPEG", new File(result));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 图像切割(按指定起点坐标和宽高切割)
     * @param srcImageFile 源图像地址
     * @param result 切片后的图像地址
     * @param x 目标切片起点坐标X
     * @param y 目标切片起点坐标Y
     * @param width 目标切片宽度
     * @param height 目标切片高度
     */
    public final static void cut(String srcImageFile, String result,
            int x, int y, int width, int height) {
        try {
            // 读取源图像
            BufferedImage bi = ImageIO.read(new File(srcImageFile));
            int srcWidth = bi.getHeight(); // 源图宽度
            int srcHeight = bi.getWidth(); // 源图高度
            if (srcWidth > 0 && srcHeight > 0) {
                Image image = bi.getScaledInstance(srcWidth, srcHeight,
                        Image.SCALE_DEFAULT);
                // 四个参数分别为图像起点坐标和宽高
                // 即: CropImageFilter(int x,int y,int width,int height)
                ImageFilter cropFilter = new CropImageFilter(x, y, width, height);
                Image img = Toolkit.getDefaultToolkit().createImage(
                        new FilteredImageSource(image.getSource(),
                                cropFilter));
                BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                Graphics g = tag.getGraphics();
                g.drawImage(img, 0, 0, width, height, null); // 绘制切割后的图
                g.dispose();
                // 输出为文件
                ImageIO.write(tag, "JPEG", new File(result));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 图像切割(指定切片的行数和列数)
     * @param srcImageFile 源图像地址
     * @param descDir 切片目标文件夹
     * @param rows 目标切片行数。默认2,必须是范围 [1, 20] 之内
     * @param cols 目标切片列数。默认2,必须是范围 [1, 20] 之内
     */
    public final static void cut2(String srcImageFile, String descDir,
            int rows, int cols) {
        try {
            if(rows<=0||rows>20) rows = 2; // 切片行数
            if(cols<=0||cols>20) cols = 2; // 切片列数
            // 读取源图像
            BufferedImage bi = ImageIO.read(new File(srcImageFile));
            int srcWidth = bi.getHeight(); // 源图宽度
            int srcHeight = bi.getWidth(); // 源图高度
            if (srcWidth > 0 && srcHeight > 0) {
                Image img;
                ImageFilter cropFilter;
                Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
                int destWidth = srcWidth; // 每张切片的宽度
                int destHeight = srcHeight; // 每张切片的高度
                // 计算切片的宽度和高度
                if (srcWidth % cols == 0) {
                    destWidth = srcWidth / cols;
                } else {
                    destWidth = (int) Math.floor(srcWidth / cols) + 1;
                }
                if (srcHeight % rows == 0) {
                    destHeight = srcHeight / rows;
                } else {
                    destHeight = (int) Math.floor(srcWidth / rows) + 1;
                }
                // 循环建立切片
                // 改进的想法:是否可用多线程加快切割速度
                for (int i = 0; i < rows; i++) {
                    for (int j = 0; j < cols; j++) {
                        // 四个参数分别为图像起点坐标和宽高
                        // 即: CropImageFilter(int x,int y,int width,int height)
                        cropFilter = new CropImageFilter(j * destWidth, i * destHeight,
                                destWidth, destHeight);
                        img = Toolkit.getDefaultToolkit().createImage(
                                new FilteredImageSource(image.getSource(),
                                        cropFilter));
                        BufferedImage tag = new BufferedImage(destWidth,
                                destHeight, BufferedImage.TYPE_INT_RGB);
                        Graphics g = tag.getGraphics();
                        g.drawImage(img, 0, 0, null); // 绘制缩小后的图
                        g.dispose();
                        // 输出为文件
                        ImageIO.write(tag, "JPEG", new File(descDir
                                + "_r" + i + "_c" + j + ".jpg"));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 图像切割(指定切片的宽度和高度)
     * @param srcImageFile 源图像地址
     * @param descDir 切片目标文件夹
     * @param destWidth 目标切片宽度。默认200
     * @param destHeight 目标切片高度。默认150
     */
    public final static void cut3(String srcImageFile, String descDir,
            int destWidth, int destHeight) {
        try {
            if(destWidth<=0) destWidth = 200; // 切片宽度
            if(destHeight<=0) destHeight = 150; // 切片高度
            // 读取源图像
            BufferedImage bi = ImageIO.read(new File(srcImageFile));
            int srcWidth = bi.getHeight(); // 源图宽度
            int srcHeight = bi.getWidth(); // 源图高度
            if (srcWidth > destWidth && srcHeight > destHeight) {
                Image img;
                ImageFilter cropFilter;
                Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
                int cols = 0; // 切片横向数量
                int rows = 0; // 切片纵向数量
                // 计算切片的横向和纵向数量
                if (srcWidth % destWidth == 0) {
                    cols = srcWidth / destWidth;
                } else {
                    cols = (int) Math.floor(srcWidth / destWidth) + 1;
                }
                if (srcHeight % destHeight == 0) {
                    rows = srcHeight / destHeight;
                } else {
                    rows = (int) Math.floor(srcHeight / destHeight) + 1;
                }
                // 循环建立切片
                // 改进的想法:是否可用多线程加快切割速度
                for (int i = 0; i < rows; i++) {
                    for (int j = 0; j < cols; j++) {
                        // 四个参数分别为图像起点坐标和宽高
                        // 即: CropImageFilter(int x,int y,int width,int height)
                        cropFilter = new CropImageFilter(j * destWidth, i * destHeight,
                                destWidth, destHeight);
                        img = Toolkit.getDefaultToolkit().createImage(
                                new FilteredImageSource(image.getSource(),
                                        cropFilter));
                        BufferedImage tag = new BufferedImage(destWidth,
                                destHeight, BufferedImage.TYPE_INT_RGB);
                        Graphics g = tag.getGraphics();
                        g.drawImage(img, 0, 0, null); // 绘制缩小后的图
                        g.dispose();
                        // 输出为文件
                        ImageIO.write(tag, "JPEG", new File(descDir
                                + "_r" + i + "_c" + j + ".jpg"));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 图像类型转换:GIF->JPG、GIF->PNG、PNG->JPG、PNG->GIF(X)、BMP->PNG
     * @param srcImageFile 源图像地址
     * @param formatName 包含格式非正式名称的 String:如JPG、JPEG、GIF等
     * @param destImageFile 目标图像地址
     */
    public final static void convert(String srcImageFile, String formatName, String destImageFile) {
        try {
            File f = new File(srcImageFile);
            f.canRead();
            f.canWrite();
            BufferedImage src = ImageIO.read(f);
            ImageIO.write(src, formatName, new File(destImageFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 彩色转为黑白 
     * @param srcImageFile 源图像地址
     * @param destImageFile 目标图像地址
     */
    public final static void gray(String srcImageFile, String destImageFile) {
        try {
            BufferedImage src = ImageIO.read(new File(srcImageFile));
            ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
            ColorConvertOp op = new ColorConvertOp(cs, null);
            src = op.filter(src, null);
            ImageIO.write(src, "JPEG", new File(destImageFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 给图片添加文字水印
     * @param pressText 水印文字
     * @param srcImageFile 源图像地址
     * @param destImageFile 目标图像地址
     * @param fontName 水印的字体名称
     * @param fontStyle 水印的字体样式
     * @param color 水印的字体颜色
     * @param fontSize 水印的字体大小
     * @param x 修正值
     * @param y 修正值
     * @param alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
     */
    public final static void pressText(String pressText,
            String srcImageFile, String destImageFile, String fontName,
            int fontStyle, Color color, int fontSize,int x,
            int y, float alpha) {
        try {
            File img = new File(srcImageFile);
            Image src = ImageIO.read(img);
            int width = src.getWidth(null);
            int height = src.getHeight(null);
            BufferedImage image = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, width, height, null);
            g.setColor(color);
            g.setFont(new Font(fontName, fontStyle, fontSize));
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                    alpha));
            // 在指定坐标绘制水印文字
            g.drawString(pressText, (width - (getLength(pressText) * fontSize))
                    / 2 + x, (height - fontSize) / 2 + y);
            g.dispose();
            ImageIO.write((BufferedImage) image, "JPEG", new File(destImageFile));// 输出到文件流
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 给图片添加文字水印
     * @param pressText 水印文字
     * @param srcImageFile 源图像地址
     * @param destImageFile 目标图像地址
     * @param fontName 字体名称
     * @param fontStyle 字体样式
     * @param color 字体颜色
     * @param fontSize 字体大小
     * @param x 修正值
     * @param y 修正值
     * @param alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
     */
    public final static void pressText2(String pressText, String srcImageFile,String destImageFile,
            String fontName, int fontStyle, Color color, int fontSize, int x,
            int y, float alpha) {
        try {
            File img = new File(srcImageFile);
            Image src = ImageIO.read(img);
            int width = src.getWidth(null);
            int height = src.getHeight(null);
            BufferedImage image = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, width, height, null);
            g.setColor(color);
            g.setFont(new Font(fontName, fontStyle, fontSize));
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                    alpha));
            // 在指定坐标绘制水印文字
            g.drawString(pressText, (width - (getLength(pressText) * fontSize))
                    / 2 + x, (height - fontSize) / 2 + y);
            g.dispose();
            ImageIO.write((BufferedImage) image, "JPEG", new File(destImageFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 给图片添加图片水印
     * @param pressImg 水印图片
     * @param srcImageFile 源图像地址
     * @param destImageFile 目标图像地址
     * @param x 修正值。 默认在中间
     * @param y 修正值。 默认在中间
     * @param alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
     */
    public final static void pressImage(String pressImg, String srcImageFile,String destImageFile,
            int x, int y, float alpha) {
        try {
            File img = new File(srcImageFile);
            Image src = ImageIO.read(img);
            int wideth = src.getWidth(null);
            int height = src.getHeight(null);
            BufferedImage image = new BufferedImage(wideth, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, wideth, height, null);
            // 水印文件
            Image src_biao = ImageIO.read(new File(pressImg));
            int wideth_biao = src_biao.getWidth(null);
            int height_biao = src_biao.getHeight(null);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                    alpha));
            g.drawImage(src_biao, (wideth - wideth_biao) / 2,
                    (height - height_biao) / 2, wideth_biao, height_biao, null);
            // 水印文件结束
            g.dispose();
            ImageIO.write((BufferedImage) image,  "JPEG", new File(destImageFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 计算text的长度(一个中文算两个字符)
     * @param text
     * @return
     */
    public final static int getLength(String text) {
        int length = 0;
        for (int i = 0; i < text.length(); i++) {
            if (new String(text.charAt(i) + "").getBytes().length > 1) {
                length += 2;
            } else {
                length += 1;
            }
        }
        return length / 2;
    }
}
Struts2的国际化 java http://www.blogjava.net/toby/archive/2009/03/19/260918.html
Struts2的国际化 
资源文件的命名:basename_language_country.properties

JAVA国际化

如果系统同时存在资源文件、类文件,系统将以类文件为主,而不会调用资源文件。

对于简体中文的Locale,ResourceBundle搜索资源的顺序是:

(1)baseName_zh_CN.class

(2)baseName_zh_CN.properties

(3)baseName_zh.class

(4)baseName_zh.properties

(5)baseName.class

(6)baseName.properties

 

Struts2的国际化入门

Struts2国际化是建立在Java国际化的基础上的,一样是通过提供不同国家/语言环境的消息资源,然后通过ResourceBundle

加载指定Locale对应的资源文件,再取得该资源文件中指定key对应的消息--整个过程与JAVA程序的国家化完全相同,只是

Struts2框架对JAVA程序国际化进行了进一步封装,从而简化了应用程序的国际化。

Struts2需要国际化的部分

类型转换:

数据校验:

验证框架xml配置文件的国际化:RegisterAction-validation.xml文件<message key="username.xml.invalid"/>

JSP页面的国际化:<s:text name="addUser"/>

Action的国际化:利用ActionSupport类提供的getText()方法.

Struts2中加载全局资源文件

struts.xml

<constant name="struts.custom.i18n.resources" value="baseName"/>

或

struts.properties

struts.custom.i18n.resources=baseName

访问国际化消息

Struts2访问国际化消息主要有如下三种方式:

(1)JSP页面:<s:text name="key"/>

(2)Action类中:使用ActionSupport类的getText方法。

(3)表单元素的Label里:为表单元素指定一个key属性

输出带占位符的国际化消息

Struts2中提供了如下两种方式来填充消息字符串中的占位符

(1)JSP页面,在<s:text.../>标签中使用多个<s:param.../>标签来填充消息中的占位符。

(2)Action中,在调用getText方法时使用getText(String aTextName,List args)或getText(String key, String[] args)方法来填充占位符。

除此之外,Struts2还提供了对占位符的一种替代方式,这种方式允许在国际化消息资源文件中使用表达式,对于这种方式,则可避免在使用国际化消息时还需要为占位符传入参数值。

如下在消息资源中使用表达式

succTip=${username}, 欢迎, 您已经登录!

在上面的消息资源中,通过使用表达式,可以从ValueStack中取出该username属性值,自动填充到该消息资源中。

加载资源文件的方式

(1)加载全局资源文件: <constant name="struts.custom.i18n.resources" value="baseName"/>

(2)包范围资源文件 :为Struts2指定包范围资源文件的方法是,在包的根路径下建立多个文件名为package_language_country.properties的文件,一旦建立了

    这个系列的国际化资源文件,应用中处于该包下的所有Action都可以访问该资源文件。需要注意的是上面的包范围资源文件的baseName就是package,

 不是Action所在的包名。

(3)Action范围资源文件:在Action类文件所在的路径建立多个文件名为ActionName_language_country.properties的文件。

(4)临时指定资源文件:<s:i18n.../>标签的name属性指定临时的国际化资源文件

加载资源文件的顺序

Action中加载资源文件,假设我们在某个ChildAction中调用了getText("user.title"),Struts 2.0的将会执行以下的操作:

(1)优先加载系统中保存在ChildAction的类文件相同位置,且baseName为ChildAction的系列资源文件。

(2)如果在(1)中找不到指定key对应的消息,且ChildAction有父类ParentAction,则加载系统中保存在ParentAction的类文件相同位置,且

   baseName为ParentAction的系列资源文件。

(3)如果在(2)中找不到指定key对应的消息,且ChildAction有实现接口IChildAction,则加载系统中保存在IChildAction的类文件相同位置,且

   baseName为IChildAction的系列资源文件。

(4)如果在(3)中找不到指定key对应的消息,且ChildAction有实现接口ModelDriven(即使用模型驱动模式),则对于getModel()方法返回的model对象,

   重新执行第(1)步操作。

(5)如果在(4)中找不到指定key对应的消息,则查找当前包下baseName为package的系列资源文件。

(6)如果在(5)中找不到指定key对应的消息,则沿着当前包上溯,直到最顶层包来查找baseName为package的系列资源文件。

(7)如果在(6)中找不到指定key对应的消息,则查找struts.custom.i18n.resources常量指定baseName的系列资源文件。

(8)如果经过上面的步骤一直找不到key对应的消息,将直接输出该key的字符串值。

对于在JSP中访问国际化消息,则简单的多,他们又可以分为两种形式:

(1)对于使用<s:i18n.../>标签作为父标签的<s:text.../>标签、表单标签的形式:

   a、将从<s:i18n.../>标签指定的国际化资源文件中加载指定key对应的消息。

   b、如果在a中找不到指定key对应的消息,则查找struts.custom.i18n.resources常量指定baseName的系列资源文件。

   c、如果经过上面步骤一直找不到该key对应的消息,将直接输出该key的字符串值。

(2)如果<s:text.../>标签、表单标签没有使用<s:i18n.../>标签作为父标签:

    直接加载struts.custom.i18n.resources常量指定baseName的系列资源文件。如果找不到该key对应的消息,将直接输出该key的字符串值。

 

允许用户自行选择程序语言

Struts2国际化的运行机制

    在Struts2中,可以通过ActionContext.getContext().setLocale(Locale arg)设置用户的默认语言。

为了简化设置用户默认语言环境,Struts2提供了一个名为i18n的拦截器(Interceptor),并且将其注册在默认的拦截器中(defaultStack)。

    i18n拦截器在执行Action方法前,自动查找请求中一个名为request_locale的参数。如果该参数存在,拦截器就将其作为参数,转换成Locale对象,

并将其设为用户默认的Locale(代表国家/语言环境)。

 除此之外,i18n拦截器还会将上面生成的Locale对象保存在用户Session的名为WW_TRANS_I18N_LOCALE的属性中。一旦用户Session中存在一个名为

WW_TRANS_I18N_LOCALE的属性,则该属性指定的Locale将会作为浏览者的默认Locale。

<%@ page language="java" contentType="text/html; charset=GBK"%>

<%@taglib prefix="s" uri="/struts-tags"%>

<script. type="text/javascript">

function langSelecter_onChanged()

{

 document.getElementById("langForm").submit();

}

</script>

<%-- 设置SESSION_LOCALE为用户session中的WW_TRANS_I18N_LOCALE属性值 --%>

<s:set name="SESSION_LOCALE" value="#session['WW_TRANS_I18N_LOCALE']"/>

<%-- 使用lee.Locales创建locales实例 --%>

<s:bean id="locales" name="lee.Locales">

 <%-- 为locales实例传入current参数值,如果SESSION_LOCALE为空,则返回ValueStack中locale属性值(即用户浏览器设置的Locale) --%>

 <s:param name="current" value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"/>

</s:bean>

<%-- 让用户选择语言的表单 --%>

<form. action="<s:url/>" id="langForm" 

    style="background-color:#bbbbbb; padding-top: 4px; padding-bottom: 4px;">

    <s:text name="languag"/>

 <s:select label="Language" list="#locales.locales" listKey="value" listValue="key"

        value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"

        name="request_locale" id="langSelecter" 

        nchange="langSelecter_onChanged()" theme="simple"/>

</form>

在其他页面中包含该页面:

<s:include value="selectlanguage.jsp"/>

在struts.xml文件中增加Action通配符的配置:

<?xml version="1.0" encoding="GBK"?>

<!DOCTYPE struts PUBLIC

        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

 <constant name="struts.custom.i18n.resources" value="messageResource"/>

 <constant name="struts.i18n.encoding" value="GBK"/>

    <package name="lee" extends="struts-default">

  <!-- 使用通配符定义Action的name -->

  <action name="*">

   <!-- 将请求转发给/WEB-INF/jsp/路径下同名的JSP页面 -->

   <result>/WEB-INF/jsp/{1}.jsp</result>

  </action>

    </package>

</struts>

struts2国际化 java http://www.cnblogs.com/langlang/archive/2010/01/14/1647627.html
struts2国际化 在struts2中需要做国际化的有:

jsp页面的国际化,action错误信息的国际化,转换错误信息的国际化,校验错误信息的国际化

在之前的例子中已经做过和国际化相关的例子了,在struts.xml中配置过

 


view plaincopy to clipboardprint?
<constant name="struts.custom.i18n.resources" value="message"></constant>    

 

 

其中的message就是国际化资源文件的baseNmae。

我们先看看简单的在jsp中进行国际化

在src目录下新建message_en_US.properties,内容为

hello=add user

新建message_zh_CN.properties文件,内容为

hello=\u589e\u52a0\u7528\u6237

然后修改register2.jsp

要想使用国际化显示,可以将信息添加到<s:text></s:text> 标签中,也可以放在<s:i18n></s:i18n> 中,

在这里,先使用标签 <s:text></s:text>

增加以下内容:

 


view plaincopy to clipboardprint?
<s:text name="hello"></s:text>    

 

 

重启服务器后,浏览该页,默认会显示出“增加用户”,可以在IE中打开Internet选项,在常规中选择语言,增加英语(美国)[en-US],然后设置为第一项,刷新就可以看到输出“add user”。


 

action错误的国际化

在message_en_US.properties中增加以下内容

username.invalid=username invalid...

在message_zh_CN.properties中增加以下内容

username.invalid=\u7528\u6237\u540d\u4e0d\u5408\u6cd5...

修改RegisterAction中的validate方法,将错误加到ActionError中,在这里将使用到ActionSupport中的getText方法获得和国际化资源文件相关的信息。

以username验证为例:

 


view plaincopy to clipboardprint?
if (null == username || username.length() < 5 || username.length() > 10) {    
   
    this.addActionError(this.getText("username.invalid"));    
   
}    


这样就从资源文件中读取username.invalid的值,增加到ActionError中。 
 

查看该页面不输入任何数据,提交后就可以看到显示效果了。


 

验证框架的国际化(field级别错误)

在message_en_US.properties文件中增加以下内容

username.xml.invalid=validate information

在message_zh_CN.properties文件中增加以下内容

username.xml.invalid=\u9a8c\u8bc1\u6846\u67b6\u4fe1\u606f

然后修改验证框架,需要将在properties文件中的内容增加到框架中。

以username为例 


 


view plaincopy to clipboardprint?
<field name="username">    
    <field-validator type="requiredstring">    
        <param name="trim">true</param>    
        <message key="username.xml.invalid"></message>    
    </field-validator>    
</field>    


 

在message标签中增加属性key,值为properties文件中的key


标签中key大多是和国际化相关的


 

国际化资源文件的分类

当应用程序很大时,需要国际化的东西会很多,因此需要将国际化资源文件进行分类。

需要知道的是在src中的properties文件是全局资源文件,另外还可以分为包级别的和类级别的

首先看看包级别的

命名规则为package_language_country.properties

新建package_en_US.properties,内容为

username.xml.invalid=package validate information

新建package_zh_CN.properties,内容为

username.xml.invalid=\u5305\u9a8c\u8bc1\u4fe1\u606f

可以看到输出的信息为“包验证信息”,由此可见包级别的国际化资源文件的优先级高于全局国际化资源文件。


 

类级别

新建RegisterAction_en_US.properties,内容为

username.xml.invalid=class validate information

新建RegisterAction_zh_CN.properties,内容为

username.xml.invalid=\u7c7b\u9a8c\u8bc1\u4fe1\u606f

此时可以看到输出的信息为“类验证信息”。

由此可以得到国际化资源文件的优先级


全局<包级别<类级别

另外要进行表单的国际化时,要去掉theme="simple"

在RegisterAction_en_US.properties中增加

username.name=username

在RegisterAction_zh_CN.properties中增加

username.name=\u7528\u6237\u540d

修改表单标签

 


view plaincopy to clipboardprint?
<s:textfield name="username" key="username.name"></s:textfield>    

 

 

注意到key一般是和国际化相关的。

另外除了用

 

 

另外除了用<s:text>这个标签外,还可以使用<s:i18n>这个标签

 


view plaincopy to clipboardprint?
<s:i18n name="temp"></s:i18n>    

 

 

标签中包含name,代表着可以定义资源文件的baseName,如可以定义成temp,那么对应着

temp_en_US.properties和temp_zh_CN.properties这两个资源文件。

 

如定义:

 

 


view plaincopy to clipboardprint?
<s:i18n name="hello">    
    <s:text name="world">    
        <s:param>struts2</s:param>    
    </s:text>    
</s:i18n>    

 

 

注意到可以在<s:text>标签中增加<s:i18n> 标签。

在hello_en_US.properties文件中增加

world=hello {0}

hello_zh_CN.properties中增加

world=\u4f60\u597d,struts2

在struts2的默认拦截器栈中已经定义了i18n拦截器,所以struts2已经是一个国际化的框架了。

struts2会查找从客户端提交的request_locale属性,并存到session中的WW_TRANS_I18N_LOCALE字段

中。

这个<s:text> 标签外,还可以使用<s:i18n> 这个标签  


view plaincopy to clipboardprint?
<s:i18n name="temp"></s:i18n>    

 总结一下显示方法: 

<s:textname="hello"></s:text>

getText("username.invalid") 

<message key="username.xml.invalid"></message>  

<s:textfield name="username" key="username.name"></s:textfield>    

<s:i18n name="temp"></s:i18n>

Struts2的I18n Interceptor(二)【转】 java http://speakoutshaoye.blog.sohu.com/178816963.html
标签: struts2国际化  分类: 软件编程 2011-07-19 12:01国际化是商业系统中不可或缺的一部分,所以无论您学习的是什么Web框架,它都是必须掌握的技能。

其实,Struts 1.x在此部分已经做得相当不错了。它极大地简化了我们程序员在做国际化时所需的工作,例如,如果您要输出一条国际化的信息,只需在代码包中加入 FILE-NAME_xx_XX.properties(其中FILE-NAME为默认资源文件的文件名),然后在struts-config.xml中指明其路径,再在页面用<bean:message>标志输出即可。 

不过,所谓“没有最好,只有更好”。Struts 2.0并没有在这部分止步,而是在原有的简单易用的基础上,将其做得更灵活、更强大。

国际化Hello World
下面让我们看一个例子——HelloWorld。这个例子演示如何根据用户浏览器的设置输出相应的HelloWorld。

在Eclipse创建工程配置开发和运行环境(如果对这个步骤有问题,可以参考我早前的文章《为Struts 2.0做好准备》)。 

在src文件夹中加入struts.properties文件,内容如下: 

struts.custom.i18n.resources=globalMessages
 Struts 2.0有两个配置文件,struts.xml和struts.properties都是放在WEB-INF/classes/下。 
struts.xml用于应用程序相关的配置 
struts.properties用于Struts 2.0的运行时(Runtime)的配置
 

在src文件夹中加入globalMessages_en_US.properties文件,内容如下:

HelloWorld=Hello World!
在src文件夹中加入globalMessages_zh_CN.properties文件,内容如下:

HelloWorld=你好,世界!
 在此想和大家分享一个不错的编写properties文件的Eclipse插件(plugin),有了它我们在编辑一些简体中文、繁体中文等Unicode 文本时,就不必再使用native2ascii编码了。您可以通过Eclipse中的软件升级(Software Update)安装此插件,步骤如下:

1、展开Eclipse的Help菜单,将鼠标移到Software Update子项,在出现的子菜单中点击Find and Install;
2、在Install/Update对话框中选择Search for new features to install,点击Next;
3、在Install对话框中点击New Remote Site;
4、在New Update Site对话框的Name填入“PropEdit”或其它任意非空字符串,在URL中填入http://propedit.sourceforge.jp/eclipse/updates/;
5、在Site to include to search列表中,除上一步加入的site外的其它选项去掉,点击Finsih;
6、在弹出的Updates对话框中的Select the features to install列表中将所有结尾为“3.1.x”的选项去掉(适用于Eclipse 3.2版本的朋友);
7、点击Finish关闭对话框;
8、在下载后,同意安装,再按提示重启Eclipse,在工具条看到形似vi的按钮表示安装成功,插件可用。此时,Eclpise中所有properties文件的文件名前有绿色的P的图标作为标识。  

在WebContent文件夹下加入HelloWorl.jsp文件,内容如下:

<%@ page  contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
    <title>Hello World</title>
</head>
<body>
    <h2><s:text name="HelloWorld"/></h2>
    <h2><s:property value="%{getText('HelloWorld')}"/></h2>
</body>
</html>
发布运行应用程序,在浏览器地址栏中输入http://localhost:8080/Struts2_i18n/HelloWorld.jsp ,出现图1所示页面。

图1 中文输出
将浏览器的默认语言改为“英语(美国)”,刷新页面,出现图2所示页面。

图2 英文输出
上面的例子的做法,与Struts 1.x的做法相似,似乎并不能体现Struts 2.0的优势。不过,我在上面的例子用了两种方法来显示国际化字符串,其输出是相同的。其实,这就是Struts 2.0的一个优势,因为它默认支持EL,所示我们可以用getText方法来简洁地取得国际化字符串。另外更普遍的情况——在使用UI表单标志时,getText可以用来设置label属性,例如:

<s:textfield name="name" label="%{getText('UserName')}"/>
资源文件查找顺序
之所以说Struts 2.0的国际化更灵活是因为它可以能根据不同需要配置和获取资源(properties)文件。在Struts 2.0中有下面几种方法:

使用全局的资源文件,方法如上例所示。这适用于遍布于整个应用程序的国际化字符串,它们在不同的包(package)中被引用,如一些比较共用的出错提示; 
使用包范围内的资源文件。做法是在包的根目录下新建名的package.properties和package_xx_XX.properties文件。这就适用于在包中不同类访问的资源; 
使用Action范围的资源文件。做法为Action的包下新建文件名(除文件扩展名外)与Action类名同样的资源文件。它只能在该Action中访问。如此一来,我们就可以在不同的Action里使用相同的properties名表示不同的值。例如,在ActonOne中 title为“动作一”,而同样用title在ActionTwo表示“动作二”,节省一些命名工夫; 
使用<s:i18n>标志访问特定路径的properties文件。使用方法请参考我早前的文章《常用的Struts 2.0的标志(Tag)介绍》。在您使用这一方法时,请注意<s:i18n>标志的范围。在<s:i18n name="xxxxx">到</s:i18n>之间,所有的国际化字符串都会在名为xxxxx资源文件查找,如果找不到,Struts 2.0就会输出默认值(国际化字符串的名字)。
上面我列举了四种配置和访问资源的方法,它们的范围分别是从大到小,而Struts 2.0在查找国际化字符串所遵循的是特定的顺序,如图3所示:


图3 资源文件查找顺序图
假设我们在某个ChildAction中调用了getText("user.title"),Struts 2.0的将会执行以下的操作: 

查找ChildAction_xx_XX.properties文件或ChildAction.properties; 
查找ChildAction实现的接口,查找与接口同名的资源文件MyInterface.properties; 
查找ChildAction的父类ParentAction的properties文件,文件名为ParentAction.properties; 
判断当前ChildAction是否实现接口ModelDriven。如果是,调用getModel()获得对象,查找与其同名的资源文件; 
查找当前包下的package.properties文件; 
查找当前包的父包,直到最顶层包; 
在值栈(Value Stack)中,查找名为user的属性,转到user类型同名的资源文件,查找键为title的资源; 
查找在struts.properties配置的默认的资源文件,参考例1; 
输出user.title。
参数化国际化字符串
许多情况下,我们都需要在动行时(runtime)为国际化字符插入一些参数,例如在输入验证提示信息的时候。在Struts 2.0中,我们通过以下两种方法做到这点:

在资源文件的国际化字符串中使用OGNL,格式为${表达式},例如:

validation.require=${getText(fileName)} is required
使用java.text.MessageFormat中的字符串格式,格式为{ 参数序号(从0开始), 格式类形(number | date | time | choice), 格式样式},例如:

validation.between=Date must between {0, date, short} and {1, date, short}
在显示这些国际化字符时,同样有两种方法设置参数的值:

使用标志的value0、value1...valueN的属性,如:

<s:text name="validation.required" value0="User Name"/>
使用param子元素,这些param将按先后顺序,代入到国际化字符串的参数中,例如:

<s:text name="validation.required">
   <s:param value="User Name"/>
</s:text>
让用户方便地选择语言
开发国际化的应用程序时,有一个功能是必不可少的——让用户快捷地选择或切换语言。在Struts 2.0中,通过ActionContext.getContext().setLocale(Locale arg)可以设置用户的默认语言。不过,由于这是一个比较普遍的应用场景(Scenario),所以Struts 2.0为您提供了一个名i18n的拦截器(Interceptor),并在默认情况下将其注册到拦截器链(Interceptor chain)中。它的原理为在执行Action方法前,i18n拦截器查找请求中的一个名为"request_locale"的参数。如果其存在,拦截器就将其作为参数实例化Locale对象,并将其设为用户默认的区域(Locale),最后,将此Locale对象保存在session的名为 “WW_TRANS_I18N_LOCALE”的属性中。

下面,我将提供一完整示例演示它的使用方法。

package tutorial;

import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;

publicclass Locales {
   public Map<String, Locale> getLocales() {
       Map<String, Locale> locales =new Hashtable<String, Locale>(2);
       locales.put("American English", Locale.US);
       locales.put("Simplified Chinese", Locale.CHINA);
       return locales;
   }
}
tutorial/Locales.java


<%@taglib prefix="s" uri="/struts-tags"%>
<script type="text/javascript">
<!--
    function langSelecter_onChanged() {
        document.langForm.submit();
    }
//-->
</script>
<s:set name="SESSION_LOCALE" value="#session['WW_TRANS_I18N_LOCALE']"/>
<s:bean id="locales" name="tutorial.Locales"/>
<form action="<s:url includeParams="get" encode="true"/>" name="langForm" 
    style="background-color: powderblue; padding-top: 4px; padding-bottom: 4px;">
    Language: <s:select label="Language" 
        list="#locales.locales" listKey="value"    listValue="key"
        value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"
        name="request_locale" id="langSelecter" 
        onchange="langSelecter_onChanged()" theme="simple"/>
</form>
LangSelector.jsp
上述代码的原理为,LangSelector.jsp先实例化一个Locales对象,并把对象的Map类型的属性locales赋予下拉列表(select) 。如此一来,下拉列表就获得可用语言的列表。大家看到LangSelector有<s:form>标志和一段Javascript脚本,它们的作用就是在用户在下拉列表中选择了后,提交包含“reqeust_locale”变量的表单到Action。在打开页面时,为了下拉列表的选中的当前区域,我们需要到session取得当前区域(键为“WW_TRANS_I18N_LOCALE”的属性),而该属性在没有设置语言前是为空的,所以通过值栈中locale属性来取得当前区域(用户浏览器所设置的语言)。

你可以把LangSelector.jsp作为一个控件使用,方法是在JSP页面中把它包含进来,代码如下所示: 
<s:include value="/LangSelector.jsp"/>

在例1中的HellloWorld.jsp中<body>后加入上述代码,并在struts.xml中新建Action,代码如下:

<action name="HelloWorld">
    <result>/HelloWorld.jsp</result>
</action>

或者,如果你多个JSP需要实现上述功能,你可以使用下面的通用配置,而不是为每一个JSP页面都新建一个Action。

<action name="*">
    <result>/{1}.jsp</result>
</action>

分布运行程序,在浏览器的地址栏中输入http://localhost:8080/Struts2_i18n/HelloWorld.action,出现图4所示页面:

图3 HelloWorld.action

在下拉列表中,选择“American English”,出现图5所示页面:

图4 HelloWorld.action
 可能大家会问为什么一定要通过Action来访问页面呢?
你可以试一下不用Action而直接用JSP的地址来访问页面,结果会是无论你在下拉列表中选择什么,语言都不会改变。这表示不能正常运行的。其原因为如果直接使用JSP访问页面,Struts 2.0在web.xml的配置的过滤器(Filter)就不会工作,所以拦截器链也不会工作。 
import com.sun.image.codec.jpeg.JPEGCodec出错 java http://blog.sina.com.cn/s/blog_6a5f09820101249i.html
import com.sun.image.codec.jpeg.JPEGCodec出错

在Eclipse中处理图片,需要引入两个包:
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
报错:
Access restriction: The type JPEGImageEncoder is not accessible due to restriction on required library C:Javajre1.6.0_07librt.jar


此时解决办法:
Eclipse默认把这些受访问限制的API设成了ERROR。只要把Windows-Preferences-Java-Complicer-Errors/Warnings里面的Deprecated and restricted API中的Forbidden references(access rules)选为Warning就可以编译通过。
java图片压缩 java
package additional.util;

import java.awt.Color;  
import java.awt.Graphics2D;  
import java.awt.Image;  
import java.awt.image.BufferedImage;  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.util.Calendar;  
import java.util.Date;  

import org.apache.log4j.Logger;

import com.sun.image.codec.jpeg.ImageFormatException;  
import com.sun.image.codec.jpeg.JPEGCodec;  
import com.sun.image.codec.jpeg.JPEGEncodeParam;  
import com.sun.image.codec.jpeg.JPEGImageEncoder;  
  
/** 
 *   图片压缩工具类 提供的方法中可以设定生成的 缩略图片的大小尺寸、压缩尺寸的比例、图片的质量等 
 */  
public class ImageUtil {  
	
	private static Logger log=Logger.getLogger(ImageUtil.class);
  
    /** 
     * * 图片文件读取 
     *  
     * @param srcImgPath 
     * @return 
     */  
    public  static BufferedImage InputImage(String srcImgPath) {  
  
        BufferedImage srcImage = null;  
        try {  
            // 构造BufferedImage对象   
            File file = new File(srcImgPath);  
            FileInputStream in = new FileInputStream(file);  
            byte[] b = new byte[5];  
            in.read(b);  
            srcImage = javax.imageio.ImageIO.read(file);  
        } catch (IOException e) {  
            log.error("读取图片文件出错!",e);
        }  
        return srcImage;  
    }  
  
    /** 
     * * 将图片按照指定的图片尺寸、源图片质量压缩(默认质量为1) 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param new_w 
     *            :压缩后的图片宽 
     * @param new_h 
     *            :压缩后的图片高 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            int new_w, int new_h) {  
        return Tosmallerpic(srcImgPath, outImgPath, new_w, new_h, 1F);  
    }  
  
    /** 
     * 将图片按照指定的尺寸比例、源图片质量压缩(默认质量为1) 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param ratio 
     *            :压缩后的图片尺寸比例 
     * @param per 
     *            :百分比 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            float ratio) {  
        return Tosmallerpic(srcImgPath, outImgPath, ratio, 1F);  
    }  
  
    /** 
     * 将图片按照指定长或者宽的最大值来压缩图片(默认质量为1) 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param maxLength 
     *            :长或者宽的最大值 
     * @param per 
     *            :图片质量 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            int maxLength) {  
        return Tosmallerpic(srcImgPath, outImgPath, maxLength, 1F);  
    }  
  
    /** 
     * * 将图片按照指定的图片尺寸、图片质量压缩 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param new_w 
     *            :压缩后的图片宽 
     * @param new_h 
     *            :压缩后的图片高 
     * @param per 
     *            :百分比 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            int new_w, int new_h, float per) {  
        // 得到图片   
        BufferedImage src = InputImage(srcImgPath);  
        int old_w = src.getWidth();  
        // 得到源图宽   
        int old_h = src.getHeight();  
        // 得到源图长   
        // 根据原图的大小生成空白画布   
        BufferedImage tempImg = new BufferedImage(old_w, old_h,  
                BufferedImage.TYPE_INT_RGB);  
        // 在新的画布上生成原图的缩略图   
        Graphics2D g = tempImg.createGraphics();  
        g.setColor(Color.white);  
        g.fillRect(0, 0, old_w, old_h);  
        g.drawImage(src, 0, 0, old_w, old_h, Color.white, null);  
        g.dispose();  
        BufferedImage newImg = new BufferedImage(new_w, new_h,  
                BufferedImage.TYPE_INT_RGB);  
        newImg.getGraphics().drawImage(  
                tempImg.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0,  
                0, null);  
        // 调用方法输出图片文件   
        OutImage(outImgPath, newImg, per); 
        return newImg;
    }  
  
    /** 
     * * 将图片按照指定的尺寸比例、图片质量压缩 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param ratio 
     *            :压缩后的图片尺寸比例 
     * @param per 
     *            :百分比 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            float ratio, float per) {  
        // 得到图片   
        BufferedImage src = InputImage(srcImgPath);  
        int old_w = src.getWidth();  
        // 得到源图宽   
        int old_h = src.getHeight();  
        // 得到源图长   
        int new_w = 0;  
        // 新图的宽   
        int new_h = 0;  
        // 新图的长   
        BufferedImage tempImg = new BufferedImage(old_w, old_h,  
                BufferedImage.TYPE_INT_RGB);  
        Graphics2D g = tempImg.createGraphics();  
        g.setColor(Color.white);  
        // 从原图上取颜色绘制新图g.fillRect(0, 0, old_w, old_h);   
        g.drawImage(src, 0, 0, old_w, old_h, Color.white, null);  
        g.dispose();  
        // 根据图片尺寸压缩比得到新图的尺寸
        new_w = (int) Math.round(old_w * ratio);   
        new_h = (int) Math.round(old_h * ratio);  
        BufferedImage newImg = new BufferedImage(new_w, new_h,  
                BufferedImage.TYPE_INT_RGB);  
        newImg.getGraphics().drawImage(  
                tempImg.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0,  
                0, null);  
        // 调用方法输出图片文件
        OutImage(outImgPath, newImg, per);  
        return newImg;
    }  
  
    /** 
     * * 指定长或者宽的最大值来压缩图片 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param maxLength 
     *            :长或者宽的最大值 
     * @param per 
     *            :图片质量 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            int maxLength, float per) {  
        // 得到图片   
        BufferedImage src = InputImage(srcImgPath);  
        int old_w = src.getWidth();  
        // 得到源图宽   
        int old_h = src.getHeight();  
        // 得到源图长   
        int new_w = 0;  
        // 新图的宽   
        int new_h = 0;  
        // 新图的长   
        BufferedImage tempImg = new BufferedImage(old_w, old_h,  
                BufferedImage.TYPE_INT_RGB);  
        Graphics2D g = tempImg.createGraphics();  
        g.setColor(Color.white);  
        // 从原图上取颜色绘制新图   
        g.fillRect(0, 0, old_w, old_h);  
        g.drawImage(src, 0, 0, old_w, old_h, Color.white, null);  
        g.dispose();  
        // 根据图片尺寸压缩比得到新图的尺寸   
        if (old_w > old_h) {  
            // 图片要缩放的比例   
            new_w = maxLength;  
            new_h = (int) Math.round(old_h * ((float) maxLength / old_w));  
        } else {  
            new_w = (int) Math.round(old_w * ((float) maxLength / old_h));  
            new_h = maxLength;  
        }  
        BufferedImage newImg = new BufferedImage(new_w, new_h,  
                BufferedImage.TYPE_INT_RGB);  
        newImg.getGraphics().drawImage(  
                tempImg.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0,  
                0, null);  
        // 调用方法输出图片文件   
        OutImage(outImgPath, newImg, per);  
        return newImg;
    }  
  
    /** 
     * * 将图片文件输出到指定的路径,并可设定压缩质量 
     *  
     * @param outImgPath 
     * @param newImg 
     * @param per 
     */  
    private static void OutImage(String outImgPath, BufferedImage newImg,  
            float per) {  
        // 判断输出的文件夹路径是否存在,不存在则创建   
        File file = new File(outImgPath);  
        if (!file.getParentFile().exists()) {  
            file.getParentFile().mkdirs();  
        }// 输出到文件流   
        try {  
            FileOutputStream newimage = new FileOutputStream(outImgPath);  
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(newimage);  
            JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(newImg);  
            // 压缩质量   
            jep.setQuality(per, true);  
            encoder.encode(newImg, jep);  
            newimage.close();  
        } catch (FileNotFoundException e) {  
            // TODO Auto-generated catch blocke.printStackTrace();   
        } catch (ImageFormatException e) {  
            // TODO Auto-generated catch blocke.printStackTrace();   
        } catch (IOException e) {  
            // TODO Auto-generated catch blocke.printStackTrace();   
        }  
    }  
  
//    public static void main(String args[]) {  
//        String f = "d:/img/";  
//        File file = new File(f);  
//        if (file.exists()) {  
//            File[] filelist = file.listFiles();  
//            for (int i = 0; i < filelist.length; i++) {  
//                File fi = filelist[i];  
//                System.out.println(fi.length());  
//                String n = filelist[i].getName();  
//                // Tosmallerpic(f, filelist[i], "_ratio_small", n,   
//                // 0.303,(float)0.7);   
//                // Tosmallerpic(f, filelist[i], "_ratio_smaller", n,   
//                // 0.083,(float)0.7);   
//            }  
//        }  
//        String srcImg = "d:/img/2.jpg";  
//        String tarDir = "d:/img/newImg/";  
//        long startTime = new Date().getTime();  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_1.jpg", 400);  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_2.jpg", 0.5F);  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_3.jpg", 400, 500);  
//        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_11.jpg", 400, 0.7F);  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_22.jpg", 0.5F, 0.8F);  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_33.jpg", 400, 500, 0.2F);  
//        System.out.println(new Date().getTime() - startTime);  
//    }  
}  
<context-param>与<init-param>的区别与作用 java http://www.cnblogs.com/hzj-/articles/1689836.html
<context-param>与<init-param>的区别与作用

spring   2009-11-04 16:49    阅读39    评论0  字号: 大  中  小<context-param>的作用:
web.xml的配置中<context-param>配置作用
1. 启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件web.xml.读两个节点: <listener></listener> 和 <context-param></context-param>2.紧接着,容器创建一个ServletContext(上下文),这个WEB项目所有部分都将共享这个上下文.3.容器将<context-param></context-param>转化为键值对,并交给ServletContext.4.容器创建<listener></listener>中的类实例,即创建监听.5.在监听中会有contextInitialized(ServletContextEvent args)初始化方法,在这个方法中获得ServletContext = ServletContextEvent.getServletContext();
context-param的值 = ServletContext.getInitParameter("context-param的键");6.得到这个context-param的值之后,你就可以做一些操作了.注意,这个时候你的WEB项目还没有完全启动完成.这个动作会比所有的Servlet都要早.
换句话说,这个时候,你对<context-param>中的键值做的操作,将在你的WEB项目完全启动之前被执行.7.举例.你可能想在项目启动之前就打开数据库.
那么这里就可以在<context-param>中设置数据库的连接方式,在监听类中初始化数据库的连接.8.这个监听是自己写的一个类,除了初始化方法,它还有销毁方法.用于关闭应用前释放资源.比如说数据库连接的关闭.如:
<!-- 加载spring的配置文件 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml,/WEB-INF/action-servlet.xml,/WEB-INF/jason-servlet.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>又如: --->自定义context-param,且自定义listener来获取这些信息<context-param>
    <param-name>urlrewrite</param-name>
    <param-value>false</param-value>
</context-param>
<context-param>
    <param-name>cluster</param-name>
    <param-value>false</param-value>
</context-param>
<context-param>
    <param-name>servletmapping</param-name>
    <param-value>*.bbscs</param-value>
</context-param>
<context-param>
    <param-name>poststoragemode</param-name>
    <param-value>1</param-value>
</context-param>
<listener>
    <listener-class>com.laoer.bbscs.web.servlet.SysListener</listener-class>
</listener>public class SysListener extends HttpServlet implements ServletContextListener {private static final Log logger = LogFactory.getLog(SysListener.class);public void contextDestroyed(ServletContextEvent sce) {   //用于在容器关闭时,操作
}//用于在容器开启时,操作public void contextInitialized(ServletContextEvent sce) {
   String rootpath = sce.getServletContext().getRealPath("/");
   System.out.println("-------------rootPath:"+rootpath);   if (rootpath != null) {
    rootpath = rootpath.replaceAll("\\\\", "/");
   } else {
    rootpath = "/";
   }
   if (!rootpath.endsWith("/")) {
    rootpath = rootpath + "/";
   }
   Constant.ROOTPATH = rootpath;
   logger.info("Application Run Path:" + rootpath);
   String urlrewrtie = sce.getServletContext().getInitParameter("urlrewrite");
   boolean burlrewrtie = false;
   if (urlrewrtie != null) {
    burlrewrtie = Boolean.parseBoolean(urlrewrtie);
   }
   Constant.USE_URL_REWRITE = burlrewrtie;
   logger.info("Use Urlrewrite:" + burlrewrtie);
   其它略之....    }}
   /*最终输出
   -------------rootPath:D:\tomcat_bbs\webapps\BBSCS_8_0_3\
   2009-06-09 21:51:46,526 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]Application Run Path:D:/tomcat_bbs/webapps/BBSCS_8_0_3/
   2009-06-09 21:51:46,526 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]Use Urlrewrite:true
   2009-06-09 21:51:46,526 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]Use Cluster:false
   2009-06-09 21:51:46,526 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]SERVLET MAPPING:*.bbscs
   2009-06-09 21:51:46,573 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]Post Storage Mode:1
   */context-param和init-param区别
web.xml里面可以定义两种参数:
(1)application范围内的参数,存放在servletcontext中,在web.xml中配置如下:
<context-param>
           <param-name>context/param</param-name>
           <param-value>avalible during application</param-value>
</context-param>(2)servlet范围内的参数,只能在servlet的init()方法中取得,在web.xml中配置如下:
<servlet>
    <servlet-name>MainServlet</servlet-name>
    <servlet-class>com.wes.controller.MainServlet</servlet-class>
    <init-param>
       <param-name>param1</param-name>
       <param-value>avalible in servlet init()</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
</servlet>在servlet中可以通过代码分别取用:
package com.wes.controller;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;public class MainServlet extends HttpServlet ...{    public MainServlet() ...{
        super();
     }
    public void init() throws ServletException ...{
         System.out.println("下面的两个参数param1是在servlet中存放的");
         System.out.println(this.getInitParameter("param1"));
         System.out.println("下面的参数是存放在servletcontext中的");
        System.out.println(getServletContext().getInitParameter("context/param"));
      }
}第一种参数在servlet里面可以通过getServletContext().getInitParameter("context/param")得到
第二种参数只能在servlet的init()方法中通过this.getInitParameter("param1")取得.
session监听器 java http://hi.baidu.com/hero8_8_8/blog/item/e2068c8fc2f792e6f11f36e7.html
session监听器2009-03-02 14:14servlet中对session的监听有很多接口,功能很灵活,最常用的是监听Session和Attribute。这里要澄清一下概念,servlet中的session监听和Attribute监听含义有差别,session监听指的不是我们一般所理解的放置一个session或者销毁一个session,这是Attribute监听的功能,因为servlet中放置session的语法是session.setAttribute(“session名”,要放入的对象)。而session监听,监听的是HTTP连接,只要有用户与server连接,就算连接的是一个空白的jsp页面,也会触发session事件,所以此处的session实际上指的是connection,用来统计当前在线用户数最合适了。不知道我说清楚了没有。下面分别讲解这两种监听方式。 

1、 session监听 

首先编写一个session监听类,实作HttpSessionListener接口,它的作用是计算当前有多少个在线用户: 
/* 
*@Author bromon 
*2004-6-12 
*/ 
package org.bromon.test; 

import javax.servlet.*; 
import javax.servlet.http.*; 

public class SessionCount implements HttpSessionListener{ 
private static int count=0; 

public void sessionCreated(HttpSessionEvent se){ 
count++; 
System.out.println(“session创建:”+new java.util.Date()); 
} 

public void sessionDestroyed(HttpSessionEvent se){ 
count--; 
System.out.println(“session销毁:”+new java.util.Date()); 
} 

public static int getCount(){ 
return(count); 
} 
} 

怎么样,是不是一目了然?count被定义为static,是因为要保证整个系统只有这一个count。如果你实在不放心,可以把它写成一个单例类。 

然后在web.xml中声明这个监听器: 
<listener> 
<listener-class> 
org.bromon.test.SessionCount 
</listener-class> 
</listener> 

编写一个测试页面test.jsp,内容是获得count: 
<% 
int count=org.bromon.test.SessionCount.getCount(); 
out.println(count); 
%> 
需要注意的是,这里根本不涉及任何session的操作。重启动App server,试着连接test.jsp,可以看到监听器已经开始工作。 

2、 Attribute监听 
例如一个站内消息系统,肯定要获得所有登陆者的ID,才有可能互发消息。这就涉及Attribute监听。假设我们写了个用户登陆的模块,用户通过身份验证之后会产生一个session,保存它的相关信息,比如: 
//check.jsp 
<% 
String name=request.getParameter(“name”); 
Name=new String(name.getBytes(“ISO8859-1”)); 
session.setAttribute(“user”,name); 
%> 

做过jsp的兄弟应该对这段代码再熟悉不过了,下面写个监听器来监听用户登陆,把所有用户的ID保存到一个List当中,这个监听器实作HttpSessionAttributeListener接口: 

/* 
*@Author bromon 
*2004-6-12 
*/ 
package org.bromon.test; 

import javax.servlet.*; 
import javax.servlet.http.*; 
import java.util.*; 

public class OnlineList implements HttpSessionAttributeListener{ 
private static List list=new ArrayList(); 

public void attributeAdded(HttpSessionBindingEvent se){ 
if("user".equals(se.getName())){ 
list.add(se.getValue()); 
} 
} 

public void attributeRemoved(HttpSessionBindingEvent se){ 
if("user".equals(se.getName())){ 
list.remove(se.getValue()); 
} 
} 

public void attributeReplaced(HttpSessionBindingEvent se){} 

public static List getList(){ 
return(list); 
} 
} 

写个简单的jsp来得到用户列表: 
<% 
java.util.List list=org.bromon.test.OnlineList.getList(); 
out.println("共有"+list.size()+"名用户已登陆:"); 
for(int I=0;I <lise.size();i++){ 
out.println(list.get(i)); 
} 
%>  
java去除字符串中的空格、回车、换行符、制表符 java
java去除字符串中的空格、回车、换行符、制表符
01
import java.util.regex.Matcher;
02

import java.util.regex.Pattern;
03
04
05
06

/**
07

 * @author lei
08

 * 2011-9-2
09

 */
10

public class StringUtils {
11

12

    public static String replaceBlank(String str) {
13

        String dest = "";

14

        if (str!=null) {
15

            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
16

            Matcher m = p.matcher(str);
17

            dest = m.replaceAll("");
18

        }

19

        return dest;
20

    }
21

    public static void main(String[] args) {
22

        System.out.println(StringUtils.replaceBlank("just do it!"));
23

    }
24

    /*-----------------------------------


    笨方法:String s = "你要去除的字符串";

 

            1.去除空格:s = s.replace('\\s','');





29

 





30

            2.去除回车:s = s.replace('\n','');





31

 





32

    这样也可以把空格和回车去掉,其他也可以照这样做。





33

 





34

    注:\n 回车(\u000a) 





35

    \t 水平制表符(\u0009) 





36

    \s 空格(\u0008) 





37

    \r 换行(\u000d)*/





38

}
Global site tag (gtag.js) - Google Analytics