框架原理
web層的知識結構導圖
SpringMVC的各種參數綁定方式
1. 基本數據類型(以int爲例,其他類似):
Controller代碼:
@RequestMapping("saysth.do")
public void test(int count) {
}
表單代碼:
<form action="saysth.do" method="post">
<input name="count" value="10" type="text"/>
......
</form>
表單中input的name值和Controller的參數變量名保持一致,就能完成數據綁定,如果不一致可以使用@RequestParam註解。需要注意的是,如果Controller方法參數中定義的是基本數據類型,但是從頁面提交過來的數據爲null或者”"的話,會出現數據轉換的異常。也就是必須保證表單傳遞過來的數據不能爲null或”",所以,在開發過程中,對可能爲空的數據,最好將參數數據類型定義成包裝類型,具體參見下面的例子。
2. 包裝類型(以Integer爲例,其他類似):
Controller代碼:
@RequestMapping("saysth.do")
public void test(Integer count) {
}
表單代碼:
<form action="saysth.do" method="post">
<input name="count" value="10" type="text"/>
......
</form>
和基本數據類型基本一樣,不同之處在於,表單傳遞過來的數據可以爲null或”",以上面代碼爲例,如果表單中num爲”"或者表單中無num這個input,那麼,Controller方法參數中的num值則爲null。
3. 自定義對象類型:
Model代碼:
public class User {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Controller代碼:
@RequestMapping("saysth.do")
public void test(User user) {
}
表單代碼:
<form action="saysth.do" method="post">
<input name="firstName" value="張" type="text"/>
<input name="lastName" value="三" type="text"/>
......
</form>
非常簡單,只需將對象的屬性名和input的name值一一匹配即可。
4. 自定義複合對象類型:
Model代碼:
public class ContactInfo {
private String tel;
private String address;
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public class User {
private String firstName;
private String lastName;
private ContactInfo contactInfo;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public ContactInfo getContactInfo() {
return contactInfo;
}
public void setContactInfo(ContactInfo contactInfo) {
this.contactInfo = contactInfo;
}
}
Controller代碼:
@RequestMapping("saysth.do")
public void test(User user) {
System.out.println(user.getFirstName());
System.out.println(user.getLastName());
System.out.println(user.getContactInfo().getTel());
System.out.println(user.getContactInfo().getAddress());
}
表單代碼:
<form action="saysth.do" method="post">
<input name="firstName" value="張" /><br>
<input name="lastName" value="三" /><br>
<input name="contactInfo.tel" value="13809908909" /><br>
<input name="contactInfo.address" value="北京海淀" /><br>
<input type="submit" value="Save" />
</form>
User對象中有ContactInfo屬性,Controller中的代碼和第3點說的一致,但是,在表單代碼中,需要使用“屬性名(對象類型的屬性).屬性名”來命名input的name。
總結 :
1.controller中的方法主要是用於參數 可以是簡單類型,pojo,包裝的pojo, 也可以是Httpsession, HttpServletRequest,HttpServletResponse,Model
Controller方法返回值
返回void(和在servlet中一樣)
在Controller方法形參上可以義request和response,使用request或response指定響應結果:
1、使用request轉發頁面,如下:
request.getRequestDispatcher("頁面路徑").forward(request, response);
request.getRequestDispatcher("/WEB-INF/jsp/success.jsp").forward(request, response);
2、可以通過response頁面重定向:
response.sendRedirect("url")
response.sendRedirect("/springmvc-web2/itemEdit.action");
- 可以通過response指定響應結果,例如響應json數據如下:
response.getWriter().print("{\"abc\":123}");
返回字符串(轉發或是重定向寫在字符串中)
Redirect重定向
Contrller方法返回字符串可以重定向到一個url地址
如下商品修改提交後重定向到商品編輯頁面。
/**
* 更新商品
*
* @param item
* @return
*/
@RequestMapping("updateItem")
public String updateItemById(Item item) {
// 更新商品
this.itemService.updateItemById(item);
// 修改商品成功後,重定向到商品編輯頁面
// 重定向後瀏覽器地址欄變更爲重定向的地址,
// 重定向相當於執行了新的request和response,所以之前的請求參數都會丟失
// 如果要指定請求參數,需要在重定向的url後面添加 ?itemId=1 這樣的請求參數
return "redirect:/itemEdit.action?itemId=" + item.getId();
}
forward轉發
Controller方法執行後繼續執行另一個Controller方法
如下商品修改提交後轉向到商品修改頁面,修改商品的id參數可以帶到商品修改方法中。
/**
* 更新商品
*
* @param item
* @return
*/
@RequestMapping("updateItem")
public String updateItemById(Item item) {
// 更新商品
this.itemService.updateItemById(item);
// 修改商品成功後,重定向到商品編輯頁面
// 重定向後瀏覽器地址欄變更爲重定向的地址,
// 重定向相當於執行了新的request和response,所以之前的請求參數都會丟失
// 如果要指定請求參數,需要在重定向的url後面添加 ?itemId=1 這樣的請求參數
// return "redirect:/itemEdit.action?itemId=" + item.getId();
// 修改商品成功後,繼續執行另一個方法
// 使用轉發的方式實現。轉發後瀏覽器地址欄還是原來的請求地址,
// 轉發並沒有執行新的request和response,所以之前的請求參數都存在
return "forward:/itemEdit.action";
}
//結果轉發到editItem.action,request可以帶過去
return "forward: /itemEdit.action";
異常處理器
springmvc在處理請求過程中出現異常信息交由異常處理器進行處理,自定義異常處理器可以實現一個系統的異常處理邏輯。
-
- 自定義異常類
爲了區別不同的異常,通常根據異常類型進行區分,這裏我們創建一個自定義系統異常。
如果controller、service、dao拋出此類異常說明是系統預期處理的異常信息。
-
- 自定義異常處理器
public class CustomHandleException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) {
// 定義異常信息
String msg;
// 判斷異常類型
if (exception instanceof MyException) {
// 如果是自定義異常,讀取異常信息
msg = exception.getMessage();
} else {
// 如果是運行時異常,則取錯誤堆棧,從堆棧中獲取異常信息
Writer out = new StringWriter();
PrintWriter s = new PrintWriter(out);
exception.printStackTrace(s);
msg = out.toString();
}
// 把錯誤信息發給相關人員,郵件,短信等方式
// TODO
// 返回錯誤頁面,給用戶友好頁面顯示錯誤信息
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", msg);
modelAndView.setViewName("error");這個view是自己需要寫好的嗎??
return modelAndView;
}
}
-
- 異常處理器配置
在springmvc.xml中添加:
<!-- 配置全局異常處理器 -->
<bean
id="customHandleException" class="cn.itcast.ssm.exception.CustomHandleException"/>
</bean>
異常測試
修改ItemController方法“queryItemList”,拋出異常:
/**
* 查詢商品列表
*
* @return
* @throws Exception
*/
@RequestMapping(value = { "itemList", "itemListAll" })
public ModelAndView queryItemList() throws Exception {
// 自定義異常
if (true) {
throw new MyException("自定義異常出現了~");
}
// 運行時異常
int a = 1 / 0;
// 查詢商品數據
List<Item> list = this.itemService.queryItemList();
// 創建ModelAndView,設置邏輯視圖名
ModelAndView mv = new ModelAndView("itemList");
// 把商品數據放到模型中
mv.addObject("itemList", list);
return mv;
}
上傳圖片
在tomcat上配置圖片虛擬目錄
訪問http://localhost:8080/pic圖片名 測試效果
在springmvc.xml中配置文件上傳解析器
<!-- 文件上傳,id必須設置爲multipartResolver -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 設置文件上傳大小 -->
<property name="maxUploadSize" value="5000000" />
</bean>
-
- 圖片上傳
在更新商品方法中添加圖片上傳邏輯
-
- @RequestBody
作用:
@RequestBody註解用於讀取http請求的內容(字符串),通過springmvc提供的HttpMessageConverter接口將讀到的內容(json數據)轉換爲java對象並綁定到Controller方法的參數上。(之前傳入的都是字符串的形式的json數據 這裏一定要是json對象的數據)
-
- @ResponseBody
作用:
@ResponseBody註解用於將Controller的方法返回的對象,通過springmvc提供的HttpMessageConverter接口轉換爲指定格式的數據如:json,xml等,通過Response響應給客戶端
-
-
- ItemController編寫
-
/**
* 測試json的交互
* @param item
* @return
*/
@RequestMapping("testJson")
// @ResponseBody
public @ResponseBody Item testJson(@RequestBody Item item) {
return item;
}
<mvc:annotation-driven />:解析:@RequestMapping("testJson") @ResponseBody @RequestBody 等註解
總結:
我們前臺請求過去的json字符串,在使用@RequestBody註解後 被HttpMessageConverter,轉換成對應的java對象,然後我們在用@ResponseBody註解,將java對象 以特定的格式(通常都是json)相應給瀏覽器。
關於@ResponseBody與@RequestBody註解的用法可以參考這篇博客:https://blog.csdn.net/weixin_43732955/article/details/92843116
攔截器
實現HandlerInterceptor接口,如下:
public class HandlerInterceptor1 implements HandlerInterceptor {
// controller執行後且視圖返回後調用此方法
// 這裏可得到執行controller時的異常信息
// 這裏可記錄操作日誌
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("HandlerInterceptor1....afterCompletion");
}
// controller執行後但未返回視圖前調用此方法
// 這裏可在返回用戶前對模型數據進行加工處理,比如這裏加入公用信息以便頁面顯示
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("HandlerInterceptor1....postHandle");
}
// Controller執行前調用此方法
// 返回true表示繼續執行,返回false中止執行
// 這裏可以加入登錄校驗、權限攔截等
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("HandlerInterceptor1....preHandle");
// 設置爲true,測試使用
return true;
}
}
-
- 攔截器配置
上面定義的攔截器再複製一份HandlerInterceptor2,注意新的攔截器修改代碼:
System.out.println("HandlerInterceptor2....preHandle");
在springmvc.xml中配置攔截器
<!-- 配置攔截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 所有的請求都進入攔截器 -->
<mvc:mapping path="/**" />
<!-- 配置具體的攔截器 -->
<bean class="cn.itcast.ssm.interceptor.HandlerInterceptor1" />
</mvc:interceptor>
<mvc:interceptor>
<!-- 所有的請求都進入攔截器 -->
<mvc:mapping path="/**" />
<!-- 配置具體的攔截器 -->
<bean class="cn.itcast.ssm.interceptor.HandlerInterceptor2" />
</mvc:interceptor>
</mvc:interceptors>
日期轉換問題
前端傳過來是string類型 但是pojo中是date 如何進行對象映射
(一般pojo也會是string類型不會寫成date)
由於日期數據有很多種格式,springmvc沒辦法把字符串轉換成日期類型。所以需要自定義參數綁定。
前端控制器接收到請求後,找到註解形式的處理器適配器,對RequestMapping標記的方法進行適配,並對方法中的形參進行參數綁定。可以在springmvc處理器適配器上自定義轉換器Converter進行參數綁定。
一般使用<mvc:annotation-driven/>註解驅動加載處理器適配器,可以在此標籤上進行配置。
-
-
- 修改jsp頁面
-
如下圖修改itemEdit.jsp頁面,顯示時間
-
-
- 自定義Converter
-
//Converter<S, T>
//S:source,需要轉換的源的類型
//T:target,需要轉換的目標類型
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
// 把字符串轉換爲日期類型
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(source);
return date;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 如果轉換異常則返回空
return null;
}
}
-
-
- 配置Converter
-
我們同時可以配置多個的轉換器。
類似下圖的usb設備,可以接入多個usb設備
<!-- 配置註解驅動 -->
<!-- 如果配置此標籤,可以不用配置... -->
<mvc:annotation-driven conversion-service="conversionService" />
<!-- 轉換器配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="cn.itcast.springmvc.converter.DateConverter" />
</set>
</property>
</bean>