Spring MVC

SpringMVC

1.1 回顾Servlet

  • 新建一个普通的Maven项目,删除src目录

  • 在该项目中添加一个普通的maven项目模块

  • 添加web框架支持

  • 导入项目需要的依赖

<dependencies>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>5.2.5.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
       <version>2.5</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet.jsp</groupId>
       <artifactId>jsp-api</artifactId>
       <version>2.2</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
       <version>1.2</version>
   </dependency>
</dependencies>
  • 编写前端jsp页面(form表单接收用户输入信息)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%--form表单--%>
<form method="post" action="/servlet">
    <input type="text" name="method">
    <input type="submit">
</form>
</body>
</html>
  • 编写Servlet(接收前端数据并转发页面)
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.接收表单提交数据
        String method = req.getParameter("method");

        //2.处理数据
        if (method.equals("post")){
            //将post存入session
            req.getSession().setAttribute("msg","使用post请求");
        }else if (method.equals("get")){
            //将get存入session
            req.getSession().setAttribute("msg","使用get请求");
        }
        //3.转发或重定向
        req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

  • 编写用户提交数据后转发到的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%--取出数据展示即可--%>
${msg}
</body>
</html>
  • 在web.xml中注册servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    <servlet>
        <servlet-name>myServlet</servlet-name>
        <servlet-class>com.rm.servlet.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>myServlet</servlet-name>
        <url-pattern>/servlet</url-pattern>
    </servlet-mapping>
</web-app>
  • 注意点
    如果页面用户不可见,放在WEB-INF下。
    如果页面用户可见,放在web目录下

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.2 SpringMVC快速入门

  1. 新建一个Module,添加web的支持
  2. 确定导入了SpringMVC的依赖
  3. 配置web.xml,注册DispatcherServlet

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation=
         "http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">


    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </context-param>
    
    <!--注册DispatcherServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
         <!--关联一个springMVC配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
      
    </servlet>
    
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  1. 编写SpringMVC的配置文件,名称:springmvc-servlet.xml :[ servlet name]-servler.xml这里的名称按照官方的要求来的。

springmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
  1. 添加处理映射器
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
  1. 添加处理器适配器
 <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
  1. 添加视图解析器
 <!--添加视图解析器
     1.获取ModelAndView的数据
     2.解析ModelAndView的视图名字(hello)
     3.拼接视图名字,找到对应的视图(/WEB-INF/jsp/hello.jsp)
     4.将数据渲染到这个视图上
 -->
<bean id="InternalResourceViewResolver" 
 class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <!--添加前缀-->
     <property name="prefix" value="/WEB-INF/jsp/"/>
     <!--后缀-->
     <property name="suffix" value=".jsp"/>
 </bean>
  1. 编写我们要操作业务Controller,要么实现Controller接口,要么增加注解;需要返回一个ModelAndView,装数据,封视图
public class HelloController implements Controller {
 public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
  HttpServletResponse httpServletResponse) throws Exception {
     //ModelAndView:模型和视图
     ModelAndView mv = new ModelAndView();
     //封装对象,放在ModelAndView
     mv.addObject("msg","HelloSpringMVC");
     //封装要跳转的视图"/WEB-INF/jsp/hello.jsp"
     mv.setViewName("hello");
     return mv;
 }
}
  1. 将自己的类交给springIOC容器,注册bean
   <!--handler -->
 <bean id="/hello" class="com.rm.controller.HelloController"/>
  1. 写jsp页面,展示ModelAndView存放的数据,以及正常的展示页面

hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

可能遇到的问题

  • 404页面
    WEB-INF目录下没有生产lib目录
    解决方法:新建一个lib目录,将依赖复制进去

在这里插入图片描述

  • 505界面
    找不到springmvc-servlet.xml文件
    解决方法:在xml中配置
    如果不特意指定参数名为contextConfigLoction的元素,那么spring的ContextLoderListener监听器就会在/WEB-INF/下去寻找并加载该目录下的名为applicationContext.xml这个文件。So,我们应该在web.xml中添加< context-param>标签并再次指定spring核心文件的位置:
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:applicationContext.xml</param-value>
</context-param>

1.2.1 使用注解实现springMVC

  • 步骤
  1. 新建一个web项目
  2. 导入相关jar包(打包的时候保证有lib文件夹)
  3. 编写web.xml。注册DispatchServlet(映射到springMVC配置文件)
  4. 编写springMVC配置文件
  5. 创建对应的控制类,Controller
  6. 完善前端和Controller之间的对应
  7. 调试
  • 新建一个项目,项目结构

在这里插入图片描述

--编写web.xml 注意要使用4.0版本

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--dispatchServlet-->
    <servlet>
        <servlet-name>dispatchServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>

        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--所有请求都会经过该servlet-->
    <servlet-mapping>
        <servlet-name>dispatchServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

-- 编写springMVC配置文件 注意要先导入约束

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">


    <!--自动扫描包。让该包下的所有注解生效,方便IOC统一管理必须先导入约束-->
    <context:component-scan base-package="com.rm.controller"/>

    <!--让springMVC不处理静态资源-->
    <mvc:default-servlet-handler/>

    <!--支持mvc注解驱动,即添加处理映射器,处理器适配器
    在spring中一般采用@RequestMapping注解来完成映射关系要想使@RequestMapping注解生效
    必须向上下文中注册DefaultAnnotationHandLerMapping和一个AnnotationMethodHandLerAdapter实例
    这两个实例分别在类级别和方法级别处理。
    而annotation-driven配置帮助我们自动完成上述两个实例的注入。
    -->
    <mvc:annotation-driven/>

    <!--视图解析器-->
    <bean id="internalResourceViewResolver" 
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

-- 创建对应的控制类

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello(Model model){
        //model用来存数据
        model.addAttribute("msg","HelloSpringMVCAnnotation");

        return "hello";
    }
}

-- jsp页面(hello.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

使用SpringMVC必须配置的三大件
处理器映射器
处理器适配器
视图解析器

通常我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,不需要在xml中配置了

1.3 Controller及RestFul风格

1.3.1 控制器Controller

  • 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。
  • 控制器负责解析用户的请求并将其转换为一个模型。
  • 在Spring MVC中一个控制器类可以包含多个方法
  • 在Spring MVC中,对于Controller的配置方式有很多种

方式一:通过实现Controller接口

public class HelloController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
     HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();
        //存放数据
        mv.addObject("msg","HelloSpringMVC..........");
        //转发数据到页面
        mv.setViewName("hello");
        return mv;
    }
}

说明

  • 通过实现Controller接口是较老的方法
  • 缺点:一个控制器只能有一个方法,如果需要多个方法则必须新建多个Controller

方式二:使用@Controller注解

  • 用于声明该类注册到spring中,是一个控制器(在将IOC的时候,还有另外另个注解@Repository和@Service和@Component)
  1. 配置xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 第一步,注册DispatchServlet-->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!--绑定spring资源文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>

        <!--设置启动等级-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--设置所有请求都会经过该Servlet-->
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  1. 配置applicationContext.xml
<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--第二步,配置xml文件-->
    <!--自动扫描包,开启注解支持-->
    <context:component-scan base-package="com.controller"/>
    <!--配置处理器映射器及处理器适配器-->
    <mvc:annotation-driven/>
    <!--过滤静态资源-->
    <mvc:default-servlet-handler/>

    <!--配置视图解析器-->
    <bean id="internalResourceViewResolver" 
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>
  1. 创建java类
package com.controller;

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

@Controller
public class HelloController {
    //第三步,编写java类,实现视图跳转

    @RequestMapping("/t1")
    public String test1(Model model){
        //存放数据
        model.addAttribute("msg","<h1>Hello SpringMVC1</h1>");
        //跳转视图
        return "test1";
    }

    @RequestMapping("/t2")
    public String test2(Model model){
        //存放数据
        model.addAttribute("msg","Hello SpringMVC2");
        //跳转视图
        return "test1";
    }
}
  1. 编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

被@Controller注解标识的类中的所有方法,如果返回值是String,并且有具体页面可以跳转,都会被视图解析器解析

通过两个不同的请求可以实现跳转到同一个页面

  • 可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。
    RequestMapping

.@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

@Controller
@RequestMapping("/c2")
public class HelloController2 {

    @RequestMapping("/t1")//访问该地址需要在url地址栏输入.../c2/t1
    public String t1(Model model){
        model.addAttribute("msg","/c2/t1");
        return "test1";
    }
}

1.3.2 RestFul风格

概念

  • Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

功能

  • 资源:互联网所有的事物都可以被抽象为资源
  • 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
  • 分别对应添加、删除、修改、查询。

传统方式操作资源 通过不同的参数来实现不同的效果!方法单一,post和get.

使用RESTful操作资源 可以通过不同的请求方式来实现不同的效果!
如下:请求地址一样但是功能可以不同!


新建一个RestFulController类
在Spring MVC中可以使用@PathVariable注解,让方法参数的值对应绑定到一个URI模板变量上。

@Controller
public class RestFulController {

    //原来的传参:http://localhost/add?a=1&b=2
    //RestFul风格:http://localhost/add/1/2
    
    @RequestMapping("/add/{a}/{b}")
    public String add(@PathVariable int a,@PathVariable int b, Model model){
        int res = a+b;
        model.addAttribute("msg","结果为"+res);
        return "test1";
    }

}

可以实现同一URL,根据请求方式不同,结果也不同

package com.controller;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
public class RestFulController {


    //原来的传参:http://localhost/add?a=1&b=2
    //RestFul风格:http://localhost/add/1/2
    //@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.DELETE)

    //http://localhost/add/1/3 点击表单提交按钮,走的是post请求
    @PostMapping("/add/{a}/{b}")//以post方式请求
    public String add(@PathVariable int a,@PathVariable int b, Model model){
        int res = a+b;
        model.addAttribute("msg","结果1为"+res);
        return "test1";
    }

    //http://localhost/add/1/3 在url地址栏直接传参数走的是get请求(可以达到url的复用)
    @GetMapping("/add/{a}/{b}")//以get方式请求
    public String add2(@PathVariable int a,@PathVariable int b, Model model){
        int res = a+b;
        model.addAttribute("msg","结果2为"+res);
        return "test1";
    }


}

  • a.jsp(以post方式提交表格数据到/add/)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<form action="/add/1/3" method="post">
    <input type="submit">
</form>

</body>
</html>

1.4 转发和重定向

1.4.1 有视图解析器

转发

  • 直接return的就是到拼接好的转发页面
  • return "test";

重定向

@Controller
public class TestController1 {

    @RequestMapping("/c1/t1")
    public String t1(Model model){

        model.addAttribute("msg","/c1/t11");
        //重定向
        //会跳转到首页http://localhost/index.jsp?msg=%2Fc1%2Ft11
        return "redirect:/index.jsp";
    }
}

1.4.2 无视图解析器

  • 注意,无视图解析器就没有ModelAndView,所以无法使用Model存放数据
@Controller
public class TestController1 {

    @RequestMapping("/c1/t1")
    public void t1(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
        request.setAttribute("msg","无视图解析器转发");
        //转发
        request.getRequestDispatcher("/WEB-INF/jsp/test1.jsp").forward(request,response);
        
        //重定向 http://localhost/index.jsp
        response.sendRedirect("/index.jsp");
    }
}

1.5 SpringMVC数据处理

处理提交数据

  1. 提交的域名称和处理方法的参数名一致
@Controller
@RequestMapping("/user")
public class UserController {

    @GetMapping("/t1")
    //http://localhost/user/t1?name=zhangsan
    public String t1(String name, Model model){
        //接收前端传递的数据
        System.out.println("前端输入的数据:"+name);
        //将数据传递给前端
        model.addAttribute("msg","name="+name);
        //转发到前端页面
        return "test1";
    }
}
  1. 提交的域名称和处理方法的参数名不一致
  • 在参数前加上@RequestParam注解(前端参数名)表示该参数是从前端接收过来的
@Controller
@RequestMapping("/user")
public class UserController {

    @GetMapping("/t1")
    //http://localhost/user/t1?username=zhangsan
    public String t1(@RequestParam("username") String name, Model model){
        //接收前端传递的数据
        System.out.println("前端输入的数据:"+name);
        //将数据传递给前端
        model.addAttribute("msg","name="+name);
        //转发到前端页面
        return "test1";
    }
}
  • 前端传递的参数是一个对象格式的数据
@Controller
@RequestMapping("/user")
public class UserController {

    @GetMapping("/t2")
    public String t2(User user,Model model){
        //接收前端传递的数据
        /*
            1.接收前端用户传递的参数,判断参数的名字,如果参数名在方法上,可以直接使用
            2.假设传递的是一个对象,如果前端传入的字段和类中的属性一致,则匹配的到,不一样的字段显示为Null
         */
        //http://localhost/user/t2?id=1&name=zhangsan&age=5
        System.out.println("前端输入的数据:"+user);
        //将数据传递给前端
        model.addAttribute("msg","user="+user);
        //转发到前端页面
        return "test1";
    }
}
  • Model只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;
  • ModelMap继承了LinkedNap ,除了实现了自身的一些方法,同样的继承 linkedMap 的方法和特性;
  • ModelAndview可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

1.5.1 解决乱码问题

在这里插入图片描述

  • form.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<form action="/e/t1" method="post">
name<input type="text" name="name">
    <input type="submit">

</form>
</body>
</html>
  • EncodingController
@Controller
public class EncodingController {

    @PostMapping("/e/t1")
    public String t1(@RequestParam("name") String name, Model model){
        model.addAttribute("msg",name);
        return "test1";
    }
}

在这里插入图片描述

解决方法

  • 使用SpringMVC的过滤器,在web.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--注册DispatchServlet-->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
 	<!--配置编码过滤器-->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

注意

  • 过滤器拦截路径要使用/* (会过滤jsp页面)
  • 如果使用/ 则不会通过过滤器

1.6 JSON

1.6.1 什么是JSON

  • JSON(JavaScript object Notation, JS对象标记)是一种轻量级的数据交换格式,目前使用特别广泛。
  • 采用完全独立于编程语言的文本格式来存储和表示数据。
  • 简洁和清晰的层次结构使得JSON成为理想的数据交换语言。
  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

在JavaScript 语言中,一切都是对象。因此,任何JavaScript支持的类型都可以通过JSON来表示,例如字符串、数字、对象、数组等。看看他的要求和语法格式:

  • 对象表示为键值对,数据由逗号分隔
  • 花括号保存对象
  • 方括号保存数组

JSON键值对是用来保存JavaScript对象的一种方式,和JavaScript对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号""包裹,使用冒号∶分隔,然后紧接着值:

1.6.2 JSON 和JavaScript对象互转

  • 要实现从JSON字符串转换为JavaScript对象,使用JSON.parse()方法:
var obj = soN.parse( ' { "a": "Hello","b": "world"}');
//结果是{ a: 'Hello', b: 'world' }
  • 要实现从JavaScript对象转换为JSON字符串,使用JSON.stringify()方法:
var json = SON. stringify({a: 'Hello', b: 'world '});
//结果是'{ " a" : "Hello","b": "world"}'

在这里插入图片描述

测试代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <script type="text/javascript">
        var user = {
            name:"张三",
            age:12
        };
        //将对象转换为JSON格式
        var json =JSON.stringify(user);
        console.log(json);
        //将JSON格式转换为JavaScript数据格式
        var user =JSON.parse(json);
        console.log(user);

    </script>
</head>

<body>

</body>
</html>

1.6.3 Jackson

Controller返回JSON数据

在Maven项目中导入依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.0</version>
</dependency>
  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--注册dispatchServlet-->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <!--添加过滤器-->
    <filter>
        <filter-name>EncodingFilter</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>EncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
  • springMVC.xml
<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">


    <!--自动扫描包。让该包下的所有注解生效,方便IOC统一管理必须先导入约束-->
    <context:component-scan base-package="com.rm.controller"/>
    <!--让springMVC不处理静态资源-->
    <mvc:default-servlet-handler/>


    <!--视图解析器-->
    <bean id="internalResourceViewResolver" 
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>


</beans>
  • User实体类(导入lombok依赖)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}

不用jackson,直接返回一个对象

@Controller
public class JSController {

    @RequestMapping("/a1")
    @ResponseBody  //加上该注解,这个方法就不会走视图解析器,会直接返回一个字符串
    public String json1(){
        User user = new User(1, "张三", "qwertasdfg");
        System.out.println(user);
        return user.toString();
    }
}
  • 在浏览器上显示结果

在这里插入图片描述

使用ObjectMapper

@Controller
public class JSController {

    @RequestMapping("/a1")
    @ResponseBody
    public String json1() throws JsonProcessingException {

        ObjectMapper mapper = new ObjectMapper();
        User user = new User(1, "张三", "qwertasdfg");
        String s = mapper.writeValueAsString(user);
        return s;
    }
}
  • 浏览器显示

在这里插入图片描述

  • 发现出现了乱码问题,需要设置编码格式为utf-8,以及返回数据类型
  • 通过@RequestMapping中的produces属性来解决
@RequestMapping(value = "/a1",produces = "application/json;charset=utf-8")
  • 再次测试,问题解决

在这里插入图片描述

代码优化

乱码统一解决

  • 上一种方法比较麻烦,如果项目中有许多请求则每一个都要添加,可以通过Spring配置统一指定,这样就不用每次都去处理了!
  • 我们可以在springmvc的配置文件上添加一段消息StringHttpMessageConverter转换配置!
    <!--解决json数据转换乱码问题-->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="utf-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

@RestController

  • 该注解作用在类上,表示该类中的所有方法都不会走视图解析器,都会返回一个字符串。
  • 前后端分离的话需要返回JSON格式的数据用该注解
@RestController
public class JSController {
    @RequestMapping("/a1")
    public String json1() throws JsonProcessingException {

        ObjectMapper mapper = new ObjectMapper();
        User user = new User(1, "张三", "qwertasdfg");
        String s = mapper.writeValueAsString(user);
        return s;
    }
}

将集合数据转换为Json数据输出

 @RequestMapping("/a2")
    public String json2() throws JsonProcessingException {
        
        List<User> list = new ArrayList<User>();
        User user1 = new User(1, "张三", "qwertasdfg");
        User user2 = new User(2, "张四", "qwertasdfg");
        User user3 = new User(3, "张五", "qwertasdfg");
        User user4= new User(4, "张六", "qwertasdfg");
        
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
        
        return new ObjectMapper().writeValueAsString(list);
    }

在这里插入图片描述

[{"id":1,"name":"张三","password":"qwertasdfg"},
{"id":2,"name":"张四","password":"qwertasdfg"},
{"id":3,"name":"张五","password":"qwertasdfg"},
{"id":4,"name":"张六","password":"qwertasdfg"}]

将Date数据展示到页面上

    @RequestMapping("/a3")
    public String json3() throws JsonProcessingException {

        Date date = new Date();
        //hh代表12小时制
        //HH代表24小时制
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

        String s = dateFormat.format(date);

        return new ObjectMapper().writeValueAsString(s);
    }

在这里插入图片描述

不使用时间戳的方式

  • 不使用SimpleDateFormat类
  • 使用ObjectMapper类完成
    @RequestMapping("/a4")
    public String json4() throws JsonProcessingException {

        ObjectMapper mapper = new ObjectMapper();
        //不使用时间戳的方式
        mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
        //自定义日期格式

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        mapper.setDateFormat(simpleDateFormat);
        Date date = new Date();


        return mapper.writeValueAsString(date);
    }

提取为一个工具类

  • JSutil .java
public class JSutil {

    public static String getJson(Object object,String dateFormat){
        ObjectMapper mapper = new ObjectMapper();
        //不使用时间戳的方式
        mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
        //自定义日期格式

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
        mapper.setDateFormat(simpleDateFormat);

        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  • 使用
    @RequestMapping("/a4")
    public String json4() {
        Date date = new Date();
        return JSutil.getJson(date,"yyyy-MM-dd HH:mm:ss");
    }

需求:用户输入一个参数,也有默认的时间格式

  • 方法重写
public class JSutil {

    public static String getJson(Object object,String dateFormat){
        ObjectMapper mapper = new ObjectMapper();
        //不使用时间戳的方式
        mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
        //自定义日期格式

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
        mapper.setDateFormat(simpleDateFormat);

        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    //如果用户不输入日期格式,则有默认的
    public static String getJson(Object object){
       return JSutil.getJson(object,"yyyy-MM-dd HH:mm:ss");
    }

}
  • 测试
 @RequestMapping("/a4")
    public String json4() {
        Date date = new Date();
        return JSutil.getJson(date);
    }

1.6.4 FastJson

  • fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是一样的。
<dependency>
	<groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.4</version>
</dependency>

fastjson三个主要的类:
【JSONObject代表json对象】

  • JSONobject实现了Map接口,猜想JSONObject底层操作是由Map实现的。
  • JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取"键:值"对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。

【JSONArray 代表json对象数组】

  • 内部是有List接口中的方法来完成操作的。

【JSON代表JSONObject和JSONArray的转化】

  • JSON类源码分析与使用
  • 仔细观察这些方法,主要是实现json对象,json对象数组,javabean对象,json字符串之间的相互转化。
system.out.println("*******Java对象转JSON字符串*******");
string str1 = JSON.toJSONString(userList) ;

system.out.println( "JSON.toJSONString(list)==>"+str1);
string str2 = JSON.toSONString(user1);
system.out.print1n( "JSON.toJSONString(user1)==>"+str2);

system.out.println("******JSON字符串转Java对象*******");
User jp_user1=JSON.parseobject(str2,User.class);
system.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);

system.out.println( "******Java对象转JSON对象******");
JSONObject jsonobject1 = (JSONObject)JSON.toJSON(user2);
System.out.println("(JSONObject)JSON.toJSON(user2)==>"+json0bject1.getString( key. "name"));

system.out.println("******JSON对象转Java对象******");
User to_java_user = JSON.toJava0bject(jsonobject1,User.class);
system.out.println("JSON.toJavaObject(json0bject1,User.class)=z>"+to_java_usen);

1.7 拦截器概述

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

过滤器与拦截器的区别:拦截器是AOP思想的具体应用。

  • 过滤器
    • servlet规范中的一部分,任何java web工程都可以使用
    • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
  • 拦截器
    • 拦截器是Spring MVC框架自己的,只有使用了SpringMVC框架的工程才能使用
    • 拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css/image/js是不会进行拦截的

1.7.1 自定义拦截器

  • 要想自定义实现拦截器,需要实现HandlerInterceptor接口
  1. 新建一个Model,添加web框架
  2. 配置web.xml和applicationContext.xml
  3. 测试环境配置是否成功
  4. 编写一个拦截器

MyInterceptor

public class MyInterceptor implements HandlerInterceptor {

    //return true :放行,执行下一个拦截器
    //return false :不执行下一个拦截器
    public boolean preHandle(HttpServletRequest request,
     HttpServletResponse response, Object handler) throws Exception {
        System.out.println("=========处理前========");
        return true;
    }

    //这两个方法没有返回值,通常用来添加日志功能
    public void postHandle(HttpServletRequest request,  HttpServletResponse 
    response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("=========处理后========");
    }

    public void afterCompletion(HttpServletRequest request,
     HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("=========清理========");
    }
}

TestController

@Controller
public class TestController {


    @ResponseBody
    @RequestMapping("/t1")
    public String t1(){

        System.out.println("拦截器执行了。。。。");
        return "Hello";
    }
}
  • 当用户在浏览器输入该访问地址时,会先走拦截器preHandle方法

在这里插入图片描述

  • 方法执行顺序

在这里插入图片描述

  • 如果preHandle方法返回值是false,则只执行该方法,不会继续执行Controller中的方法

1.7.2 拦截器实现登录判断验证

  • 需求
  • 用户不登录的情况不能访问主页
  • 需要用到session存放用户的数据,用户点击注销即删除session中的信息

用户登录存放session

@RequestMapping("/login")
    public String login(String userName, String password, Model model, 
    HttpServletRequest request){
        //获取session
        HttpSession session = request.getSession();
        User user = userService.login(userName, password);
        model.addAttribute("user",user);
        if (user !=null){
            //证明登录成功,将数据存入session
            session.setAttribute("user",user);
            return "redirect:/book/all";
        }
        return "error";
    }

拦截器查看是否存在user的session

  • 如果存在,放行
  • 如果不存在,不放行,重定向到登录页
package com.rm.config;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

//配置拦截器
public class MyInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request,
     HttpServletResponse response, Object handler) throws Exception {
        //获取session
        HttpSession session = request.getSession();
        if (session.getAttribute("user")!=null){
            return true;
        }else{
            //重定向到登录页面
            response.sendRedirect("/index.jsp");
            return false;
        }
    }
}

配置拦截器

  • /book下的所有请求都会被拦截器拦截
<!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/book/**"/>
            <bean class="com.rm.config.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

添加注销功能

  • 用户点击注销按钮会移除session中的user
  • 移除后再输入/book/all会重定向到登录页

前端

<a  class="btn btn-primary"
 href="${pageContext.request.contextPath}/book/remove">注销用户</a>

后端

 //添加注销功能,用户点击注销即移除session,跳转登录页
    @RequestMapping("/book/remove")
    public String remove(HttpServletRequest request){
        HttpSession session = request.getSession();
        session.removeAttribute("user");
        return "redirect:/index.jsp";
    }

在这里插入图片描述

服务器重启会移除所有session

1.8 文件上传和下载

准备工作

  • 文件上传是项目开发中最常见的功能之一,springMVC可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。

  • 前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;

对表单中的enctype属性做个详细的说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的value属性值,采用这种编码方式的表单会将表单域中的值处理成URL编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为“+”号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache SoftwarFoundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。

  • Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。·而Spring MVC则提供了更简单的封装。
  • Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
  • Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache
    Commons FileUpload的组件
  1. 导包
    commons-fileupload
    servlet-api 必须用高版本的
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>
  1. 配置bean: multipartResolver

【注意!!!这个bean的id必须为: multipartResolver,否则上传文件会报400的错误!在这里栽过坑,教训!】
web.xml

<!--文件上传配置-->
<bean id="multipartResolver" 
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--请求的编码格式,必须和jSP的pageEncoding属性一致,
    以便正确读取表单的内容,默认为ISO-8859-1 -->
    <property name="defaultEncoding" value="utf-8"/>
    <!-- 上传文件大小上限,单位为字节(10485760=10M)-->
    <property name="maxUploadSize" value="10485760"/>
    <property name="maxInMemorySize" value="40960"/>

</bean>

前端

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>

  <form action="${pageContext.request.contextPath}/upload" method="post" 
  enctype="multipart/form-data">
    <input name="file" type="file">
    <input type="submit" value="upload">

  </form>
  </body>
</html>

FileController

@RestController
//文件上传是一个动作,不需要返回一个页面
public class FileController {

    //@RequestParam("file")将name=file控件得到的文件封装成
	//CommonsMultipartFile 对象,批量上传CommonsMuLtipartFiLe则为数组即可
    @RequestMapping( "/upload")
    public String fileupload(@RequestParam("file") CommonsMultipartFile file , 
    HttpServletRequest request) throws IOException {
        //获取文件名:file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();
        //如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName) ){
            return "redirect: /index.jsp" ;
        }
        System.out.println("上传文件名: "+uploadFileName);
        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if ( ! realPath.exists()){
             realPath.mkdir();
        }
        System.out.println("上传文件保存地址:"+realPath);
        //文件输入流
        InputStream is = file.getInputStream(); 
        //文件输出流
        FileOutputStream os = new FileOutputStream(new File(realPath, uploadFileName));
        //读取写出
        int len=0;
         byte[] buffer = new byte[1024];
         while ( ( len=is.read(buffer) )!=-1){
             os.write( buffer,0,len);
             os.flush();
         }
         os.close();
         is.close();

         //重定向到首页
        return "redirect:/index.jsp";

    }

    /**采用file.Transto来保存上传的文件
    */
    @RequestMapping( "/upload2")
    public String fileupload2(@RequestParam("file")CommonsMultipartFile file,
    HttpServletRequest request) throws IOException{
        //上传路径保存设置
         String path =  request.getServletContext().getRealPath("/upload" );
         File realPath = new File(path);
        if ( !realPath.exists()) {
            realPath.mkdir();
        }
        //上传文件地址
        System.out.println("上传文件保存地址:"+realPath);
        //通过过CommonsMultipartFiLe的方法直接写文件(注意这个时候)
        file.transferTo(new File( realPath +"/"+ file.getOriginalFilename()));
        return "redirect: /index.jsp";
    }

}

下载文件

 @RequestMapping("/download")
    public String downloads(HttpServletResponse response,
     HttpServletRequest request) throws Exception {
        //要下载的图片地址
        String path = request.getServletContext().getRealPath("/upload");
        String fileName = "数据结构与算法.jpg";
        //1,设置response 响应头
        response.reset();//设置页面不缓存,清空buffer
        response.setCharacterEncoding("UTF-8");//字符编码
        response.setContentType("multipart/form-data");//二进制传输数据/设置啊应头
        response.setHeader("content-Disposition", "attachment;fileName=" + 
       URLEncoder.encode(fileName, "UTF-8"));
        File file = new File(path, fileName);
        //2、读取文件--输入流
        InputStream input = new FileInputStream(file);
        //3、写出文件--输出流
        OutputStream out = response.getOutputStream();
        byte[] buff = new byte[1024];
        int index = 0;
        //4、执行写出操作
        while ((index = input.read(buff)) != -1) {
            out.write(buff, 0, index);
            out.flush();
            out.close();
            input.close();

        }
        return null;
    }

在这里插入图片描述

posted @ 2021-11-04 15:53  初夏那片海  阅读(23)  评论(0)    收藏  举报