栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring MVC笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring MVC笔记

Spring MVC

Spring MVC 是目前主流的实现 MVC 设计模式的企业级开发框架,Spring 框架的一个子模块,无需整合,开发起来更加便捷。

什么是 MVC 设计模式?

将应用程序分为 Controller、Model、View 三层,Controller 接收客户端请求,调用 Model 生成业务数据,传递给 View。

Spring MVC 就是对这套流程的封装,屏蔽了很多底层代码,开放出接口,让开发者可以更加轻松、便捷地完成基于 MVC 模式的 Web 开发。

Spring MVC 的核心组件
  • DispatcherServlet:前置控制器,是整个流程控制的核心,控制其他组件的执行,进行统一调度,降低组件之间的耦合性,相当于总指挥。
  • Handler:处理器,完成具体的业务逻辑,相当于 Servlet 或 Action。
  • HandlerMapping:DispatcherServlet 接收到请求之后,通过 HandlerMapping 将不同的请求映射到不同的 Handler。
  • HandlerInterceptor:处理器拦截器,是一个接口,如果需要完成一些拦截处理,可以实现该接口。
  • HandlerExecutionChain:处理器执行链,包括两部分内容:Handler 和 HandlerInterceptor(系统会有一个默认的 HandlerInterceptor,如果需要额外设置拦截,可以添加拦截器)。
  • HandlerAdapter:处理器适配器,Handler 执行业务方法之前,需要进行一系列的操作,包括表单数据的验证、数据类型的转换、将表单数据封装到 JavaBean 等,这些操作都是由 HandlerApater 来完成,开发者只需将注意力集中业务逻辑的处理上,DispatcherServlet 通过 HandlerAdapter 执行不同的 Handler。
  • ModelAndView:装载了模型数据和视图信息,作为 Handler 的处理结果,返回给 DispatcherServlet。
  • ViewResolver:视图解析器,DispatcheServlet 通过它将逻辑视图解析为物理视图,最终将渲染结果响应给客户端。
Spring MVC 的工作流程
  • 客户端请求被 DisptacherServlet 接收。
  • 根据 HandlerMapping 映射到 Handler。
  • 生成 Handler 和 HandlerInterceptor。
  • Handler 和 HandlerInterceptor 以 HandlerExecutionChain 的形式一并返回给 DisptacherServlet。
  • DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法完成业务逻辑处理。
  • Handler 返回一个 ModelAndView 给 DispatcherServlet。
  • DispatcherServlet 将获取的 ModelAndView 对象传给 ViewResolver 视图解析器,将逻辑视图解析为物理视图 View。
  • ViewResovler 返回一个 View 给 DispatcherServlet。
  • DispatcherServlet 根据 View 进行视图渲染(将模型数据 Model 填充到视图 View 中)。
  • DispatcherServlet 将渲染后的结果响应给客户端。

Spring MVC 流程非常复杂,实际开发中很简单,因为大部分的组件不需要开发者创建、管理,只需要通过配置文件的方式完成配置即可,真正需要开发者进行处理的只有 Handler 、View。

如何使用?
  • 创建 Maven 工程,pom.xml
    (也是用的spring,spring-webmv是其中的一个小模块)


    
        org.springframework
        spring-webmvc
        5.0.11.RELEASE
    


  • 在 web.xml 中配置 DispatcherServlet。
    (servlet中配置dispathServlet,然后要导入springmvc.xml。mapping中配置 dispatcherServlet)



  Archetype Created Web Application
  
  
    dispatcherServlet
    org.springframework.web.servlet.DispatcherServlet
    
      contextConfigLocation
      classpath:springmvc.xml
    
  
  
  
    dispatcherServlet
    /
  
  

  • springmvc.xml



    
    

    
    
        
        
    


视图解析器:加个前缀/,加个后缀.jsp,假定文件名叫做index,结果就是/index.jsp。

  • 创建 Handler
package com.southwind.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloHandler {

    @RequestMapping("/index")
    public String index(){
        System.out.println("执行了index...");
        return "index";
    }
}
Spring MVC 注解
  • @RequestMapping

Spring MVC 通过 @RequestMapping 注解将 URL 请求与业务方法进行映射,在 Handler 的类定义处以及方法定义处都可以添加 @RequestMapping ,在类定义处添加,相当于客户端多了一层访问路径。

  • @Controller

@Controller 在类定义处添加,将该类交个 IoC 容器来管理(结合 springmvc.xml 的自动扫描配置使用!!!),同时使其成为一个控制器,可以接收客户端请求。

package com.southwind.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/hello")
public class HelloHandler {

    @RequestMapping("/index")
    public String index(){
        System.out.println("执行了index...");
        return "index";
    }
}
  • @RequestMapping 相关参数

1、value:指定 URL 请求的实际地址,是 @RequestMapping 的默认值。

@RequestMapping("/index")
public String index(){
    System.out.println("执行了index...");
    return "index";
}

等于

@RequestMapping(value="/index")
public String index(){
    System.out.println("执行了index...");
    return "index";
}

2、method:指定请求的 method 类型,GET、POST、PUT、DELET。(增删改查)

@RequestMapping(value = "/index",method = RequestMethod.GET)
public String index(){
    System.out.println("执行了index...");
    return "index";
}

上述代码表示 index 方法只能接收 GET 请求。

3、params:指定请求中必须包含某些参数,否则无法调用该方法。

@RequestMapping(value = "/index",method = RequestMethod.GET,params = {"name","id=10"})
public String index(){
    System.out.println("执行了index...");
    return "index";
}

上述代码表示请求中必须包含 name 和 id 两个参数,同时 id 的值必须是 10。
输入http://localhost:8080/hello/index?name=zhangsan&id=10,可以正常访问
输入http://localhost:8080/hello/index?name=zhangsan&id=10会报错,说id不对什么的。

关于参数绑定,在形参列表中通过添加 @RequestParam 注解完成 HTTP 请求参数与业务方法形参的映射。(@RequestParam是用来解放命名的,如果实参和形参名字一致其实是可以不用这个注解的)

@RequestMapping(value = "/index",method = RequestMethod.GET,params = {"name","id=10"})
public String index(@RequestParam("name") String str,@RequestParam("id") int age){
    System.out.println(str);
    System.out.println(age);
    System.out.println("执行了index...");
    return "index";
}

上述代码表示将请求的参数 name 和 id 分别赋给了形参 str 和 age ,同时自动完成了数据类型转换,将 “10” 转为了 int 类型的 10,再赋给 age,这些工作都是由 HandlerAdapter 来完成的。

Spring MVC 也支持 RESTful 风格的 URL。

传统类型:http://localhost:8080/hello/index?name=zhangsan&id=10

REST:http://localhost:8080/hello/index/zhangsan/10

@RequestMapping("/rest/{name}/{id}")
public String rest(@PathVariable("name") String name,@PathVariable("id") int id){
    System.out.println(name);
    System.out.println(id);
    return "index";
}

通过 @PathVariable 注解完成请求参数与形参的映射(在restful风格使用)。

  • 映射 Cookie

Spring MVC 通过映射可以直接在业务方法中获取 Cookie 的值。

@RequestMapping("/cookie")
public String cookie(@CookieValue(value = "JSESSIONID") String sessionId){
    System.out.println(sessionId);
    return "index";
}
  • 使用 JavaBean 绑定参数

Spring MVC 会根据请求参数名和 JavaBean 属性名进行自动匹配,自动为对象填充属性值,同时支持级联属性。(这段代码要是提交的数据格式不对,会报错。。。)

package com.southwind.entity;

import lombok.Data;

@Data
public class Address {
    private String value;
}
package com.southwind.entity;

import lombok.Data;

@Data
public class User {
    private long id;
    private String name;
    private Address address;
}
<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-13
  Time: 15:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


    
用户id:
用户名:
用户地址:
@RequestMapping(value = "/save",method = RequestMethod.POST)
public String save(User user){
    System.out.println(user);
    return "index";
}

如果出现中文乱码问题,只需在 web.xml 添加 Spring MVC 自带的过滤器即可。(复制就好,也不用管为啥)


    encodingFilter
    org.springframework.web.filter.CharacterEncodingFilter
    
        encoding
        UTF-8
    



    encodingFilter
    /*

输出结果:
User(id=1, name=阿萨德, address=Address(value=科技路))

  • JSP 页面的转发(服务器调整)和重定向(客户端调整):

Spring MVC 默认是以转发的形式响应 JSP。

1、转发

@RequestMapping("/forward")
public String forward(){
    return "forward:/index.jsp";
    //        return "index";
}

return “forward:/index.jsp”; 先写 forward:表示转发,转发到哪?
index.jsp。
2、重定向

@RequestMapping("/redirect")
public String redirect(){
    return "redirect:/index.jsp";
}

return “redirect:/index.jsp”; 先写redirect:表示转发,转发到哪?
index.jsp。(地址栏会变的!)

Spring MVC 数据绑定

数据绑定:在后端的业务方法中直接获取客户端 HTTP 请求中的参数,将请求参数映射到业务方法的形参中,Spring MVC 中数据绑定的工作是由 HandlerAdapter 来完成的。

  • 基本数据类型
@RequestMapping("/baseType")
@ResponseBody
public String baseType(int id){
    return id+"";
}

@ResponseBody 表示 Spring MVC 会直接将业务方法的返回值响应给客户端,如果不加 @ResponseBody 注解,Spring MVC 会将业务方法的放回值传递给 DispatcherServlet,再由 DisptacherServlet 调用 ViewResolver 对返回值进行解析,映射到一个 JSP 资源。

  • 包装类
@RequestMapping("/packageType")
@ResponseBody
public String packageType(@RequestParam(value = "num",required = false,defaultValue = "0") Integer id){
    return id+"";
}

包装类可以接收 null,当 HTTP 请求没有参数时,使用包装类定义形参的数据类型,程序不会抛出异常。

@RequestParam

value = “num”:将 HTTP 请求中名为 num 的参数赋给形参 id。

requried:设置 num 是否为必填项,true 表示必填,false 表示非必填,可省略。

defaultValue = “0”:如果 HTTP 请求中没有 num 参数,默认值为0.

  • 数组
@RestController
@RequestMapping("/data")
public class DataBindHandler {
    @RequestMapping("/array")
    public String array(String[] name){
        String str = Arrays.toString(name);
        return str;
    }
}

@RestController 表示该控制器会直接将业务方法的返回值响应给客户端,不进行视图解析。(替换每一个方法前面写的@ResponseBody )(如果与@Controller同时存在,听RestController的)

@Controller 表示该控制器的每一个业务方法的返回值都会交给视图解析器进行解析,如果只需要将数据响应给客户端,而不需要进行视图解析,则需要在对应的业务方法定义处添加 @ResponseBody。

@RestController
@RequestMapping("/data")
public class DataBindHandler {
    @RequestMapping("/array")
    public String array(String[] name){
        String str = Arrays.toString(name);
        return str;
    }
}

等同于

@Controller
@RequestMapping("/data")
public class DataBindHandler {
    @RequestMapping("/array")
    @ResponseBody
    public String array(String[] name){
        String str = Arrays.toString(name);
        return str;
    }
}
  • List

Spring MVC 不支持 List 类型的直接转换,需要对 List 集合进行包装。

集合封装类

package com.southwind.entity;

import lombok.Data;

import java.util.List;

@Data
public class UserList {
    private List users;
}

JSP

<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-14
  Time: 09:12
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


    
用户1编号:
用户1名称:
用户2编号:
用户2名称:
用户3编号:
用户3名称:

业务方法

@RequestMapping("/list")
public String list(UserList userList){
    StringBuffer str = new StringBuffer();
    for(User user:userList.getUsers()){
        str.append(user);
    }
    return str.toString();
}

处理 @ResponseBody 中文乱码,在 springmvc.xml 中配置消息转换器。


    
    
        
            
        
    

  • Map

自定义封装类

package com.southwind.entity;

import lombok.Data;

import java.util.Map;

@Data
public class UserMap {
    private Map users;
}

业务方法

@RequestMapping("/map")
public String map(UserMap userMap){
    StringBuffer str = new StringBuffer();
    for(String key:userMap.getUsers().keySet()){
        User user = userMap.getUsers().get(key);
        str.append(user);
    }
    return str.toString();
}

JSP

<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-14
  Time: 09:12
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


    
用户1编号:
用户1名称:
用户2编号:
用户2名称:
用户3编号:
用户3名称:
  • JSON

客户端发生 JSON 格式的数据,直接通过 Spring MVC 绑定到业务方法的形参中。

处理 Spring MVC 无法加载静态资源(js),在 web.xml 中添加配置即可。(这种方法我反正是不行)


    default
    *.js

可行的解决方法,在mvc配置文件中添加这样一段代码就行了,可以放开所有的静态资源。

    
    

JSP

<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-14
  Time: 10:35
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title
    
    





业务方法

@RequestMapping("/json")
public User json(@RequestBody User user){
    System.out.println(user);
    user.setId(6);
    user.setName("张六");
    return user;
}

@RequestBody与@ResponseBody相对应,@RequestBody指的是直接从客户端拿到某个数据(这里是直接从客户端拿到json数据),@ResponseBody指的是直接将数据返回给客户端,而不是给dispatcherServlet再让它去做地址解析。
Spring MVC 中的 JSON 和 JavaBean 的转换需要借助于 fastjson,pom.xml 引入相关依赖。


    com.alibaba
    fastjson
    1.2.32

springmvc.xml 添加 fastjson 配置。


    
    
        
            
        
        
        
    

Spring MVC 模型数据解析

JSP 四大作用域对应的内置对象:pageContext、request、session、application。

模型数据的绑定是由 ViewResolver 来完成的,实际开发中,我们需要先添加模型数据,再交给 ViewResolver 来绑定。

Spring MVC 提供了以下几种方式添加模型数据:

  • Map
  • Model
  • ModelAndView
  • @SessionAttribute
  • @ModelAttribute

将模式数据绑定到 request 对象。

1、Map

@RequestMapping("/map")
public String map(Map map){
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    map.put("user",user);
    return "view";
}

访问http://localhost:8080/view/map
将map对象里的键值对弄到view.jsp的request里!
JSP

<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-14
  Time: 11:36
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>


    Title


    ${requestScope.user}


isELIgnored如果设定为true,也就是EL被忽略,那么JSP中的表达式被当成字符串处理。False的话,把${requestScope.user}解析成当前路径。
${requestScope.user}就是拿到 request请求里的user(viewHandler发起的)。

2、Model

@RequestMapping("/model")
public String model(Model model){
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    model.addAttribute("user",user);
    return "view";
}

访问http://localhost:8080/view/model
将model对象里的键值对弄到view.jsp的request里!
(model引用 import org.springframework.ui.Model;)
3、ModelAndView
new ModelAndView();的构造方法

ModelAndView对象的构造方法

package com.southwind.controller;

import com.southwind.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.InternalResourceView;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/view")
public class ViewHandler {
    @RequestMapping("/modelAndView")
    public ModelAndView modelAndView(){
        User user = new User();
        user.setId(1L);
        user.setName("张三");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject(user);
        modelAndView.setViewName("view");
        return modelAndView;
    }
    @RequestMapping("/modelAndView2")
    public ModelAndView modelAndView2(){
        User user = new User();
        user.setId(2L);
        user.setName("张三");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("user",user);
        View view = new InternalResourceView("/view.jsp");
        modelAndView.setView(view);
        return modelAndView;
    }
    @RequestMapping("/modelAndView3")
    public ModelAndView modelAndView3(){
        User user = new User();
        user.setId(3L);
        user.setName("张三");
        ModelAndView modelAndView = new ModelAndView( "view");
        modelAndView.addObject("user",user);
        return modelAndView;
    }
    @RequestMapping("/modelAndView4")
    public ModelAndView modelAndView4(){
        User user = new User();
        user.setId(4L);
        user.setName("张三");
        View view = new InternalResourceView("/view.jsp");
        ModelAndView modelAndView = new ModelAndView( view);
        modelAndView.addObject("user",user);
        return modelAndView;
    }
    @RequestMapping("/modelAndView5")
    public ModelAndView modelAndView5(){
        User user = new User();
        user.setId(5L);
        user.setName("张三");
        Map map = new HashMap<>();
        map.put("user",user);
        ModelAndView modelAndView = new ModelAndView("view",map);
        return modelAndView;
    }
    @RequestMapping("/modelAndView6")
    public ModelAndView modelAndView6(){
        User user = new User();
        user.setId(6L);
        user.setName("张三");
        Map map = new HashMap<>();
        map.put("user",user);
        View view = new InternalResourceView("/view.jsp");
        ModelAndView modelAndView = new ModelAndView(view,map);
        return modelAndView;
    }
    @RequestMapping("/modelAndView7")
    public ModelAndView modelAndView7(){
        User user = new User();
        user.setId(7L);
        user.setName("张三");
        ModelAndView modelAndView = new ModelAndView("view","user",user);
        return modelAndView;
    }
    @RequestMapping("/modelAndView8")
    public ModelAndView modelAndView8(){
        User user = new User();
        user.setId(8L);
        user.setName("张三");
        View view = new InternalResourceView("/view.jsp");
        ModelAndView modelAndView = new ModelAndView(view,"user",user);
        return modelAndView;
    }
}

4、HttpServletRequest

@RequestMapping("/request")
public String request(HttpServletRequest request){
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    request.setAttribute("user",user);
    return "view";
}

直接通过HttpServletRequest 创建一个request对象,然后把"user",user作为键值对装进去!

5、@ModelAttribute

  • 定义一个方法,该方法专门用来返回要填充到模型数据中的对象。
  • 理解:就是写一个用@ModelAttribute注解的方法,这个方法负责创建模型,返回数据或者装载数据到request里面,然后再写一个modelAttribute()返回逻辑视图就行
  • 这里只实验过@ModelAttribute注解一个方法,注解多个方法,返回多个对象好像是不行的。
@ModelAttribute
public User returnUser1(){
    User user = new User();
    user.setId(1L);
    user.setName("user1");
    return user;
}
@ModelAttribute
public void returnUser2(Map map){
    User user = new User();
    user.setId(1L);
    user.setName("user1");
    map.put("user",user);
}
@ModelAttribute
public void returnUser3(Model model){
    User user = new User();
    user.setId(1L);
    user.setName("user1");
    model.addAttribute("user",user);
}

这里没有使用getUser()因为感觉会和get,set方法有点重复的感觉!

  • 业务方法中无需再处理模型数据,只需返回视图即可。
@RequestMapping("/modelAttribute")
public String modelAttribute(){
    return "view";
}

将模型数据绑定到 session 对象

1、直接使用原生的 Servlet API。

@RequestMapping("/session")
public String session(HttpServletRequest request){
    HttpSession session = request.getSession();
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    session.setAttribute("user",user);
    return "view";
}

@RequestMapping("/session2")
public String session2(HttpSession session){
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    session.setAttribute("user",user);
    return "view";
}

记得修改jsp文件,让request和session里的文件同时显示!

  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>


    Title


    ${requestScope.user}
    
${sessionScope.user}

结果如下,request 和session里的东西都被显示出来了!!!!

2、@SessionAttribute

@SessionAttributes(value = {"user","address"})
public class ViewHandler {
}

对于 ViewHandler 中的所有业务方法(加到类前面,不是方法前面!!),只要向 request 中添加了 key = “user”、key = “address” 的对象时,Spring MVC 会自动将该数据添加到 session 中,保存 key 不变。

@SessionAttributes(types = {User.class,Address.class})
public class ViewHandler {
}

对于 ViewHandler 中的所有业务方法,只要向 request 中添加了数据类型是 User 、Address 的对象时,Spring MVC 会自动将该数据添加到 session 中,保存 key 不变。

将模型数据绑定到 application 对象

@RequestMapping("/application")
public String application(HttpServletRequest request){
    ServletContext application = request.getServletContext();
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    application.setAttribute("user",user);
    return "view";
}

修改view.jsp为下面(加入applicationscope)

<%--
  Created by IntelliJ IDEA.
  User: 杨
  Date: 2022/5/1
  Time: 17:54
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>


    Title


    ${requestScope.user}
    
${sessionScope.user}
${applicationScope.user}

这里注意一下applicaiton是通过 ServletContext 进行创建的,虽然名字不一样,看起来很奇怪的样子,但是就是这样!
结果如下,只application里的东西显示出来了。

Spring MVC 自定义数据转换器

数据转换器是指将客户端 HTTP 请求中的参数转换为业务方法中定义的形参,自定义表示开发者可以自主设计转换的方式,HandlerAdpter 已经提供了通用的转换,String 转 int,String 转 double,表单数据的封装等,但是在特殊的业务场景下,HandlerAdapter 无法进行转换,就需要开发者自定义转换器。
补充: 这里我们做的就是HandlerAdpter做不了的是事,这里回顾一下HandlerAdpter做的事是啥,可以回溯到前面看一下。这里简单讲一讲,就是HandAdpter拿到了客户端传来了数据,然后要给到具体的某个方法了,这个具体的某个方法是有@RequestMapping标注的地址确定的,然后HandAdpter把客户端传来的数据要装换成方法要求的数据类型,客户端传来的数据就是String类型的,一般方法要求的数据类型,是int啊,double啊之类的,HandlerAdpter自己内置的方法就能转换了,不用我们写了,但是有的确实转不了,就得你自己写啦。这里就是客户端传来的数据是String,要转换成Date类型,HandAdpter干不了,所以得我自己写一个数据转换器来转换啦,大概就是这么个逻辑(理解的前提是知道数据是怎么从客户端传到服务器,再由服务器传到客户端的)

客户端输入 String 类型的数据 “2019-03-03”,自定义转换器将该数据转为 Date 类型的对象。

  • 创建 DateConverter 转换器,实现 Converter 接口。
package com.southwind.converter;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateConverter implements Converter {

    private String pattern;

    public DateConverter(String pattern){
        this.pattern = pattern;
    }

    @Override
    public Date convert(String s) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.pattern);
        Date date = null;
        try {
            date = simpleDateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
  • springmvc.xml 配置转换器。


    
        
            
                
            
        
    



    
    
        
            
        
        
        
    

下面这一代码其实就是在构造DateConverter的bean,给的是有参构造的写法,type="java.lang.String"表示String,value=“yyyy-MM-dd”,表示传入的值是"yyyy-MM-dd。(如果不记得的话,记得回去看spring的笔记,里面是怎么配置spring.xml的)


  • JSP
<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-14
  Time: 14:47
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


    
请输入日期:(yyyy-MM-dd)
  • Handler
package com.southwind.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@RestController
@RequestMapping("/converter")
public class ConverterHandler {

    @RequestMapping("/date")
    public String date(Date date){
        return date.toString();
    }
}

构造date转换器的总结:
1.写一个jsp,传一个日期的string进来,并连接到某一个handler进行处理
2.写一个handler接住上面的string,在接到之前会经过HandlerAdpter的类型转换,因为我这个handler对应的方法的方法参数想要的类型HandlerAdpter没法自己从String转换过来,所以要补充一下HandlerAdpter的功能
3.写一个DateConverter的方法,构造方法传入参数是yyyy-MM-dd,convert实例方法传入参数为需要进行转换的字符串(从Converter接口继承得来的,重写完这个功能就行)。
4.在springmvc.xml构造一个DateConverter的bean(但得被一些东西包裹),也就是配置自定义转换器的步骤,然后再把这个自定义转换器的id,加到消息转换器里面就行(3,4对照代码看)

String 转 Student

StudentConverter

package com.southwind.converter;

import com.southwind.entity.Student;
import org.springframework.core.convert.converter.Converter;

public class StudentConverter implements Converter {
    @Override
    public Student convert(String s) {
        String[] args = s.split("-");
        Student student = new Student();
        student.setId(Long.parseLong(args[0]));
        student.setName(args[1]);
        student.setAge(Integer.parseInt(args[2]));
        return student;
    }
}

springmvc.xml



    
        
            
                
            
            
        
    



    
    
        
            
        
        
        
    

JSP

<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-14
  Time: 15:23
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


    
请输入学生信息:(id-name-age)

Handler

@RequestMapping("/student")
public String student(Student student){
    return student.toString();
}
Spring MVC REST

REST:Representational State Transfer,资源表现层状态转换,是目前比较主流的一种互联网软件架构,它结构清晰、标准规范、易于理解、便于扩展。

  • 资源(Resource)

网络上的一个实体,或者说网络中存在的一个具体信息,一段文本、一张图片、一首歌曲、一段视频等等,总之就是一个具体的存在。可以用一个 URI(统一资源定位符)指向它,每个资源都有对应的一个特定的 URI,要获取该资源时,只需要访问对应的 URI 即可。

  • 表现层(Representation)

资源具体呈现出来的形式,比如文本可以用 txt 格式表示,也可以用 HTML、XML、JSON等格式来表示。

  • 状态转换(State Transfer)

客户端如果希望操作服务器中的某个资源,就需要通过某种方式让服务端发生状态转换,而这种转换是建立在表现层(WEB页面之类的)之上的,所有叫做"表现层状态转换"。

特点
  • URL 更加简洁。
  • 有利于不同系统之间的资源共享,只需要遵守一定的规范,不需要进行其他配置即可实现资源共享。
如何使用

REST 具体操作就是 HTTP 协议中四个表示操作方式的动词分别对应 CRUD 基本操作。

GET 用来表示获取资源。

POST 用来表示新建资源。

PUT 用来表示修改资源。

DELETe 用来表示删除资源。

Handler

package com.southwind.controller;

import com.southwind.entity.Student;
import com.southwind.repository.StudentRepository;
import com.southwind.repository.impl.StudentRepositoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.Collection;

@RestController
@RequestMapping("/rest")
public class RestHandler {
    @Autowired
    private StudentRepository studentRepository;

    @GetMapping("/findAll")
    public Collection finAll(HttpServletResponse response){
        response.setContentType("text/json;charset=UTF-8");
        return studentRepository.findAll();
    }
    @GetMapping("/findById/{id111}")
    public  Student findById(@PathVariable("id111") long id){
        return studentRepository.findById(id);
    }
    @PostMapping("/save")
    public void save(@RequestBody Student student){
        studentRepository.saveOrUpdate(student);
    }

    @PutMapping("/update")
    public  void update(@RequestBody Student student){
        studentRepository.saveOrUpdate(student);
    }
    @DeleteMapping("/deleteById/{id222}")
    public void deleteById(@PathVariable("id222") long id){
        studentRepository.deleteById(id);

    }
}

我比较好奇的是为啥这里注入的是接口StudentRepository而不是studentRepositoryImpl实现类,刚刚试了一下是一样的,可能构造接口的对象,默认用了这studentRepositoryImpl类的创建方法吧

    @Autowired
    private StudentRepository studentRepository

StudentRepository

package com.southwind.repository;

import com.southwind.entity.Student;

import java.util.Collection;

public interface StudentRepository {
    public Collection findAll();
    public Student findById(long id);
    public void saveOrUpdate(Student student);
    public void deleteById(long id);
}

StudentRepositoryImpl

spring中的注解,@Repository用于标注数据访问组件,即DAO组件

package com.southwind.repository.impl;

import com.southwind.entity.Student;
import com.southwind.repository.StudentRepository;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Repository
public class StudentRepositoryImpl implements StudentRepository {

    private static Map studentMap;

    static{
        studentMap = new HashMap<>();
        studentMap.put(1L,new Student(1L,"张三",22));
        studentMap.put(2L,new Student(2L,"李四",23));
        studentMap.put(3L,new Student(3L,"王五",24));
    }

    @Override
    public Collection findAll() {
        return studentMap.values();
    }

    @Override
    public Student findById(long id) {
        return studentMap.get(id);
    }

    @Override
    public void saveOrUpdate(Student student) {
        studentMap.put(student.getId(),student);
    }

    @Override
    public void deleteById(long id) {
        studentMap.remove(id);
    }
}

postman测试以上Handler到底功能是否正常

Spring MVC 文件上传下载

单文件上传

底层是使用 Apache fileupload 组件完成上传,Spring MVC 对这种方式进行了封装。

  • pom.xml

    commons-io
    commons-io
    2.5



    commons-fileupload
    commons-fileupload
    1.3.3

  • JSP
<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-15
  Time: 09:09
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>


    Title


    

1、input 的 type 设置为 file。

2、form 的 method 设置为 post(get 请求只能将文件名传给服务器)

3、from 的 enctype 设置为 multipart-form-data(如果不设置只能将文件名传给服务器)

  • Handler
package com.southwind.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;

@Controller
@RequestMapping("/file")
public class FileHandler {

    @PostMapping("/upload")
    public String upload(MultipartFile img, HttpServletRequest request){
        if(img.getSize()>0){
            //获取保存上传文件的file路径
            String path = request.getServletContext().getRealPath("file");
            //获取上传的文件名
            String name = img.getOriginalFilename();
            File file = new File(path,name);
            try {
                img.transferTo(file);
                //保存上传之后的文件路径
                request.setAttribute("path","/file/"+name);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "upload";
    }
}
  • springmvc.xml


  • web.xml 添加如下配置,否则客户端无法访问 png

    default
    *.png

多文件上传

pom.xml


    jstl
    jstl
    1.2



    taglibs
    standard
    1.1.2

JSP

<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-15
  Time: 09:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


    Title


    
file1:
file2:
file3:

Handler

@PostMapping("/uploads")
public String uploads(MultipartFile[] imgs,HttpServletRequest request){
    List files = new ArrayList<>();
    for (MultipartFile img:imgs){
        if(img.getSize()>0){
            //获取保存上传文件的file路径
            String path = request.getServletContext().getRealPath("file");
            //获取上传的文件名
            String name = img.getOriginalFilename();
            File file = new File(path,name);
            try {
                img.transferTo(file);
                //保存上传之后的文件路径
                files.add("/file/"+name);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    request.setAttribute("files",files);
    return "uploads";
}

下载

  • JSP
<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-15
  Time: 10:36
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


    1.png
    2.png
    3.png


  • Handler
@GetMapping("/download/{name}")
public void download(@PathVariable("name") String name, HttpServletRequest request, HttpServletResponse response){
    if(name != null){
        name += ".png";
        String path = request.getServletContext().getRealPath("file");
        File file = new File(path,name);
        OutputStream outputStream = null;
        if(file.exists()){
            response.setContentType("application/forc-download");
            response.setHeader("Content-Disposition","attachment;filename="+name);
            try {
                outputStream = response.getOutputStream();
                outputStream.write(FileUtils.readFileToByteArray(file));
                outputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(outputStream != null){
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
Spring MVC 表单标签库
  • Handler
@GetMapping("/get")
public ModelAndView get(){
    ModelAndView modelAndView = new ModelAndView("tag");
    Student student = new Student(1L,"张三",22);
    modelAndView.addObject("student",student);
    return modelAndView;
}
  • JSP
<%--
  Created by IntelliJ IDEA.
  User: southwind
  Date: 2019-03-15
  Time: 10:53
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>


    Title


    学生信息
    
        学生ID:
学生姓名:
学生年龄:

1、JSP 页面导入 Spring MVC 表单标签库,与导入 JSTL 标签库的语法非常相似,前缀 prefix 可以自定义,通常定义为 from。

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

2、将 form 表单与模型数据进行绑定,通过 modelAttribute 属性完成绑定,将 modelAttribute 的值设置为模型数据对应的 key 值。

Handeler:modelAndView.addObject("student",student);
JSP:

3、form 表单完成绑定之后,将模型数据的值取出绑定到不同的标签中,通过设置标签的 path 属性完成,将 path 属性的值设置为模型数据对应的属性名即可。

学生ID:
学生姓名:
学生年龄:
常用的表单标签
  • from

渲染的是 HTML 中的

,通过 modelAttribute 属性绑定具体的模型数据。

  • input

渲染的是 HTML 中的 ,from 标签绑定的是模型数据,input 标签绑定的是模型数据中的属性值,通过 path 属性可以与模型数据中的属性名对应,并且支持及联操作。


  • password

渲染的是 HTML 中的 ,通过 path 属性与模型数据的属性值进行绑定,password 标签的值不会在页面显示。

  • checkbox

student.setFlag(false);
checkbox:

渲染的是 HTML 中的 ,通过 path 与模型数据的属性值进行绑定,可以绑定 boolean、数组和集合。

如果绑定 boolean 值,若该变量的值为 true,则表示该复选框选中,否则表示不选中。

如果绑定数组或者集合,数组/集合中的元素等于 checkbox 的 value 值,则选中。

student.setHobby(Arrays.asList("读书","看电影","玩游戏"));
modelAndView.addObject("student",student);
爱好:摄影
读书
听音乐
看电影
旅游
玩游戏
  • checkboxes

渲染的是 HTML 中的一组 ,是对 的一种简化,需要结合 items 和 path 属性来使用,items 绑定被遍历的集合或数组,path 绑定被选中的集合或数组,可以这样理解,items 为全部可选集合,path 为默认的选中集合。

student.setHobby(Arrays.asList("摄影","读书","听音乐","看电影","旅游","玩游戏"));
student.setSelectHobby(Arrays.asList("摄影","读书","听音乐"));
modelAndView.addObject("student",student);
爱好:

需要注意的是 path 可以直接绑定模型数据的属性值,items 则需要通过 EL 表达式的形式从域对象中获取数据,不能直接写属性名。

  • rabiobutton

渲染的是 HTML 中的一个 ,绑定的数据与标签的 value 值相等则为选中,否则不选中。

student.setRadioId(1);
modelAndView.addObject("student",student);
radiobutton:radiobutton
  • radiobuttons

渲染的是 HTML 中的一组 ,这里需要结合 items 和 path 两个属性来使用,items 绑定被遍历的集合或数组,path 绑定被选中的值,items 为全部的可选类型,path 为默认选中的选项,用法与 一致。

Map gradeMap = new HashMap<>();
gradeMap.put(1,"一年级");
gradeMap.put(2,"二年级");
gradeMap.put(3,"三年级");
gradeMap.put(4,"四年级");
gradeMap.put(5,"五年级");
gradeMap.put(6,"六年级");
student.setGradeMap(gradeMap);
student.setSelectGrade(3);
modelAndView.addObject("student",student);
学生年级:
  • select

渲染的是 HTML 中的一个