2010年3月25日星期四

[精品]Spring MVC的深入学习一


一 SessionAttribute

         Spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 session 中,以便下一个请求属对应的 ModelMap 的属性列表中还能访问到这些属性。这一功能是通过类定义处标注 @SessionAttributes 注解来实现的。

使用方法:
 @Controller @RequestMapping("/bbtForum.do") @SessionAttributes("currUser") //①将ModelMap中属性名为currUser的属性 //放到Session属性列表中,以便这个属性可以跨请求访问 public class BbtForumController { …     @RequestMapping(params = "method=listBoardTopic")     public String listBoardTopic(@RequestParam("id")int topicId, User user, ModelMap model) {         bbtForumService.getBoardTopics(topicId);         System.out.println("topicId:" + topicId);         System.out.println("user:" + user);         model.addAttribute("currUser",user); //②向ModelMap中添加一个属性         return "listTopic";     } }

二 ModelAttribute

         上面讲述了如何往ModelMap中放置属性以及如何使ModelMap中的属性拥有Session域的作用范围。除了在JSP视图页面中通过传统的方法访问ModelMap中的属性外,Spring提供了一个@ModelAttribute的注解可以将ModelMap中的属性绑定到请求处理方法的入参中,他还可以与jsp标签<form:form modelAttribute="entity"></form:form>一起使用。
         总结它的作用:
  • 将多个参数绑定到一个Bean中;
  • 自动将Bean值add到Model中,相当于进行了Model.addAttribute("key",bean);这样可以在jsp中使用EL表达式或是传统的jsp表达式访问bean;
  • 与jsp标签<form:form modelAttribute="entity"></form:form>一起使用,直接将bean的各个域对应到form标签的子标签(如<form:input path="field"/>,只需要将域的值设置为path值)直接显示在页面上。
  • 在方法之上使用,使方法的返回值被add到Model中,这个方法会被所有此类中的请求方法被调用时使用。所以所有的请求的model中都会有这个Attribute存在。

使用方法:
 @Controller @RequestMapping("/bbtForum.do") @SessionAttributes("currUser") //①让ModelMap的currUser属性拥有session级作用域 public class BbtForumController {  @Autowired private BbtForumService bbtForumService;     @RequestMapping(params = "method=listBoardTopic")     public String listBoardTopic(@RequestParam("id")int topicId, User user, ModelMap model) {         bbtForumService.getBoardTopics(topicId);         System.out.println("topicId:" + topicId);         System.out.println("user:" + user);         model.addAttribute("currUser",user); //②向ModelMap中添加一个属性         return "listTopic";     }
    @RequestMapping(params = "method=listAllBoard")    //③将ModelMap中的 public String listAllBoard(@ModelAttribute("currUser") User user) {  //currUser属性绑定到user入参中。         bbtForumService.getAllBoard();         System.out.println("user:"+user);         return "listBoard";     } }

在 ② 处,我们向 ModelMap 中添加一个名为 currUser 的属性,而 ① 外的注解使这个 currUser 属性拥有了 session 级的作用域。所以,我们可以在 ③ 处通过 @ModelAttribute 注解将 ModelMap 中的 currUser 属性绑定以请求处理方法的 user 入参中。

所以当我们先调用以下 URL 请求: http://localhost/bbtForum.do?method=listBoardTopic&id=1&userName=tom&dept.deptId=12

以执行listBoardTopic()请求处理方法,然后再访问以下URL: http://localhost/sample/bbtForum.do?method=listAllBoard

你将可以看到 listAllBoard() 的 user 入参已经成功绑定到 listBoardTopic() 中注册的 session 级的 currUser 属性上了。


在方法级上使用ModelAttribute:

 @Controller @RequestMapping("/bbtForum.do") public class BbtForumController {      @Autowired     private BbtForumService bbtForumService;      @ModelAttribute("items")//<——①向模型对象中添加一个名为items的属性     public List<String> populateItems() {         List<String> lists = new ArrayList<String>();         lists.add("item1");         lists.add("item2");         return lists;     }      @RequestMapping(params = "method=listAllBoard")     public String listAllBoard(@ModelAttribute("currUser")User user, ModelMap model) {         bbtForumService.getAllBoard();         //<——②在此访问模型中的items属性         System.out.println("model.items:" + ((List<String>)model.get("items")).size());         return "listBoard";     } }

三 注册自己的属性编辑器
 
  • 注册框架级的自定义属性编辑器
 <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">     <property name="webBindingInitializer">         <bean class="com.baobaotao.web.MyBindingInitializer"/>     </property> </bean>

 
 MyBindingInitializer是继承自
 
 WebBindingInitializer的类,如下:
 
 
 
 
 
 
 public class MyBindingInitializer implements WebBindingInitializer {     public void initBinder(WebDataBinder binder, WebRequest request) {         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");         dateFormat.setLenient(false);         binder.registerCustomEditor(Date.class,              new CustomDateEditor(dateFormat, false));         binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));     } }
  • 注册 Controller 级的自定义属性编辑器
如果希望某个属性编辑器仅作用于特定的 Controller,可以在 Controller 中定义一个标注 @InitBinder 注解的方法,可以在该方法中向 Controller 了注册若干个属性编辑器,来看下面的代码:

 @Controller public class MyFormController {     @InitBinder     public void initBinder(WebDataBinder binder) { //必须有WebDataBinder类型的参数         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");         dateFormat.setLenient(false);         binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));     }     … }

[note]Spring MVC接收数组或List参数

客户端:
方法:POST
参数:columnList  Health
          columnList  Sport
          拥有相同的name即可。 

服务器端:

Servlet方式:request.getParameterValues("columnList");可以得到String数组

SpringMVC注解方式:@RequestParameter("columnList") String[] list;           //数组形式
                                 @RequestParameter("columnList") List<String> list;   //List形式
                                 @ModelAttribute MyModel model;                             //模型中带有数组或者List

public class MyModel {
      List<String> list;
      或者String[] list;

      ......
}

[note][转]外部引入CSS 的两种方式link和@import的区别


本质上,这两种方式都是为了加载CSS文件,但还是存在着细微的差别。
  • 老祖宗的差别。link属于XHTML标签,而@import完全是CSS提供的一种方式。link标签除了可以加载CSS外,还可以做很多其它的事情,比如定义RSS,定义rel连接属性等,@import就只能加载CSS了。
  • 加载顺序的差别。当一个页面被加载的时候(就是被浏览者浏览的时候),link引用的CSS会同时被加载,而@import引用的CSS 会等到页面全部被下载完再被加载。所以有时候浏览@import加载CSS的页面时开始会没有样式,然后突然样式会出现,网速慢的时候还挺明显。
  • 兼容性的差别。由于@import是CSS2.1提出的所以老的浏览器不支持,@import只有在IE5以上的才能识别,而link标签无此问题。
  • 使用dom控制样式时的差别。当使用javascript控制dom去改变样式的时候,只能使用link标签,因为@import不是dom可以控制的。
所以,无特殊情况推荐使用link来引入样式,尽量避免使用@import

2010年3月23日星期二

[note][转]javascript 中Object.prototype的几个方法

转自:http://playgoogle.com/?p=198


hasOwnProperty(property)

判断对象是否有某个特定的属性。必须用字符串指定该属性。(例如,o.hasOwnProperty(“name”)),返回布尔值。此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。

如下代码:

var s =”";

alert(s.hasOwnProperty(“split”)); èreturn false

alert(String.prototype.hasOwnProperty(“split”));èreturn true

isPrototypeOf(object)

判断该对象是否为另一个对象的原型。

obj1.isPrototypeOf(obj2);

obj1是一个对象的实例;obj2是另一个将要检查其原型链的对象。原型链可以用来在同一个对象类型的不同实例之间共享功能。如果obj2的原型链中包含obj1,那么isPrototypeOf 方法返回 true。如果obj2不是一个对象或者obj1没有出现在obj2中的原型链中,isPrototypeOf 方法将返回 false

propertyIsEnumerable()

判断给定的属性是否可以用 for…in 语句进行枚举,返回布尔值。

toString()

返回对象的原始字符串表示。对于 Object 对象,ECMA-262 没有定义这个值,所以不同的 ECMAScript 实现具有不同的值。

toLocaleString()

Object中和toString()方法的实现方式一样,但在其他类中有特定的实现,如在DatetoLocaleString() 方法可根据本地时间把 Date 对象转换为字符串,并返回结果。

valueOf()

返回最适合该对象的原始值。对于许多对象,该方法返回的值都与 toString() 的返回值相同。

以上6个方法都是Object.prototype上定义的,ECMAScript 中的所有对象都由Object继承而来,所以在ECMAScript上的所有对象都具有以几个方法。

2010年3月19日星期五

Broadcom Corporation BCM4312 802.11b/g
Intel(R) Pentium(R) Dual  CPU  T2370  @ 1.73GHz
8600M GT
bootsect /nt60 SYS /mbr /force

2010年3月17日星期三

[note]display:inline及与float:left用途详细区分与特征说明

      首先我们要明确,display:inline;与float:left;正确含义。display:inline;(内联)《CSS权威指南》中文字显示:任何不是块级元素的可见元素都是内联元素。其表现的特性是“行布局”形式,这里的“行布局”的意思就是说其表现形式始终以行进行显示。比如,我们设定一个内联元素border-bottom:1px solid #000;时其表现是以每行进行重复,每一行下方都会有一条黑色的细线。如果是块级元素那么所显示的的黑线只会在块的下方出现。

  当然这看起来不像是display:inline;与float:left;的区别所在,但是当理解了float:left;的特性那么我们就清楚到底是怎么回事了。float:left;(左浮动)他使得指定元素脱离普通的文档流而产生的特别的布局特性。并且FLOAT必需应用在块级元素之上,也就是说浮动并不应用于内联标签。或者换句话来说当应用了FLOAT那么这个元素将被指定为块级元素。

  那么我们很清楚了,内联(display:inline;)元素不能设置宽高,因为内联属于行布局,其特性是在一行里进行布局,所以不能被设定宽高。

2010年3月10日星期三

[note]Spring MultipartFile类解决文件上传问题

使用MultipartFile类解决文件的上传十分方便,代码如下:

        @RequestMapping(value = "/member/spaceBackground.do", method = RequestMethod.POST)
public void processSetupBackground(Model model, HttpServletResponse resp, HttpServletRequest req, @RequestParam("fileupload") MultipartFile fileupload) {
try {
String logoName = "logonai.gif";
String filePath = ProjectEnv.getWebappPath() + "/images/";
FileTools.input2File(filePath, fileupload.getInputStream(), logoName, true);  //将上传的文件用input流的方式传到文件logonai.gif中
PrintWriter out = resp.getWriter();
out.print("true");
out.close();
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
}
}

[note]真正的JSON格式

以前我写JSON时是这样写的:{name:'zmm',career:'programmer'}

今天发现有两个错误:

第一:JSON官方标准里面说明了不用单引号而必须用双引号。
第二:JSON官方标准里面String必须用双引号包裹,而key使用String表示。

下面是正确的JSON:
{"name":"zmm","career":"programmer"}

如果用Java那么需要放到String里面,如下:

String json = "{\"name\":\"zmm\",\"career\":\"programmer\"}";

使用转义符" \ "。

2010年3月4日星期四

[note]jQuery选择符转义

jQuery的选择符中如果有特殊字符如"#"、"."。那么需要进行转义,很容易只要在特殊字符前加上"\\"即可,如".\\.show",查找的是类名为".show"的节点。

2010年3月2日星期二

[note][转]使用Iterator 或for-each注意:java.util.ConcurrentModificationException

原帖:http://www.blogjava.net/fingki/archive/2010/03/02/314268.html

在使用Iterator处理Collection时,注意java.util.ConcurrentModificationException。
1.如果你仅仅是对collection进行遍历查询,那么不必担心什么。
2.但如果你在遍历过程中要对collection进行删除,那么你就要注意了。
For example:
private void testDel() {  
  1.     List<String> list = new ArrayList<String>();  
  2.     for (int i = 0; i < 10; i++) {  
  3.         String str = "td" + i;  
  4.         list.add(str);  
  5.     }  
  6.   
  7.     for (Iterator it = list.iterator(); it.hasNext();) {  
  8.         String str = (String) it.next();  
  9.         if (str.equals("td5")) {  
  10.             // list.remove(str);  // 删除方法一 
  11.             it.remove();  // 删除方法二 
  12.         }  
  13.     }  

上面的代码运行没有问题,但如果你用“方法一”替代“方法二”,则会出现java.util.ConcurrentModificationException。
(用for-each遍历也会出个类似问题)
具体原因是可以看一下先看看List中的remove方法源码:
  1. public boolean remove(Object o) {  
  2.     if (o == null) {  
  3.         for (int index = 0; index < size; index++)  
  4.             if (elementData[index] == null) {  
  5.                 fastRemove(index);  
  6.                 return true;  
  7.             }  
  8.     } else {  
  9.         for (int index = 0; index < size; index++)  
  10.             if (o.equals(elementData[index])) {  
  11.                 fastRemove(index);  
  12.                 return true;  
  13.             }  
  14.     }  
  15.     return false;  
  16. }  
  17.   
  18. private void fastRemove(int index) {  
  19.     modCount++; // 特别注意这里,这里只增加了modCount的值  
  20.     int numMoved = size - index - 1;  
  21.     if (numMoved > 0)  
  22.         System.arraycopy(elementData, index + 1, elementData, index,  
  23.                 numMoved);  
  24.     elementData[--size] = null; // Let gc do its work  

接着看。删除后得到下一个元素的代码,it.next():  it为AbstractList的内部类Iterator的一个实例。
  1. public E next() {  
  2.     checkForComodification();  
  3.     try {  
  4.         E next = get(cursor);  
  5.         lastRet = cursor++;  
  6.         return next;  
  7.     } catch (IndexOutOfBoundsException e) {  
  8.         checkForComodification();  
  9.         throw new NoSuchElementException();  
  10.     }  
  11. }  
  12.   
  13. final void checkForComodification() {  //注意这个方法
  14.     if (modCount != expectedModCount)  //检查这两个值是否相同
  15.         throw new ConcurrentModificationException();  

最后看Iterator的remove()方法的源代码:
  1. public void remove() {  
  2.     if (lastRet == -1)  
  3.         throw new IllegalStateException();  
  4.     checkForComodification();  
  5.     try {  
  6.         AbstractList.this.remove(lastRet);  
  7.         if (lastRet < cursor)  
  8.             cursor--;  
  9.         lastRet = -1;  
  10.         expectedModCount = modCount; // 设置expectedModCount  
  11.     } catch (IndexOutOfBoundsException e) {  
  12.         throw new ConcurrentModificationException();  
  13.     }  
  14. }  
  15.   
  16. final void checkForComodification() {  
  17.     if (modCount != expectedModCount)  
  18.         throw new ConcurrentModificationException();  

这下就明白了,list的remove方法只修改了modCount值,而iterator的remove能同步modCount和expectedModCount.