在上一篇Spring+SpringMVC+Mybatis整合中說到了SSM的整合,並且在其中添加了一個簡單的查詢功能,目的只是將整個整合的流程進行一個梳理,下面在上一篇中工程的基礎上再說一些關於SpringMVC的Controller的一些細節。
首先附上整個項目結構圖,附上整個代碼工程的下載地址,下面所講到的測試用例都是在下面這個測試項目的基礎上進行的。
一、關於Controller的注解形式
1、使用@Controller注解可以實現Controller的注解開發,然后在springmvc.xml的配置文件中配置注解掃描器,就可以使用注解形式進行Controller的開發,下面我們簡單使用一個helloworld的例子進行說明
①在springmvc.xml中配置注解掃描器
其中也當然包含springmvc所需要的處理器映射器、處理器適配器、視圖解析器(這幾個組件個概念可以查看SpringMvc入門,其中開篇說到了SpringMVC的處理流程和各個組件以及之間的關系),我們這里直接使用下面的配置方式進行配置
②寫一個簡單的helloworld,在瀏覽器中請求對應的Controller,然后輸出在頁面上
1 package cn.test.ssm.controller; 2 3 import org.springframework.stereotype.Controller; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.servlet.ModelAndView; 6 7 @Controller 8 public class HelloWorldController { 9 10 @RequestMapping("/helloWorld.do") 11 public ModelAndView helloWorld() throws Exception{ 12 ModelAndView modelAndView = new ModelAndView(); 13 modelAndView.addObject("test","HelloSSM"); 14 modelAndView.setViewName("/WEB-INF/items/hello.jsp"); 15 return modelAndView; 16 } 17 }

1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <html> 3 <head> 4 <title>$Title$</title> 5 </head> 6 <body> 7 測試Controller 8 ${test} 9 </body> 10 </html>
③然后在地址欄中請求http://localhost:8080/TestSSM2/helloWorld.do,輸出
二、關於RequestMapping
1、使用不同的處理器映射規則
a、我們通過RequestMapping 可以使用不同的處理器映射規則,RequestMapping注解能夠控制http請求的路徑和方式(get、post......),在同一個Controller中可以寫不同的映射方法,映射瀏覽器不同的請求業務。
具體的使用方式就是:@RequestMapping(value="/test.do")或@RequestMapping("/test),其中value的值是數組,可以將多個url映射到同一個方法
b、下面我們就在上一篇中查詢列表的基礎上增加查詢詳細信息的一個功能,通過RequestMapping注解來實現
①首先在mapper中將ProductDemo.xml中添加查詢詳細信息的Sql配置
<select id="queryProductInfo" parameterType="java.lang.Integer" resultType="cn.test.ssm.po.ProductExtend"> SELECT pname,shop_price FROM product WHERE pid = #{id} </select>
②在mapper接口中添加上面的方法
③在service接口中添加相應的方法和方法實現
service接口
接口實現類
④在controller層中加上queryInfo方法,其中使用RequestMapping映射了兩個不同請求對應的方法實現
1 package cn.test.ssm.controller; 2 3 import cn.test.ssm.po.ProductExtend; 4 import cn.test.ssm.service.ProductService; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.stereotype.Controller; 7 import org.springframework.web.bind.annotation.RequestMapping; 8 import org.springframework.web.servlet.ModelAndView; 9 10 import java.util.List; 11 12 @Controller 13 public class ProductController { 14 15 @Autowired 16 private ProductService productService; 17 18 @RequestMapping("/queryList.do") 19 public ModelAndView queryList() throws Exception{ 20 //從service層調用方法 21 List<ProductExtend> productExtendList = productService.findProductListByName(null); 22 //返回ModelandView 23 ModelAndView modelAndView = new ModelAndView(); 24 modelAndView.addObject(productExtendList); 25 modelAndView.setViewName("/WEB-INF/items/itemsList.jsp"); 26 return modelAndView; 27 } 28 29 @RequestMapping("/queryInfo.do") 30 public ModelAndView queryInfo() throws Exception { 31 32 ProductExtend productExtend = productService.queryProductInfo(1); 33 productExtend.setDesc("這是相機"); 34 ModelAndView modelAndView = new ModelAndView(); 35 modelAndView.addObject(productExtend); 36 modelAndView.setViewName("/WEB-INF/items/editItem.jsp"); 37 return modelAndView; 38 } 39 }
⑤最后在查詢列表中點擊查詢即可查看詳細信息
2、窄化請求映射
a)為了實現不同模塊之間的開發,我們可以進行這樣的使用:在class上添加@RequestMapping(url)指定通用請求前綴, 限制此類下的所有方法請求url必須以請求前綴開頭,通過此方法對url進行分類管理。
b)如下:@RequestMapping放在類名上邊,設置請求前綴
@Controller
@RequestMapping("/test")
然后在方法名上邊設置請求映射url:
@RequestMapping("/queryItem ")
訪問地址為:http://localhost:8080/TestSSM2/test/queryList.do
3、關於http請求方式限定
a)限定POST方法:@RequestMapping(method = RequestMethod.POST)
如果通過Get訪問則報錯:HTTP Status 405 - Request method 'GET' not supported,例如
然后訪問http://localhost:8080/TestSSM2/test/queryList.do,就會是下面的錯誤
b)限定GET方法:@RequestMapping(method = RequestMethod.GET)
如果通過Post訪問則報錯:HTTP Status 405 - Request method 'POST' not supported
c)GET和POST都可以:@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
三、關於Controller的返回值問題
1、返回ModelAndView
a)我們上面編寫Controller都是以這種方式進行的,大概就是定義一個ModelAndView對象,然后填充模型(從數據庫中得到的數據)和邏輯視圖(指定的jsp等路徑),並返回即可
b)例如
2、使用void類型
a)在controller方法形參上可以定義request和response,使用request或response指定響應結果:
①使用request轉向頁面:request.getRequestDispatcher("頁面路徑").forward(request, response);
例如:
@RequestMapping("/test_void.do") public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception{ request.setAttribute("test","返回值為void類型的測試"); request.getRequestDispatcher("/WEB-INF/items/hello.jsp").forward(request,response); }
然后輸出結果
②通過response頁面重定向:response.sendRedirect("url"),實現方式同上
3、使用String作為返回值
a)Controller中的方法形參為model,然后通過形參將數據返回到請求頁面上,最后返回字符串可以指定邏輯視圖名(路徑信息),通過視圖解析器解析為物理視圖地址;
1 @RequestMapping("/testString.do") 2 public String testString(Model model) throws Exception { 3 //其他進行的操作 4 //通過形參model將數據返回到請求頁面上 類似於返回ModelAndView中的addObject方法 5 model.addAttribute("testString","testString"); 6 //然后返回邏輯視圖名,經過視圖解析器解析為相應的jsp等路徑 7 return "test/helloWorld"; 8 }
b)重定向:Contrller方法返回結果重定向到一個url地址,但是由於重定向之后原來的request中的數據不在,所以如果要傳參數可以/item/queryItem.action后邊加參數:/test/queryTest?test1Key=test1Value&test2Key=test2Value
c)轉發:Controller中的方法執行后繼續執行另一個controller方法,如下信息modify提交后轉向到信息顯示頁面,修改信息的id參數可以帶到修改方法中。//結果轉發到update.action,request可以帶過去:return "forward:update.action";forward方式相當於“request.getRequestDispatcher().forward(request,response)”,轉發后瀏覽器地址欄還是原來的地址。轉發並沒有執行新的request和response,而是和轉發前的請求共用一個request和response。所以轉發前請求的參數在轉發后仍然可以讀取到。
四、關於SpringMVC的參數綁定問題
1、參數綁定過程
a)參數綁定:注解適配器對RequestMapping標記的方法進行適配,將從瀏覽器中請求的數據(key/value或者表單信息)在方法中的形參會進行參數綁定,所以在springmvc中的參數綁定是通過Controller的方法形參進行綁定的。
b)參數綁定所支持的默認參數類型,可以直接在Controller方法上面定義下面類型的形參,然后在方法體內直接使用
①HttpServletRequest(通過request對象獲取請求信息)
②HttpServletResponse(通過response處理響應信息)
③HTTPSession(通過session對象得到session中存放的對象)
④Model(通過model向頁面傳遞數據,然后頁面通過${test.XXXX}獲取item對象的屬性值。如同上面Controller中方法返回值為String的情況)
2、RequestParam注解使用
a)在沒有使用注解的時候,我們在前端提交參數的key名字應該Controller中方法的形參相同,否則在request域中無法進行匹配。當我們需要將Controller方法中的形參設置為不一樣的參數名時候,就需要使用這個注解
b)注解簡介:@RequestParam用於綁定單個請求參數。
①value:參數名字,即入參的請求參數名字,如value=“test_id”表示請求的參數區中的名字為test_id的參數的值將傳入;
②required:是否必須傳入參數,默認是true,表示請求中一定要有相應的參數,否則將報HTTP Status 400 - Required Integer parameter 'XXXX' is not present
③defaultValue:默認值,表示如果請求中沒有同名參數時的默認值
比如:形參名稱為id,但是這里使用value=" test_id"限定請求的參數名為test_id,所以頁面傳遞參數的名必須為test_id。
注意:如果請求參數中沒有test_id將拋出異常:HTTP Status 500 - Required Integer parameter 'test_id' is not present
這里通過required=true限定itest_id參數為必需傳遞,如果不傳遞則報400錯誤,可以使用defaultvalue設置默認值,即使required=true也可以不傳item_id參數值
c)下面使用例子來進行說明上面的幾點內容
①測試注解
測試工程如同上一篇SSM整合中搭建的工程,其中只有一個功能就是查詢列表。然后我們在本篇最開始的時候介紹RequestMapping時候添加了查詢詳細信息的功能,但是其中我們沒有接收前端傳入的數據,全部都是用的默認值1,下面來將這個方法使用RequestParam進行改寫,接收前端傳入的參數進行查詢。
1 @RequestMapping("/queryInfo.do") 2 public ModelAndView queryInfo(@RequestParam(value = "id") Integer testId) throws Exception { 3 //在沒有使用注解的時候,方法形參中的參數名需要和前端請求的key名稱一樣,使用之后就可以自定義 4 ProductExtend productExtend = productService.queryProductInfo(testId); 5 6 productExtend.setDesc("這是相機"); 7 ModelAndView modelAndView = new ModelAndView(); 8 modelAndView.addObject(productExtend); 9 modelAndView.setViewName("/WEB-INF/items/editItem.jsp"); 10 return modelAndView; 11 }
然后我們再次進行測試,首先訪問http://localhost:8080/TestSSM2/test/queryList.do,然后查看id=2的信息
得到下面的結果
在對比id=1的時候
②測試required
在上面的方法中加上
然后進行測試,輸入http://localhost:8080/TestSSM2/test/queryInfo.do,不加參數,則報出下面的錯誤
③測試defaultValue
當required設置為true的時候,如果沒有傳入key/value,當在Controller中設置defaultValue的時候也不會報出上面的異常,在Controller方法參數中改成下面這樣
然后直接輸入http://localhost:8080/TestSSM2/test/queryInfo.do不加id參數,還是能夠查詢到默認的id=1的數據
3、普通POJO類型綁定
a)將pojo對象中的屬性名和傳遞進來的屬性名對應,如果傳進來的參數名稱和對象中的屬性名稱一致則將參數值設置在pojo對象中 ,然后在Contrller方法定義: 請求的參數名稱和pojo的屬性名稱一致,會自動將請求參數賦值給pojo的屬性。
b)我們現在在Controller中新添加一個下面的方法,輸出從頁面上面提交的數據
c)在頁面表單中點擊提交數據
然后后台中輸出
4、自定義參數綁定
a)定義一個Date類型的參數綁定,首先建議里個轉換器的java工具類,用來將String轉換為java.util.Date類型
1 package cn.test.ssm.controller.converter; 2 3 4 import org.springframework.core.convert.converter.Converter; 5 6 import java.text.ParseException; 7 import java.text.SimpleDateFormat; 8 import java.util.Date; 9 10 public class StringToDateConverter implements Converter<String, Date> { 11 @Override 12 public Date convert(String s) { 13 14 try { 15 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 16 return simpleDateFormat.parse(s); 17 } catch (ParseException e) { 18 e.printStackTrace(); 19 } 20 return null; 21 } 22 }
然后在springmvc.xml中配置上上面的轉換器
1 <!--配置mvc:annotation代替基於注解方式的處理器映射器和適配器的配置--> 2 <mvc:annotation-driven conversion-service="converterService"></mvc:annotation-driven> 3 4 <bean id="converterService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> 5 <!--配置轉換器--> 6 <property name="converters"> 7 <list> 8 <bean class="cn.test.ssm.controller.converter.StringToDateConverter"></bean> 9 </list> 10 </property> 11 </bean>
最后可以修改一下上面的查看信息方法,在后台中打印出所有提交的數據,注意下面的方法中形參createtime的類型已經設置為Date類型,在前端頁面中也添加上了createtime的輸入
1 @RequestMapping("/printInfo.action") 2 public String printInfo(Product product, Date createtime) throws Exception { 3 System.out.println("輸出信息"+product); 4 System.out.println(createtime); 5 return "forward:queryList.do"; 6 }
最后在瀏覽器中進行如下輸入測試,輸入一個日期類型
最后在后台查看打印的信息
5、自定義包裝類型
a)當前天傳入的參數比較復雜的時候(比如說涵蓋不同數據實體類之間關聯的查詢或者某個實體類的擴展屬性信息),這個時候我們可以在擴展類中加上額外的屬性然后將其作為我們自定義的包裝類的屬性,然后將前台頁面傳入參數的name設置為包裝類的屬性名.實體類屬性(testExtend.name)
b)看下面的例子,這是一個模糊查詢的簡單功能實現
①我們首先在實體類Product的擴展類ProductExtend中添加接受模糊查詢參數的一個屬性
②然后定義一個包裝類型,其中將上面的擴展類型設置為一個屬性
③然后就在Controller方法中將自定義的包裝類型設置為方法形參,用以進行參數綁定,並且調用service的方法進行查詢
④在前台中查看查詢
⑤在debug模式中查看是否接收到前端傳入的參數,發現可以接收到頁面傳入的參數
並且在調用service方法之后的查詢結果也為
最終頁面顯示結果