• 03springMVC注解式控制器开发


    •   注解式控制器开发简介
    •  注解式控制器开发HelloWorld
    •  HelloWorld的运行流程
    •   处理器定义
    •   REST简介
    •   URL路径映射
    •   数据绑定
    •   不同的Model有相同的属性的处理
    •  静态资源的处理

    1      注解式控制器开发简介

    1)         Spring2.5之前,我们都是通过实现Controller接口或其实现来定义我们的处理器类。已经@Deprecated,建议不再使用了。

    2)         Spring2.5引入注解式处理器支持,通过@Controller 和@RequestMapping注解定义我们的处理器类。并且提供了一组强大的注解:

    @Controller:用于标识是处理器类;

    @RequestMapping:请求到处理器功能方法的映射规则;

    @RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定;

    @ModelAttribute:请求参数到命令对象的绑定

    @SessionAttributes:用于声明session级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名称,则这些属性会透明的保存到session中;

    @InitBinder:自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对应类型;

     注意:

      需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter来开启支持@Controller 和@RequestMapping注解的处理器。

    3)         Spring3.0引入RESTful架构风格支持,且又引入了更多的注解支持

    @CookieValue:cookie数据到处理器功能处理方法的方法参数上的绑定;

    @RequestHeader:请求头(header)数据到处理器功能处理方法的方法参数上的绑定;

    @RequestBody:请求的body体的绑定(通过HttpMessageConverter进行类型转换);

    @ResponseBody:处理器功能处理方法的返回值作为响应体(通过HttpMessageConverter进行类型转换);

    @ResponseStatus:定义处理器功能处理方法/异常处理器返回的状态码和原因;

    @ExceptionHandler:注解式声明异常处理器;

    @PathVariable:请求URI中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI;

    4)         Spring3.1使用新的HandlerMapping 和HandlerAdapter来支持@Contoller和@RequestMapping注解处理器,使用处理器映射RequestMappingHandlerMapping 和处理器适配器RequestMappingHandlerAdapter组合来代替Spring2.5开始的处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter。

    配置如下:

    1 <!-- 注解的映射器 -->
    2 <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
    3 <!-- 注解适配器 -->
    4 <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>

    5)         <mvc:annotation-driven>会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter两个Bean,所以有一种更简便的方法可以替代上面的配置。

    1 <!-- 注解驱动配置 -->
    2 <mvc:annotation-driven/>

    2      注解式控制器开发HelloWorld

    实现控制器 :

     1 @Controller
     2 // 或@RequestMapping //①将一个POJO类声明为处理器
     3 public class HelloWorldController {
     4     @RequestMapping(value = "/hello")
     5     // ②请求URL到处理器功能处理方法的映射
     6     public ModelAndView helloWorld() {
     7         // 1、收集参数//2、绑定参数到命令对象
     8         // 3、调用业务对象//4、选择下一个页面
     9         ModelAndView mv = new ModelAndView();
    10         // 添加模型数据可以是任意的POJO对象
    11         mv.addObject("message", "Hello World!");
    12         // 设置逻辑视图名,视图解析器会根据该名字解析到具体的视图页面
    13         mv.setViewName("hello");
    14         return mv; // 3 模型数据和逻辑视图名
    15     }
    16 }

    2:@RequestMapping(value="/hello") 请求URL到处理器的功能处理方法的映射;1:可以通过在一个POJO类上放置@Controller或@RequestMapping,即可把一个POJO类变身为处理器;

    3:现在的处理器无需实现/继承任何接口/类,只需要在相应的类/方法上放置相应的注解说明下即可,非常方便。

    •   HandlerMapping和HandlerAdapter的配置

    见上一节。

    •   视图解析器的配置

    见第一章

    •   处理器的配置

      <bean  class="cn.javass.springmvc.hello.HelloWorldController"/>

    •   视图页面(/WEB-INF/jsp/hello.jsp)

      还是前面演示的页面,这里就不重复了 

    3      HelloWorld的运行流程

     

    和前面第一章的HelloWorld不同之处在于:

    1、HandlerMapping实现:使用DefaultAnnotationHandlerMapping(spring3.1之前)

      或RequestMappingHandlerMapping(spring3.1)替换之前BeanNameUrlHandlerMapping。

    注解式处理器映射会扫描spring容器中的bean,发现bean实现类上拥有@Controller或@RequestMapping注解的bean,并将它们作为处理器。

    2、HandlerAdapter实现:使用 AnnotationMethodHandlerAdapter(spring3.1之前)或RequestMappingHandlerAdapter(spring3.1)替换之前的SimpleControllerHandlerAdapter。

    注解式处理器适配器会通过反射调用相应的功能处理方法(方法上拥有@RequestMapping注解)。

    4      处理器定义

    1)         @Controller

    1 @Controller
    2 public class HelloWorldController {
    3 ……
    4 }

    推荐使用这种方式声明处理器,它和@Service、@Repository很好的对应了我们常见的三层开发架构的组件。

    2)         @RequestMapping

    1 @RequestMapping
    2 public class HelloWorldController {
    3 ……
    4 }

    这种方式也是可以工作的,但如果在类上使用@ RequestMapping注解一般是用于窄化功能处理方法的映射的

    3)         窄化请求映射

    1 @Controller
    2 @RequestMapping(value="/user") //①处理器的通用映射前缀
    3 public class HelloWorldController2 {
    4   @RequestMapping(value = "/hello2") //②相对于①处的映射进行窄化
    5   public ModelAndView helloWorld() {
    6     //省略实现
    7   }
    8 }

    所谓窄化,就是在前面路径的基础上,继续细化的意思。比如:

    http://localhost:9080/mvcexample/hello2 无法映射到HelloWorldController2的helloWorld功能处理方法;而http://localhost:9080/mvcexample/user/hello2是可以的

    5      REST简介

    1)         REST(Representational State Transfer,简称REST )是什么

      REST(表征状态转移)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。

      REST 从资源的角度来观察整个网络,分布在各处的资源由URI确定,而客户端的应用通过URI来获取资源的表征。获得这些表征致使这些应用程序转变了其状态。随着不断获取资源的表征,客户端应用不断地在转变着其状态,所谓表征状态转移。

      如果一个架构符合REST原则,就称它为RESTful架构。

    2)         REST特点

    1:REST是设计风格而不是标准。REST通常基于使用HTTP,URI,和XML以及HTML这些现有的广泛流行的协议和标准。

    2:资源是由URI来指定

    3:对资源的操作包括获取、创建、修改和删除资源

    4:通过操作资源的表现形式来操作资源

    补充:URI和URL的区别

    统一资源标志符URI就是在某一规则下能把一个资源独一无二地标识出来。比如人的身份证号码。统一资源定位符URL,通过定位的方式来实现的URI,是URL的一种子集。

    3)         REST的优点

    1:可以利用缓存Cache来提高响应速度

    2:通讯本身的无状态性可以让不同的服务器的处理一系列请求中的不同请求,提高服务器的扩展性

    3:浏览器即可作为客户端,简化软件需求

    4:相对于其他叠加在HTTP协议之上的机制,REST的软件依赖性更小

    5:不需要额外的资源发现机制

    6:在软件技术演进中的长期的兼容性更好

    非REST的url:http://...../queryItems.action?id=001&type=T01

    REST的url风格:http://..../items/001

    4)         一个简单的REST例子

    • 需求

    查询商品信息,返回json数据。

    • controller

    定义方法,进行url映射使用REST风格的url,将查询商品信息的id传入controller.

    输出json使用@ResponseBody将java对象输出json。

        

     1 //查询商品信息,输出json
     2 
     3     ///itemsView/{id}里面的{id}表示将这个位置的参数传到@PathVariable指定名称中
     4 
     5     @RequestMapping("/itemsView/{id}")
     6 
     7     public @ResponseBody MItemsCustom itemsView(@PathVariable("id") Integer id) throws Exception{
     8 
     9        //调用service根据商品id查询商品信息
    10 
    11        MItemsCustom mItemsCustom = mItemsService.findMItemsById(id);
    12 
    13        return mItemsCustom;
    14 
    15     }

    @RequestMapping(value="/ itemsView/{id}"):{×××}占位符,请求的URL可以是“/viewItems/1”或“/viewItems/2”,通过在方法中使用@PathVariable获取{×××}中的×××变量。

    @PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

    如果RequestMapping中表示为"/ itemsView /{id}",id和形参名称一致,@PathVariable不用指定名称。

     

    • REST方法的前端控制器配置

    在web.xml配置:

     

    6      URL路径映射

    1)         普通URL路径映射

    @RequestMapping(value={"/test1", "/user/create"}):多个URL路径可以映射到同一个处理器的功能处理方法。

    2)         URI模板模式映射

    1:@RequestMapping(value="/users/{userId}"):{×××}占位符, 请求的URL可以是“/users/123456”或“/users/abcd”,通过后面讲的通过@PathVariable可以提取URI模板模式中的{×××}中的×××变量。

    2:@RequestMapping(value=“/users/{userId}/create”):这样也是可以的,请求的URL可以是“/users/123/create”。

    3:@RequestMapping(value=“/users/{userId}/topics/{topicId}”):这样也是可以的,请求的URL可以是“/users/123/topics/123”。

    3)         Ant风格的URL路径映射

    1:@RequestMapping(value=“/users/**”):可以匹配“/users/abc/abc”,但“/users/123”将会被【URI模板模式映射中的“/users/{userId}”模式优先映射到】。

    因为最长匹配优先。

    2:@RequestMapping(value="/product?"):可匹配“/product1”或“/producta”,但不匹配“/product”或“/productaa”;

    3:@RequestMapping(value="/product*"):可匹配“/productabc”或“/product”,但不匹配“/productabc/abc”;

    4:@RequestMapping(value="/product/*"):可匹配“/product/abc”,但不匹配“/productabc”;

    5:@RequestMapping(value="/products/**/{productId}"):可匹配“/products/abc/abc/123”或“/products/123”,也就是Ant风格和URI模板变量风格可混用;

    ? 匹配一个字符,如/index?可以匹配/index1,但不能匹配/index或/index12

    * 匹配零个或多个字符,如/index1/*,可以匹配/index1/demo,但不匹配/index1/demo/demo

    ** 匹配零个或多个路径,如/index2/**:可以匹配/index2路径下的所有子路径,如匹配/index2/demo,或/index2/demo/demo

    如果有如下模式,那Spring该选择哪一个执行呢?当请求为“/long/long”时如下所示:

    /long/long

    /long/**/abc

    /long/**

    /**

    Spring的AbstractUrlHandlerMapping使用:最长匹配优先;

    如请求为“/long/long” 将匹配第一个“/long/long”,但请求“/long/acd” 则将匹配“/long/**”,如请求“/long/aa/abc”则匹配“/long/**/abc”,如请求“/abc”则将匹配“/**”。

    4)         正则表达式风格的URL路径映射

       从Spring3.0开始支持正则表达式风格的URL路径映射,格式为{变量名:正则表达式},这样就可以通过@PathVariable提取模式中的{×××:正则表达式匹配的值}中的×××变量了。

       比如:@RequestMapping(value=“/products/{categoryCode:\d+}-{pageNumber:\d+}”):可以匹配“/products/123-1”,但不能匹配“/products/abc-1”,这样可以设计更加严格的规则。

       正则表达式风格的URL路径映射是一种特殊的URI模板模式映射:URI模板模式映射是{userId},不能指定模板变量的数据类型,如是数字还是字符串;正则表达式风格的URL路径映射,可以指定模板变量的数据类型,可以将规则写的相当复杂。

    5)         组合使用是“或”的关系

    如@RequestMapping(value={"/test1", "/user/create"}) 组合使用是或的关系,即“/test1”或“/user/create”请求URL路径都可以映射到@RequestMapping指定的功能处理方法。

    7      数据绑定

    到目前为止,请求已经能交给处理器进行处理了,接下来的事情是要进行收集数据啦。

    注解概要说明:

    1、@RequestParam绑定单个请求参数值;

    2、@PathVariable绑定URI模板变量值;

    3、@CookieValue绑定Cookie数据值

    4、@RequestHeader绑定请求头数据;

    5、@ModelValue绑定参数到命令对象;

    6、@SessionAttributes绑定命令对象到session;

    7、@RequestBody绑定请求的内容区数据并能进行自动类型转换等。

    8、@RequestPart绑定“multipart/data”数据,除了能绑定@RequestParam能做到的请求参数外,还能绑定上传的文件等。

    除了上边提到的注解,还可以通过如HttpServletRequest等API得到请求数据,但推荐使用注解方式,因为使用起来更简单。下面是一些参数绑定的方法:

    注意:掌握命令/表单对象、Model和Errors/BindingResult,其他的看看就行。

    一、ServletRequest/HttpServletRequest和ServletResponse/HttpServletResponse

    public String requestOrResponse (ServletRequest servletRequest, HttpServletRequest httpServletRequest,ServletResponse servletResponse, HttpServletResponse httpServletResponse)

    Spring Web MVC框架会自动把相应的Servlet请求/响应作为参数传递过来。

     

    二、InputStream/OutputStream 和Reader/Writer

    public void inputOrOutBody(InputStream requestBodyIn, OutputStream responseBodyOut) throws IOException {responseBodyOut.write("success".getBytes()); }

    requestBodyIn:获取请求的内容区字节流,等价于request.getInputStream();

    responseBodyOut:获取相应的内容区字节流,等价于response.getOutputStream()。

    public void readerOrWriteBody(Reader reader, Writer writer) throws IOException { writer.write("hello"); }

    reader:获取请求的内容区字符流,等价于request.getReader();

    writer:获取相应的内容区字符流,等价于response.getWriter()。

    注意:InputStream/OutputStream 和Reader/Writer两组不能同时使用,只能使用其中的一组。

    三、WebRequest/NativeWebRequest

    WebRequest是Spring Web MVC提供的统一请求访问接口,不仅仅可以访问请求相关数据(如参数区数据、请求头数据,但访问不到Cookie区数据),还可以访问会话和上下文中的数据;NativeWebRequest继承了WebRequest,并提供访问本地Servlet API的方法。

    public String webRequest(WebRequest webRequest, NativeWebRequest nativeWebRequest) {

    System.out.println(webRequest.getParameter("test"));//①得到请求参数test的值

    webRequest.setAttribute("name", "value", WebRequest.SCOPE_REQUEST);//②

    System.out.println(webRequest.getAttribute("name", WebRequest.SCOPE_REQUEST));

    HttpServletRequest request=nativeWebRequest.getNativeRequest(HttpServletRequest.class);//③

    HttpServletResponse response =

    nativeWebRequest.getNativeResponse(HttpServletResponse.class);

    return "success";

    }

    ① webRequest.getParameter:访问请求参数区的数据,可以通过getHeader()访问请求头数据;

    ② webRequest.setAttribute/getAttribute:到指定的作用范围内取/放属性数据,Servlet定义的三个作用范围分别使用如下常量代表:

         SCOPE_REQUEST :代表请求作用范围;

         SCOPE_SESSION :代表会话作用范围;

         SCOPE_GLOBAL_SESSION :代表全局会话作用范围,即ServletContext上下文作用范围。

    ③nativeWebRequest.getNativeRequest/nativeWebRequest.getNativeResponse:得到本地ServletAPI

    四、HttpSession

    public String session(HttpSession session) {

      System.out.println(session);

      return "success";

    }

    此处的session永远不为null。

    注意:session访问不是线程安全的,如果需要线程安全,需要设置AnnotationMethodHandlerAdapter或RequestMappingHandlerAdapter的synchronizeOnSession属性为true,即可线程安全的访问session。

     

    五、命令/表单对象

    Spring Web MVC能够自动将请求参数绑定到功能处理方法的命令/表单对象上。

    @RequestMapping(value = "/commandObject", method = RequestMethod.GET)

    public String toCreateUser(HttpServletRequest request, UserModel user) {

    return "customer/create";

    }

    @RequestMapping(value = "/commandObject", method = RequestMethod.POST)

    public String createUser(HttpServletRequest request, UserModel user) {

    System.out.println(user);

    return "success";

    }

    如果提交的表单(包含username和password文本域),将自动将请求参数绑定到命令对象user中去。

     

    六、Model、Map、ModelMap

    Spring Web MVC 提供Model、Map或ModelMap让我们能去暴露渲染视图需要的模型数据。

    public String createUser(Model model, Map model2, ModelMap model3) {

      model.addAttribute("a", "a");

      model2.put("b", "b");

      model3.put("c", "c");

      System.out.println(model == model2);

      System.out.println(model2 == model3);

      return "success";

    }

    注意:虽然此处注入的是三个不同的类型(Model model, Map model2, ModelMap model3),但三者是同一个对象AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter将使用BindingAwareModelMap作为模型对象的实现,即此处我们的形参(Model model, Map model2, ModelMap model3)都是同一个BindingAwareModelMap实例。

    此处还有一点需要我们注意:

    public ModelAndView mergeModel(Model model) {

     model.addAttribute("a", "a");//①添加模型数据

     ModelAndView mv = new ModelAndView("success");

     mv.addObject("a", "update");//②在视图渲染之前更新③处同名模型数据

     model.addAttribute("a", "new");//③修改①处同名模型数据

     //视图页面的a将显示为"update" 而不是"new"

     return mv;

    }

    从代码中我们可以总结出功能处理方法的返回值中的模型数据(如ModelAndView)会合并功能处理方法形式参数中的模型数据(如Model),但如果两者之间有同名的,返回值中的模型数据会覆盖形式参数中的模型数据。

    七、Errors/BindingResult

    public String error1(UserModel user, BindingResult result)

    public String error2(UserModel user, BindingResult result, Model model)

    public String error3(UserModel user, Errors errors)

    以上代码都能获取错误对象。

    Spring3.1之前(使用AnnotationMethodHandlerAdapter)错误对象必须紧跟在命令对象/表单对象之后,如下定义是错误的:

    public String error4(UserModel user, Model model, Errors errors)

    如上代码从Spring3.1开始(使用RequestMappingHandlerAdapter)将能正常工作,但还是推荐“错误对象紧跟在命令对象/表单对象之后”,这样是万无一失的。

     

    八、其他杂项

    public String other(Locale locale, Principal principal)

    java.util.Locale:得到当前请求的本地化信息,默认等价于

    ServletRequest.getLocale(),如果配置LocaleResolver解析器则由它决定Locale,后续介绍;

    java.security.Principal:该主体对象包含了验证通过的用户信息,等价于HttpServletRequest.getUserPrincipal()。

    还有其他功能处理方法的形式参数类型(如HttpEntity、UriComponentsBuilder、SessionStatus、RedirectAttributes)等等。

     

    数据绑定注解详解:

    1. 1.         数据绑定@RequestParam
    •   @RequestParam :用于将请求参数区数据映射到功能处理方法的参数上。

    例子:public String requestparam1(@RequestParam String username)

    1:如果请求中包含username参数(如/requestparam1?username=zhang),则自动传入。

    2:也可以使用@RequestParam("username")明确告诉Spring Web MVC使用username进行入参

    •  @RequestParam的主要参数

    value:参数名字,即入参的请求参数名字,如username表示请求的参数区中的名字为username的参数的值将传入;

    required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报400错误码;

    defaultValue:默认值,表示如果请求中没有同名参数时的默认值,默认值可以是SpEL表达式,如“#{systemProperties[‘java.vm.version’]}”。

    例子1:public String test(@RequestParam(value="username",required=false) String username)

    表示请求中可以没有名字为username的参数,如果没有默认为null,需注意如下几点:

    (1):原子类型:必须有值,否则抛出异常,如果允许空值请使用包装类代替。

    (2):Boolean包装类型类型:默认Boolean.FALSE,其他引用类型默认为null。

    例子2:public String requestparam5( @RequestParam(value="username", required=true, defaultValue="zhang") String username)表示如果请求中没有名字为username的参数,默认值为“zhang”。

    • 如果请求中有多个同名的应该如何接收呢?如给用户授权时,可能授予多个权限,先看如下代码:

    public String test(@RequestParam(value="role") String roleList)

    如果请求参数类似于url?role=admin&role=user,则实际roleList参数入参的数据为“admin,user”,即多个数据之间使用“,”分割;我们应该使用如下方式来接收多个请求参数:

    public String test(@RequestParam(value="role") String[] roleList)

    public String test(@RequestParam(value="list") List<String> list)

    1. 2.         数据绑定@PathVariable

    @PathVariable

    功能:用于将请求URL中的模板变量映射到功能处理方法的参数上

    例子:

    @RequestMapping(value="/users/{userId}/topics/{topicId}")

    public String test(@PathVariable(value="userId") int userId,@PathVariable(value="topicId") int topicId)

    如请求的URL为“控制器URL/users/123/topics/456”,则自动将URL中模板变量{userId}和{topicId}绑定到通过@PathVariable注解的同名参数上,即入参后userId=123、topicId=456 java

    1. 3.         数据绑定@CookieValue

    @CookieValue

    功能:用于将请求的Cookie数据映射到功能处理方法的参数上

    例子1:

    public String test(@CookieValue(value="JSESSIONID", defaultValue="") String sessionId)

    如上配置将自动将JSESSIONID值入参到sessionId参数上,defaultValue表示Cookie中没有JSESSIONID时默认为空。

    例子2:

    public String test2(@CookieValue(value="JSESSIONID", defaultValue="") Cookie sessionId)

    传入参数类型也可以是javax.servlet.http.Cookie类型。

    @CookieValue也拥有和@RequestParam相同的三个参数,含义一样。

    1. 4.         数据绑定@RequestHeader

    @RequestHeader

    功能:用于将请求的头信息区数据映射到功能处理方法的参数上

    例子:

    @RequestMapping(value="/header")

    public String test(@RequestHeader("User-Agent") String userAgent,

    @RequestHeader(value="Accept") String[] accepts)

    如上配置将自动将请求头“User-Agent”值入参到userAgent参数上,并将“Accept”请求头值入参到accepts参数上。

    @RequestHeader也拥有和@RequestParam相同的三个参数,含义一。

    1. 5.         数据绑定@ModelAttribute

    @ModelAttribute

    @ModelAttribute一般具有如下三个作用:

    1:绑定请求参数到命令对象:放在功能处理方法的入参上时,用于将多个请求参数绑定到一个命令对象,从而简化绑定流程,而且自动暴露为模型数据用于视图页面展示时使用;

    2:暴露表单引用对象为模型数据:放在处理器的一般方法(非功能处理方法)上时,是为表单准备要展示的表单引用对象,如注册时需要选择的所在城市等,而且在执行功能处理方法(@RequestMapping注解的方法)之前,自动添加到模型对象中,用于视图页面展示时使用;

    3:暴露@RequestMapping方法返回值为模型数据:放在功能处理方法的返回值上时,是暴露功能处理方法的返回值为模型数据,用于视图页面展示时使用。

    •   绑定请求参数到命令对象

    如实现用户登录,需要捕获用户s登录的请求参数(用户名、密码)并封装为用户对象,此时可以使用@ModelAttribute绑定多个请求参数到我们的命令对象。

    例子:public String test1(@ModelAttribute("user") UserModel user)

    说明:1:和前面命令/表单对象一样,只是此处多了一个注解@ModelAttribute(“user”),

      它的作用是将该绑定的命令对象以“user”为名称添加到模型对象中供视图页面展示使用。

      我们此时可以在视图页面使用${user.username}来获取绑定的命令对象的属性。

    2:绑定请求参数到命令对象支持对象图导航式的绑定,如请求参数包含

    “?username=zhang&password=123&workInfo.city=bj”自动绑定到user中的workInfo属性的city属性中。

    3:@RequestMapping(value="/model2/{username}")

    public String test2(@ModelAttribute("model") UserModel model) {

      URI模板变量也能自动绑定到命令对象中,当你请求的URL中包含“&username=zhang”会自动绑定到命令对象上。当URI模板变量和请求参数同名时,请求参数具有高优先权。

    •  暴露表单引用对象为模型数据

    例子1:

    @ModelAttribute("cityList")

    public List<String> cityList() {

    return Arrays.asList("北京", "山东");

    }

    如上代码会在执行功能处理方法之前执行,并将其自动添加到模型对象中,在功能处理方法中可以使用Model入参,则可以在处理方法中使用citylist了,如:

    public ModelAndView handleRequest(Model m) {

      List<String> list = (List<String>)m.asMap().get("cityList");

      for(String s : list){

        System.out.println("s==="+s);

      }

       ......

    }

    例子2:

    @ModelAttribute("user") //①

    public UserModel getUser(@RequestParam(value=“username", defaultValue="")

    String username) { //TODO 去数据库根据用户名查找用户对象}

    如你要修改用户资料时一般需要根据用户的编号/用户名查找用户来进行编辑,此时可以通过如上代码查找要编辑的用户。也可以进行一些默认值的处理。

    @RequestMapping(value="/model1") //②

    public String test1(@ModelAttribute("user") UserModel user, Model model)

    说明:

    此处我们看到①和②有同名的命令对象,那Spring Web MVC内部如何处理的呢:

    1、首先执行@ModelAttribute注解的方法,准备视图展示时所需要的模型数据;

      @ModelAttribute注解方法形式参数规则和@RequestMapping规则一样,如可以有

      @RequestParam等;

    2、执行@RequestMapping注解方法,进行模型绑定时首先查找模型数据中是否含有同名对象,如果有直接使用,如果没有通过反射创建一个,因此②处的user将使用①处返回的命令对象。即②处的user等于①处的user。

    •  暴露@RequestMapping方法返回值为模型数据

    例子:

    public @ModelAttribute("user2") UserModel test3(@ModelAttribute("user2") UserModel user)

    大家可以看到返回值类型是命令对象类型,而且通过@ModelAttribute(“user2”)注解,此时会暴露返回值到模型数据(名字为user2)中供视图展示使用。可能有同学会注意到,此时@RequestMapping注解方法的入参user暴露到模型数据中的名字也是user2,那么到底user2代表哪一个呢?

    规则是:@ModelAttribute注解的返回值会覆盖@RequestMapping注解方法中的@ModelAttribute注解的同名命令对象

    •   匿名绑定命令参数

    例子1:

    public String test4(@ModelAttribute UserModel user, Model model)

    或public String test5(UserModel user, Model model)

    说明:

    此时我们没有为命令对象提供暴露到模型数据中的名字,此时的名字是什么呢?

    Spring Web MVC自动将简单类名(首字母小写)作为名字暴露,如

    “cn.javass.springmvc.model.UserModel”暴露的名字为“userModel”。

    例子2:

    public @ModelAttribute List<String> test6()

    或public @ModelAttribute List<UserModel> test7()

    说明:

        对于集合类型(Collection接口的实现者们,包括数组),生成的模型对象属性名为“简单类名(首字母小写)”+“List”,如List<String>生成的模型对象属性名为“stringList”,List<UserModel>生成的模型对象属性名为“userModelList”。

        其他情况一律都是使用简单类名(首字母小写)作为模型对象属性名,如Map<String, UserModel>类型的模型对象属性名为“map”。

    1. 数据绑定@SessionAttributes

    @SessionAttributes 绑定命令对象到session

    //1、在控制器类头上添加@SessionAttributes注解

    @SessionAttributes(value = {"user"}) //①

    public class SessionAttributeController

    //2、@ModelAttribute注解的方法进行表单引用对象的创建

    @ModelAttribute("user") //②

    public UserModel initUser()

    //3、@RequestMapping注解方法的@ModelAttribute注解的参数进行命令对象的绑定

    @RequestMapping("/session1") //③

    public String session1(@ModelAttribute("user") UserModel user)

    //4、通过SessionStatus的setComplete()方法清除@SessionAttributes指定的会话数据

    @RequestMapping("/session2") //③

    public String session(@ModelAttribute("user") UserModel user, SessionStatus status) {

    if(true) { //④

    status.setComplete(); }

    return "success";

    }

    @SessionAttributes(value = {“user”})含义:

    @SessionAttributes(value = {“user”}) 标识将模型数据中的名字为“user” 的对象存储到会话中(默认HttpSession),此处value指定将模型数据中的哪些数据(名字进行匹配)存储到会话中,此外还有一个types属性表示模型数据中的哪些类型的对象存储到会话范围内,如果同时指定value和types属性则那些名字和类型都匹配的对象才能存储到会话范围内。

    包含@SessionAttributes的执行流程如下所示:

    ① 首先根据类上的@SessionAttributes注解信息,查找会话内的对象放入到模型数据中;

    ② 执行@ModelAttribute注解的方法:如果模型数据中包含同名的数据,则不执行@ModelAttribute注解方法进行准备表单引用数据,而是使用①步骤中的会话数据;如果模型数据中不包含同名的数据,执行@ModelAttribute注解的方法并将返回值添加到模型数据中;

    ③ 执行@RequestMapping方法,绑定@ModelAttribute注解的参数:查找模型数据中是否有@ModelAttribute注解的同名对象,如果有直接使用,否则通过反射创建一个;并将请求参数绑定到该命令对象;

        此处需要注意:如果使用@SessionAttributes注解控制器类之后,③步骤一定是从模型对象中取得同名的命令对象,如果模型数据中不存在将抛出

    HttpSessionRequiredException Expected session attribute‘user’(Spring3.1)

    或HttpSessionRequiredException Session attribute ‘user’ required -not found in session(Spring3.0)异常。

    ④ 如果会话可以销毁了,如多步骤提交表单的最后一步,此时可以调用SessionStatus对象的setComplete()标识当前会话的@SessionAttributes指定的数据可以清理了,此时当@RequestMapping功能处理方法执行完毕会进行清理会话数据。

    1. 7.         数据绑定@Value

    @Value

    功能:用于将一个SpEL表达式结果映射到到功能处理方法的参数上

    例子:

    public String test(@Value("#{systemProperties['java.vm.version']}")

      String jvmVersion)

    1. 8.         数据绑定@MatrixVariable

    @MatrixVariable

    功能:用于接收URL的path中的矩阵参数

    语法格式:XXX/XXX/path;name=value;name=value

    开启功能:

    (1)如果是xml配置的RequestMappingHandlerMapping,那么需要设置removeSemicolonContent属性为false

    (2)如果是注解的方式,直接设置<mvc:annotation-driven>的enableMatrixVariables=“true“ 就可以了

    例子代码见下页:

    测试的URL为

    http://localhost:9080/mvcexample/users/42;q=11;r=12/others/21;q=22;s=23

    @RequestMapping(value = "/users/{userId}/others/{otherUserId}",method =

    RequestMethod.GET)

    public void hello(

    //如果只有一个地方有q,也可以这么取,但如果有多个q,这样就错了,必须像第二个那样去指定取谁的q值

    // @MatrixVariable int q,

    @MatrixVariable(value="q", pathVar="userId") int q1,

    @MatrixVariable(value="q", pathVar="otherUserId") int q2,

    @MatrixVariable Map<String, String> matrixVars,

    @MatrixVariable(pathVar="userId") Map<String, String> userIdMatrixVars

    )

    {

    // System.out.println("q=="+q);

    System.out.println("q1="+q1+",q2="+q2+",matrixVars="+matrixVars+",userIdMatrixV

    ars="+userIdMatrixVars);

    }

    运行结果为:

      q1=11,q2=22,matrixVars={q=[11, 22], r=[12],s=[23]},userIdMatrixVars={q=[11],r=[12]}

    8      不同的Model有相同的属性的处理

    •   不同的Model有相同的属性

         指的是在方法的入参上,可能需要有不同的Model,但是他们又可能有相同的属性,目前Spring Web MVC默认的方式是,只要同名的属性,值就是一样的。

    public ModelAndView handleRequest(UserModel um,DepModel dm)

    如果:um和dm都有一个uuid的属性,那么映射后他们的值会是一样的,这显然不合理,在struts2中是通过使用前缀来表示这个uuid的值到底是给谁的,比如:um.uuid,肯定值就给um,而不会给dm。在Spring3 Web Mvc中到底如何实现类似的功能呢?

    •  使用@InitBinder,在每次进行数据绑定的时候,都会回调这个注解的方法

    @InitBinder("um")

    public void initBinderUser(WebDataBinder binder) {

    binder.setFieldDefaultPrefix("um."); }

    @InitBinder("dm")

    public void initBinderDep(WebDataBinder binder) {

    binder.setFieldDefaultPrefix("dm."); }

    @RequestMapping(value = "/hello")

    public ModelAndView handleRequest(@ModelAttribute("um") UserModel

      um,@ModelAttribute("dm")DepModel dm)

    注意:这个方式对URI上的参数无效,仅对parameter区的数据有效

    9      静态资源的处理

    <mvc:resources/>用来指定逻辑静态资源路径到物理静态资源路径的映射。

    例子:

    1 <!-- 静态资源解析 包括:js,css,img,html... -->
    2 <mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
    3 <mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
    4 <mvc:resources location="/images/" mapping="/images/**"></mvc:resources>
    5 <mvc:resources location="/html/" mapping="/html/**"></mvc:resources>
  • 相关阅读:
    爱加密亮相第十八届软博会,移动App安全引关注
    android 实现自己定义状态栏通知(Status Notification)
    Android中View绘制流程以及invalidate()等相关方法分析
    设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
    目标检測的图像特征提取之(一)HOG特征
    Vbox 未指定XXX网络名称 找不到网卡问题
    NetBeans工具学习之道:NetBeans IDE Java 高速新手教程
    NETSH WINSOCK RESET这条命令的含义和作用?
    红帽/CentOS ext4无法格式化大分区 补充ext4格式化方式
    android之PackageManager简单介绍
  • 原文地址:https://www.cnblogs.com/zhouyeqin/p/8418639.html
Copyright © 2020-2023  润新知