在传统的Spring MVC开发方法中,必须在Bean配置文件中为每个控制器类配置实例和请求映射和让每个控制器类去实现或者扩展特定于框架的接口或者基类,不够灵活。
如果Spring MVC可以自动侦测你的控制器类和请求映射,就能减少配置所需要的工作量。
Spring2.5支持一种基于注解的控制器开发方法。
Spring可以通过@Controller注解自动发现你的控制器类以及@RequestMapping注解中的请求映射,这样就为你免去了在Bean配置文件中配置它们的麻烦。此外,如果使用注解,控制器类和处理程序方法在访问上下文资源(例如请求参数、模型属性和会话属性)时也会更加灵活。
常用到的注解
处理程序方法的签名非常灵活。你可以为处理程序方法指定任意的名称,并定义以下任意一种类型作为它的方法参数。在这里,只提到了常见的参数类型。关于有效参数类型的完整列表,请参阅有关配置基于注解的控制器的Spring文档。
常见的参数类型
1.HttpServletRequest、HttpServletResponse或HttpSession。
2.添加了@RequestParam注解的任意类型的请求参数
3.添加了@ModelAttribute注解的任意类型的模型属性
4.任意类型的命令对象,供Spring绑定请求参数
5.Map或者ModelMap,供处理程序方法向模型添加属性
6.Errors或者BindingResult,让处理程序方法访问命令对象的绑定和验证结果
7.SessionStatus,让处理程序方法发出会话处理已经完成的通知
常见的返回值类型
处理程序方法的返回类型可以是ModelAndView、Model、Map、String、void
在创建基于注解的控制器之前,必须构建web应用程序上下文来处理注解。
首先,为了让Spring用@Controller注解自动侦测控制器,必须通过<context:component-scan>元素启用Spring的组件扫描特性。
其次Spring MVC还能够根据@RequestMapping将请求映射到控制器类和处理程序方法。
为了使其生效,必须在web应用程序上下文中注册DefaultAnnotationHandlerMapping实例和AnnotationMethodHandlerAdapter实例。
它们分别处理在类级别和方法级别上的@RequestMapping注解。
必要的Spring MVC配置
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc/spring-mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- 自动扫描注解的Controller --> <context:component-scan base-package="com.wy.controller.annotation" /> <!-- 处理在类级别上的@RequestMapping注解--> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" /> <!-- 处理方法级别上的@RequestMapping注解--> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> <!-- 视图解析器策略 和 视图解析器 --> <!-- 对JSTL提供良好的支持 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 默认的viewClass,可以不用配置 <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView" /> --> <property name="prefix" value="/WEB-INF/page/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter是默认在Web应用程序上下文中预先注册好的。然而,如果你还显式地注册了其他的处理程序映射或者处理程序适配器,它们就不会自动注册了。在这种情况下,你必须亲自注册它们。
基于注解的控制器类可以是个任意类,不实现特殊接口,也不扩展特殊的基类。你只要用@Controller注解对它进行标注即可。还可以在控制器中定义一个或者多个处理程序方法来处理单个或者多个动作。处理程序方法的签名很灵活,足以接受一系列参数。
@RequestMapping注解可以被应用到类级别或者方法级别上。
Controller层:代码中写了很详细的注释
package com.wy.controller.annotation; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.support.WebRequestDataBinder; import org.springframework.web.servlet.ModelAndView; import com.wy.pojo.User; /** * @author Administrator * @version 2011-12-3 */ @Controller @RequestMapping("userManagerContoller") //指定请求路径,相对路径可以不定义 public class UserManagerContoller { /** * 欢迎 * @return */ /* 1.传统的获取请求的参数方式 * http://localhost:8080/SpringMVC/userManagerContoller/welcome.do?name=wy */ @RequestMapping("/welcome") public ModelAndView welcome(HttpServletRequest request){ ModelAndView mav = new ModelAndView(); String name = request.getParameter("name"); Date today = new Date(); String date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(today); mav.addObject("today", date); mav.addObject("name", name); mav.setViewName("welcome"); return mav; } /* 2.restful风格获取请求的参数方式 Spring3.0的一个重要变化(将参数放到了请求路径中) * http://localhost:8080/SpringMVC/userManagerContoller/welcome/wy.do * * 注意点: JVM将Java文件编译成Class文件有两种模式 Debug 和Release * 这两种编译方式的区别是: * Debug 包含额外的调试信息,可以完整的保留变量的名称 (Eclipse 使用的是Debug) * Release 把变量名称使用其他的一些符号代替,量名称就不可见啦 (在使用 javac命令) */ @RequestMapping("/welcome/{param}/{sex}") //前一个“param是防止Release编译下找不到参数名称,因此要指定;要和模板中定义的参数名称一致 //后面一个“param”可以和模板中定义的参数名称不一致,建议还是一致 public ModelAndView welcome(@PathVariable("param") String param, @PathVariable("sex") String xingbie){ ModelAndView mav = new ModelAndView(); mav.addObject("name", param); mav.addObject("sex", xingbie); Date today = new Date(); String date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(today); mav.addObject("today", date); mav.setViewName("welcome"); return mav; } /** * 3.不同请求方式(get,post),映射不同的方法 * * value 指定请求路径,method 指定请求方式 */ @RequestMapping(value="/welcome", method=RequestMethod.GET) public ModelAndView requestMethodGet(){ ModelAndView mav = new ModelAndView(); mav.setViewName("welcome"); return mav; } @RequestMapping(value="/hello", method=RequestMethod.POST) public ModelAndView requestMethodPost(){ ModelAndView mav = new ModelAndView(); mav.setViewName("hello"); return mav; } /** * 4. @RequestParam 使用方法和@PathVariable类似(要注意Debug和Release) * http://localhost:8080/SpringMVC/userManagerContoller/welcomeParam.do?username=wy&password=123&age=23 */ @RequestMapping(value="/welcomeParam", method=RequestMethod.GET) public ModelAndView welcome(@RequestParam("username") String username, @RequestParam("password") String password, @RequestParam("age") int age) { ModelAndView mav = new ModelAndView(); User user = new User(); user.setUsername(username); user.setPassword(password); user.setAge(age); mav.addObject("user", user); mav.setViewName("hello"); return mav; } /** * 5.获取表单中的值 * BindingResult 绑定数据过程中产生的错误注入到BindingResult中。 */ @RequestMapping(value="/welcome", method=RequestMethod.POST) public ModelAndView commonCommod(User user, BindingResult result){ ModelAndView mav = new ModelAndView(); mav.addObject(user); if(result.hasErrors() && result.hasFieldErrors()){ String field = null; Object fieldValue = null; Map<String, Object> map = new HashMap<String, Object>(); List<FieldError> fieldErrors = result.getFieldErrors(); for(FieldError fieldError : fieldErrors){ field = fieldError.getField(); fieldValue = fieldError.getRejectedValue(); map.put(field, fieldValue); } mav.addObject("map", map); mav.setViewName("welcome"); }else{ mav.setViewName("hello"); } return mav; } /** * 属性编辑器 类型转换 * 典型应用: 日期转换 */ @InitBinder public void initBinder(WebRequestDataBinder binder){ binder.registerCustomEditor(Date.class, new CustomDateEditor( new SimpleDateFormat("yyyy-MM-dd"),false)); } public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception{ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true)); } /** * 6.常用的参数 * 使用Session有一个前提就是session必须是可用的 */ // public ModelAndView commonArguments(HttpServletRequest request, HttpServletResponse response, HttpSession session, // @CookieValue AnyType cookieName, @RequestHeader("user-Agent") AnyType name, // @PathVariable AnyType variableName, @RequestParam AnyType paramName){ // ModelAndView mav = new ModelAndView(); // // return mav; // } /** * 7.返回类型 * void 、String、AnyType(任意对象)、Model、ModelAndView * 说明:Model继承了Map,为SpringMVC定制的 */ // void @RequestMapping public void commonReturnType(HttpServletResponse response){ try { PrintWriter out = response.getWriter(); out.println("向页面中输出的值"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @RequestMapping public void commonReturnType(PrintWriter out){//其实也是从HttpServletResponse 通过getWriter()得到out out.println("向页面中输出的值"); } @RequestMapping("/commonReturnType") public void commonReturnType(){ //默认生成隐含的viewName(规则测略:按照请求路径${appName/userManagerContoller/commonReturnType.do // ---> userManagerContoller/commonReturnType} // ---> /WEB-INF/page/userManagerContoller/commonReturnType.jsp // ) } // String /** * ModelAndView中的Model * ModelAndView中的View */ @RequestMapping public String commonReturnType(Map<Object, Object> model){//model model.put("", ""); return "viewName"; } //AnyType(任意对象) /** * user放到model中,model(key, value) key默认是取Bean的名称将其首字母小写 即user,value即user * 默认生成隐含的viewName * 在页面上可以使用request.getAttribute("user")或者${user.key} ${user.value} * @return */ @RequestMapping public User commonReturnTypeUser(){// User user = null; return user; } /** * userList放到model中,model(key, value) key默认是框架生成userList,value即user * 默认生成隐含的viewName * 在页面上可以使用request.getAttribute("userList")或者${userList.key} ${userList.value} * @return */ @RequestMapping public List<User> commonReturnTypeUserList(){ List<User> userList = null; return userList; } /** * * 默认生成隐含的viewName * @return */ @RequestMapping public Model commonReturnTypeModel(){ Model model = null; return model; } /** * * @return */ @RequestMapping public ModelAndView commonReturnTypeModelAndView(){ ModelAndView mav = new ModelAndView(); mav.addObject("", ""); mav.setViewName(""); return mav; } }
view层
welcome.jsp
<%@ page language="java" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <% String path = request.getContextPath(); %> <!DOCTYPE html> <html> <head> <title>welcome.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css">--> </head> <body> This is my annotation HTML page. <br/> 今天是: ${today}<br/> 参数是: ${name} ${sex}<br/> <hr/> <c:forEach var="field" items="${map}"> <c:if test="${field != null}" var="param" scope="page"> 您输入的${field.key}不正确! ${field.value}<br/> </c:if> </c:forEach> <form action="<%=path%>/userManagerContoller/welcome.do" method="post"> 用户名: <input type="text" id="username" name="username" value="wy" /><br/> 密码 : <input type="text" id="password" name="password" value="wy" /><br/> 年龄 : <input type="text" id="age" name="age" value="23wy" /><br/> <input type="submit" value="提交" /> </form> </body> </html>
值得注意的点:
1、@PathVariable("paramName") @RequestParam("paramName") 建议指定参数名称
原因是VM将Java文件编译成Class文件有两种模式 Debug 和Release
这两种编译方式的区别是:
Debug 包含额外的调试信息,可以完整的保留变量的名称 (Eclipse 使用的是Debug)
Release 把变量名称使用其他的一些符号代替,量名称就不可见啦 (在使用 javac命令)
2、restful风格获取请求的参数方式
3、参数类型转换
注册属性编辑器
4、对于无任何输出的方法
@RequestMapping("/commonReturnType") public void commonReturnType(){ //默认生成隐含的viewName(规则测略:按照请求路径${appName/userManagerContoller/commonReturnType.do // ---> userManagerContoller/commonReturnType} // ---> /WEB-INF/page/userManagerContoller/commonReturnType.jsp // ) }