本文主要是講解在Controller中的開發,主要的知識點有以下:javascript
@RequestMapping
詳解在SpringMVC的控制器中,若是沒有對編碼進行任何的操做,那麼獲取到的中文數據是亂碼!html
即便咱們在handle()方法中,使用request對象設置編碼也不行!緣由也很是簡單,咱們SpringMVC接收參數是經過控制器中的無參構造方法,再通過handle()方法的object對象來獲得具體的參數類型的。java
Struts2是使用攔截器來自動幫咱們完成中文亂碼的問題的。那麼SpringMVC做爲一個更增強大的框架,確定也有對應的方法來幫咱們完成中文亂碼問題!web
值得注意的是:該過濾編碼器只能解決POST的亂碼問題!ajax
咱們只須要在web.xml配置文件中設置過濾編碼器就好了!spring
<!-- 編碼過濾器 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
咱們在快速入門的例子中使用的是XML配置的方式來使用SpringMVC的,SpringMVC也可以支持註解。【我的很是喜歡註解的方式】json
咱們在使用Action的時候,要麼繼承着AbstractCommandController類,要麼顯示使用註解Controller接口。當咱們使用了註解之後就不用顯示地繼承或實現任何類了!數組
使用@Controller
這個註解,就代表這是一個SpringMVC的控制器!微信
@Controller public class HelloAction { }
固然了,如今Spring是不知道有這麼一個註解的,所以咱們須要在配置文件中配置掃描註解session
值得注意的是:在配置掃描路徑的時候,後面不要加.*
否則掃描不了,我不知道學Struts2仍是其餘的地方時候,習慣加了.*,因而就搞了好久!
<!--掃描註解,後面不要加.*--> <context:component-scan base-package="zhongfucheng"/>
在控制器中寫業務方法
@Controller public class HelloAction { /** * * @RequestMapping 表示只要是/hello.action的請求,就交由該方法處理。固然了.action能夠去掉 * @param model 它和ModelAndView相似,它這個Model就是把數據封裝到request對象中,咱們就能夠獲取出來 * @return 返回跳轉的頁面【真實路徑,就不用配置視圖解析器了】 * @throws Exception */ @RequestMapping(value="/hello.action") public String hello(Model model) throws Exception{ System.out.println("HelloAction::hello()"); model.addAttribute("message","你好"); return "/index.jsp"; } }
跳轉到index頁面,首頁獲得對應的值。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> 這是個人首頁 <br> ${message} </body> </html>
固然了,基於註解和基於XML來開發SpringMVC,都是經過映射器、適配器和視圖解析器的。 只是映射器、適配器略有不一樣。可是都是能夠省略的!
<!-- 基於註解的映射器(可選) --> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/> <!-- 基於註解的適配器(可選) --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/> <!-- 視圖解析器(可選) --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
更新:上邊的適配器和映射器只是Spring3.1版本以前使用的、3.1版本以後如今通常用如下的兩個
映射器: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 適配器: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
固然了,這上面兩個配置也可使用<mvc:annotation-driven>
>替代註解處理器和適配器的配置。
@RequestMapping可以控制請求路徑和請求方式!
到目前爲止,咱們都是一個控制器寫一個業務方法,這確定是不合理的。咱們在Struts2中一個Action就對應多個業務方法了。那麼咱們在SpringMVC中又怎麼寫呢???
其實咱們能夠推理出來,@RequestMapping
就是用於配置哪一個請求對應哪一個業務方法的!
public @interface RequestMapping { String[] value() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; }
當咱們請求hello.action的時候,處理的業務方法是hello().....當咱們請求bye.action的時候,處理的業務方法是bye()
@Controller public class HelloAction { /** * * @RequestMapping 表示只要是/hello.action的請求,就交由該方法處理。固然了.action能夠去掉 * @param model 它和ModelAndView相似,它這個Model就是把數據封裝到request對象中,咱們就能夠獲取出來 * @return 返回跳轉的頁面【真實路徑,就不用配置視圖解析器了】 * @throws Exception */ @RequestMapping(value="/hello.action") public String hello(Model model) throws Exception{ System.out.println("HelloAction::hello()"); model.addAttribute("message","你好"); return "/index.jsp"; } @RequestMapping(value = "/bye.action") public String bye(Model model) throws Exception { model.addAttribute("message","再見"); return "/index.jsp"; } }
固然了,咱們在Struts2經常使用namespace來進行分模塊開發,在SpringMVC中咱們也能夠這樣幹,而且咱們又是使用的是@RequestMapping
這個註解!
只要把@RequestMapping
這個註解寫到類上面去,就表明了分模塊。
@Controller //咱們知道,若是是value屬性上的註解,咱們能夠把value省略掉的 @RequestMapping("/zhongfucheng") public class HelloAction { /** * @param model 它和ModelAndView相似,它這個Model就是把數據封裝到request對象中,咱們就能夠獲取出來 * @return 返回跳轉的頁面【真實路徑,就不用配置視圖解析器了】 * @throws Exception * @RequestMapping 表示只要是/hello.action的請求,就交由該方法處理。固然了.action能夠去掉 */ @RequestMapping(value = "/hello.action") public String hello(Model model) throws Exception { System.out.println("HelloAction::hello()"); model.addAttribute("message", "你好"); return "/index.jsp"; } @RequestMapping(value = "/bye.action") public String bye(Model model) throws Exception { model.addAttribute("message", "再見"); return "/index.jsp"; } }
那麼咱們想要HelloAction該控制器處理咱們的請求,訪問的地址要麼是:http://localhost:8080/zhongfucheng/hello.action
,或者要麼是http://localhost:8080/zhongfucheng/bye.action
咱們若是想要限定某個業務控制方法,只容許GET或POST請求方式訪問。仍是經過@RequestMapping
來實現。只要設定它的method屬性就好了!
@RequestMapping(value = "/bye.action",method = RequestMethod.POST) public String bye(Model model) throws Exception { model.addAttribute("message", "再見"); return "/index.jsp"; }
當我把業務方法的請求設置爲POST之後,我想要經過GET方式來訪問該業務方法。就行不通了!
咱們的業務方法除了能夠寫Model這個參數之外,若是有須要咱們還能夠寫request,response等傳統Servlet的參數。這是同樣可使用的....
可是呢,咱們並不建議使用傳統的web參數,由於會耦合
@RequestMapping(method=RequestMethod.POST,value="/register") public String registerMethod(HttpServletRequest request,HttpServletResponse response) throws Exception{ //獲取用戶名和薪水 String username = request.getParameter("username"); String salary = request.getParameter("salary"); System.out.println("用戶註冊-->" + username + ":" + salary); //綁定到session域對象中 request.getSession().setAttribute("username",username); request.getSession().setAttribute("salary",salary); //重定向/jsp/success.jsp頁面 //response.sendRedirect(request.getContextPath()+"/jsp/success.jsp"); //轉發/jsp/ok.jsp頁面 request.getRequestDispatcher("/jsp/ok.jsp").forward(request,response); //轉發(提倡) return "/jsp/success.jsp"; }
小細節:若是咱們的返回值是返回一個真實路徑,而咱們在程序中又使用了轉發或重定向。。。那麼具體跳轉的位置就是按咱們程序中跳轉的路徑爲準!
咱們在Struts2中收集web端帶過來的參數是在控制器中定義成員變量,該成員變量的名字與web端帶過來的名稱是要一致的...而且,給出該成員變量的set方法,那麼Struts2的攔截器就會幫咱們自動把web端帶過來的參數賦值給咱們的成員變量....
那麼在SpringMVC中是怎麼收集參數的呢????咱們SpringMVC是不可能跟Struts2同樣定義成員變量的,由於SpringMVC是單例的,而Struts2是多例的。所以SpringMVC是這樣乾的:
若是是普通參數的話,咱們直接在方法上寫上與web端帶過來名稱相同的參數就好了!
<form action="${pageContext.request.contextPath}/hello.action" method="post"> <table align="center"> <tr> <td>用戶名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>編號</td> <td><input type="text" name="id"></td> </tr> <tr> <td colspan="2"> <input type="submit" value="提交"> </td> </tr> </table> </form>
@RequestMapping(value = "/hello.action") public String hello(Model model, String username, int id) throws Exception { System.out.println("用戶名是:" + username); System.out.println("編號是:" + id); model.addAttribute("message", "你好"); return "/index.jsp"; }
效果:
咱們處理表單的參數,若是表單帶過來的數據較多,咱們都是用JavaBean對其進行封裝的。那麼咱們在SpringMVC也是能夠這麼作的。
建立JavaBean,javaBean屬性與表單帶過來的名稱相同
public class User { private String id; private String username; public User() { } public User(String id, String username) { this.id = id; this.username = username; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String toString() { return "User{" + "id='" + id + '\'' + ", username='" + username + '\'' + '}'; } }
在業務方法參數上寫入Javabean
@RequestMapping(value = "/hello.action") public String hello(Model model,User user) throws Exception { System.out.println(user); model.addAttribute("message", "你好"); return "/index.jsp"; }
收集數組和收集普通的參數是相似的,看了如下的代碼就懂了。
<form action="${pageContext.request.contextPath}/hello.action" method="post"> <table align="center"> <tr> <td>用戶名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>愛好</td> <td><input type="checkbox" name="hobby" value="1">籃球</td> <td><input type="checkbox" name="hobby" value="2">足球</td> <td><input type="checkbox" name="hobby" value="3">排球</td> <td><input type="checkbox" name="hobby" value="4">羽毛球</td> </tr> <tr> <td colspan="2"> <input type="submit" value="提交"> </td> </tr> </table> </form>
業務方法獲取參數
@RequestMapping(value = "/hello.action") public String hello(Model model,int[] hobby) throws Exception { for (int i : hobby) { System.out.println("喜歡運動的編號是:" + i); } model.addAttribute("message", "你好"); return "/index.jsp"; }
效果:
List<JavaBean>
集合咱們在Spring的業務方法中是不能夠用List
咱們使用一個JavaBean把集合封裝起來,給出對應的set和get方法。那麼咱們在接收參數的時候,接收的是JavaBean
/** * 封裝多個Emp的對象 * @author AdminTC */ public class Bean { private List<Emp> empList = new ArrayList<Emp>(); public Bean(){} public List<Emp> getEmpList() { return empList; } public void setEmpList(List<Emp> empList) { this.empList = empList; } }
業務方法接收JavaBean對象
/** * 批量添加員工 */ @RequestMapping(value="/addAll",method=RequestMethod.POST) public String addAll(Model model,Bean bean) throws Exception{ for(Emp emp:bean.getEmpList()){ System.out.println(emp.getUsername()+":"+emp.getSalary()); } model.addAttribute("message","批量增長員工成功"); return "/jsp/ok.jsp"; }
在JSP頁面直接寫上empList[下表].
<form action="${pageContext.request.contextPath}/emp/addAll.action" method="POST"> <table border="2" align="center"> <caption><h2>批量註冊員工</h2></caption> <tr> <td><input type="text" name="empList[0].username" value="哈哈"/></td> <td><input type="text" name="empList[0].salary" value="7000"/></td> </tr> <tr> <td><input type="text" name="empList[1].username" value="呵呵"/></td> <td><input type="text" name="empList[1].salary" value="7500"/></td> </tr> <tr> <td><input type="text" name="empList[2].username" value="班長"/></td> <td><input type="text" name="empList[2].salary" value="8000"/></td> </tr> <tr> <td><input type="text" name="empList[3].username" value="鍵狀哥"/></td> <td><input type="text" name="empList[3].salary" value="8000"/></td> </tr> <tr> <td><input type="text" name="empList[4].username" value="綠同窗"/></td> <td><input type="text" name="empList[4].salary" value="9000"/></td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="批量註冊"/> </td> </tr> </table> </form>
其實這種方法看起來也沒有那麼難理解,咱們就是向上封裝了一層【與接收普通的JavaBean相似的】。
咱們有可能在JSP頁面上即有User模型的數據要收集,又有Emp模型的數據要收集....而且User模型的屬性和Emp模型的屬性如出一轍....此時咱們該怎麼辦呢???
咱們也是能夠在User模型和Emp模型上向上抽象出一個Bean,該Bean有Emp和User對象
/** * 封裝User和Admin的對象 * @author AdminTC */ public class Bean { private User user; private Admin admin; public Bean(){} public User getUser() { return user; } public void setUser(User user) { this.user = user; } public Admin getAdmin() { return admin; } public void setAdmin(Admin admin) { this.admin = admin; } }
在JSP頁面收集的時候,給出對應的類型就好了。
<form action="${pageContext.request.contextPath}/person/register.action" method="POST"> <table border="2" align="center"> <tr> <th>姓名</th> <td><input type="text" name="user.username" value="${user.username}"/></td> </tr> <tr> <th>月薪</th> <td><input type="text" name="user.salary" value="${user.salary}"></td> </tr> <tr> <th>入職時間</th> <td><input type="text" name="user.hiredate" value='<fmt:formatDate value="${user.hiredate}" type="date" dateStyle="default"/>'/></td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="普通用戶註冊" style="width:111px"/> </td> </tr> </table> </form>
咱們在Struts2中,若是web端傳過來的字符串類型是yyyy-mm-dd hh:MM:ss這種類型的話,那麼Struts2默認是能夠自動解析成日期的,若是是別的字符串類型的話,Struts2是不能自動解析的。要麼使用自定義轉換器來解析,要麼就本身使用Java程序來解析....
而在SpringMVC中,即便是yyyy-mm-dd hh:MM:ss這種類型SpringMVC也是不能自動幫咱們解析的。咱們看以下的例子:
JSP傳遞關於日期格式的字符串給控制器...
<form action="${pageContext.request.contextPath}/hello.action" method="post"> <table align="center"> <tr> <td>用戶名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>出生日期</td> <td><input type="text" name="date" value="1996-05-24"></td> </tr> <tr> <td colspan="2"> <input type="submit" value="提交"> </td> </tr> </table> </form>
User對象定義Date成員變量接收
public Date getDate() { return date; } public void setDate(Date date) { this.date = date; }
業務方法獲取Date值
@RequestMapping(value = "/hello.action") public String hello(Model model, User user) throws Exception { System.out.println(user.getUsername() + "的出生日期是:" + user.getDate()); model.addAttribute("message", "你好"); return "/index.jsp"; }
結果出問題了,SpringMVC不支持這種類型的參數:
如今問題就拋出來了,那咱們要怎麼解決呢????
SpringMVC給出相似於Struts2類型轉換器這麼一個方法給咱們使用:若是咱們使用的是繼承AbstractCommandController類來進行開發的話,咱們就能夠重寫initBinder()方法了....
具體的實現是這樣子的:
@Override protected void initBinder(HttpServletRequest request,ServletRequestDataBinder binder) throws Exception { binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true)); }
那咱們如今用的是註解的方式來進行開發,是沒有重寫方法的。所以咱們須要用到的是一個註解,代表我要重寫該方法!
@InitBinder protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception { binder.registerCustomEditor( Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true)); }
再次訪問:
值得注意的是:若是咱們使用的是Oracle插入時間的話,那麼咱們在SQL語句就要寫TimeStrap時間戳插入進去,不然就行不通!
咱們通常作開發的時候,常常編輯完數據就返回到顯示列表中。咱們在Struts2是使用配置文件進行重定向或轉發的:
而咱們的SpringMVC就很是簡單了,只要在跳轉前寫上關鍵字就好了!
public String hello(Model model, User user) throws Exception { System.out.println(user.getUsername() + "的出生日期是:" + user.getDate()); model.addAttribute("message", user.getDate()); return "redirect:/index.jsp"; }
以此類推,若是是想要再次請求的話,那麼咱們只要寫上對應的請求路徑就好了!
@RequestMapping(value = "/hello.action") public String hello(Model model, User user) throws Exception { return "redirect:/bye.action"; } @RequestMapping("/bye.action") public String bye() throws Exception { System.out.println("我進來了bye方法"); return "/index.jsp"; }
回顧一下Struts2返回JSON文本是怎麼操做的:
那麼咱們在SpringMVC又怎麼操做呢???
導入兩個JSON開發包
在要返回JSON的業務方法上給上註解:
@RequestMapping(value = "/hello.action") public @ResponseBody User hello() throws Exception { User user = new User("1", "zhongfucheng"); return user; }
配置JSON適配器
<!-- 1)導入jackson-core-asl-1.9.11.jar和jackson-mapper-asl-1.9.11.jar 2)在業務方法的返回值和權限之間使用@ResponseBody註解表示返回值對象須要轉成JSON文本 3)在spring.xml配置文件中編寫以下代碼: --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/> </list> </property> </bean>
測試的JSP
<input type="button" value="Emp轉JSON"/><p> <input type="button" value="List<Emp>轉JSON"/><p> <input type="button" value="Map<String,Object>轉JSON"/><p> <!-- Map<String,Object>轉JSON --> <script type="text/javascript"> $(":button:first").click(function(){ var url = "${pageContext.request.contextPath}/hello.action"; var sendData = null; $.post(url,sendData,function(backData,textStaut,ajax){ alert(ajax.responseText); }); }); </script>
測試:
Map測試:
@RequestMapping(value = "/hello.action") public @ResponseBody Map hello() throws Exception { Map map = new HashMap(); User user = new User("1", "zhongfucheng"); User user2 = new User("12", "zhongfucheng2"); map.put("total", user); map.put("rows", user2); return map; }
更新------------------------------------------------------------------
若是傳遞進來的數據就是JSON格式的話,咱們咱們須要使用到另一個註解@RequestBody
,將請求的json數據轉成java對象
@InitBinder
註解來重寫方法。@ResponseBody
註解,若是接收JSON數據封裝成JavaBean的話,咱們就須要用到@RequestBody
註解。隨後在配置文件上建立對應的bean便可。若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y