MVC:模型(dao、service) 视图(jsp) 控制器(servlet)

什么是MVC?

  • MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。
  • 是将业务逻辑、数据、显示分离的方法来组织代码。
  • MVC主要作用是降低了视图与业务逻辑间的双向耦合
  • MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在不同的差异。

面试题
假设:你的项目的架构是设计好的?还是演进的?

  • Alibaba PHP
  • 随着用户大 java
  • 王坚 去IOE MySQL
  • MySQL:MySQL —> AliSQL、AliRedis
  • All in one —> 微服务

1、回顾Servlet

导入依赖
image.png

见项目 D:\IntelliJ IDEA workSpace\Maven\javaEE\SpringMVC\spring-01-servlet

MVC框架要做哪些事情

  1. 将url映射到java类或者java类的方法
  2. 封装用户提交的数据
  3. 处理请求 — 调用相关的业务处理 — 封装相应数据
  4. 将相应的数据进行渲染 .jsp / html 等表示层数据

说明:
常见的服务器MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framwork、JSF;常见前端MVC架构:vue、angularjs、react、backbone;由于MVC演化出了另外一些模式,如:MVP、MVVM等等……

2、初识SpringMVC

Spring MVC 是 Spring Framework 的一部分,是基于Java实现MVC的轻量级web框架。

为什么要学习Spring MVC呢?

  1. 轻量级,简单易学
  2. 高效,基于请求响应的MVC框架
  3. 与Spring兼容性好,无缝结合
  4. 约定大于配置
  5. 功能强大:RESTful风格、数据验证、格式化、本地化、主题等
  6. 简介灵活

2.1、中心控制器

Spring 的 web 框架围绕 DispatcherServlet 设计。DispatcherServlet的作用是将请求分发到不同的处理器。从Spring2.5 开始,使用Java 5 或者以上版本的用户可以采用基于注解的 controller 声明方式。
Spring MVC 框架像许多其他MVC框架一样,以请求为驱动,围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet(它继承自HttpServlet基类)。
DispatcherServlet:也可以叫做前端控制器

image.png

image.png

DispatcherServlet执行流程:

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DIspatcherServlet 接收请求并拦截请求。
    1. 我们假设请求的 url 为:http://localhost:8080/SpringMVC/hello
    2. 如上 url 可以拆分成三部分
    3. http://localhost:8080 服务器域名
    4. SpringMVC 部署在服务器上的 web 站点
    5. hello 表示控制器
    6. 通过分析,如上url 表示为:请求位于服务器 localhost://8080 上的 SpringMVC 站点 的hello 控制器。
  2. HandlerMapper 为处理器映射。DispatcherServlet 调用HandlerMapper,Handler Mapping根据请求 url 查找Handler
  3. HandlerExecution 表示具体的 Handler,其主要作用是根据 url 查找控制器,如上 url 被查找控制器为:hello
  4. HandlerExecution 将解析后的信息传递给 DispatcherServlet,如解析控制器映射等。
  5. HandlerAdapter 表示处理器适配器,其按照特性的规则去执行 Handler。
  6. Handler 让具体的Controller 执行。
  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。
  11. DispacherServlet 根据视图解析器解析的视图结果,调用具体的视图。
  12. 渲染数据,将最终视图呈现给用户。

实线是SpringMVC帮我们的操作,虚线是我们自己明面上的操作。
未命名文件.png

2.2、HelloSpringMVC原理

image.png

  1. 首先配置web.xml配置文件 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

    1. <servlet-name>springmvc</servlet-name>
    2. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    3. <!--DispatcherServlet要绑定SpringMVC(说是SpringMVC,其实本质还是Spring)的配置文件-->
    4. <init-param>
    5. <param-name>contextConfigLocation</param-name>
    6. <param-value>classpath:springmvc-servlet.xml</param-value>
    7. </init-param>
    8. <!--启动级别:1(服务器一启动,我就启动)-->
    9. <load-on-startup>1</load-on-startup>

     <servlet-name>springmvc</servlet-name>
     <url-pattern>/</url-pattern>
    


2. 因为web.xml中初始化参数绑定了 springmvc-servlet.xml,所以配置 springmvc-servlet.xml
```xml
<?xml version="1.0" encoding="UTF8"?>
<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
         http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--处理器映射器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

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

    <!--因为BeanNameUrlHandlerMapping是根据bean的id名来找映射路径,所以我们需要在这里配置一个对应的bean-->
    <bean id="/hello" class="com.jungle.controller.HelloController"/>

</beans>
  1. 编写Controller ```java package com.jungle.controller;

import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller;

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

public class HelloController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mv = new ModelAndView();

    // 业务代码
    String str = "Hello SpringMVC!";
    mv.addObject("msg", str);

    // 视图跳转
    mv.setViewName("test");

    return mv;
}

}


4. 编写视图层
```html
<%--
  Created by IntelliJ IDEA.
  User: PC
  Date: 2022-03-02
  Time: 0:58
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

2.2.1、可能遇到的问题:

  1. 查看控制台输出,看一下是不是缺少了什么 jar 包
  2. 如果 jar 包存在,显示无法输出,就在IDEA的项目发布中,添加 lib 依赖
  3. 重启Tomcat即可解决!

image.png
image.png

  1. 可能存在静态资源过滤的问题,这个问题在pom.xml导入静态资源过滤就行了。

2.3、使用注解开发HelloSpringMVC

  1. 配置web.xml

这一块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">

    <!--配置DispatcherServlet:这个是SpringMVC的核心;叫请求分发器或者,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定SpringMVC(说是SpringMVC,其实本质还是Spring)的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1(服务器一启动,我就启动)-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  1. 配置springmvc-servlet.xml 【这一块的代码也是死的】 ```xml <?xml version=”1.0” encoding=”UTF8”?>

![image.png](https://cdn.nlark.com/yuque/0/2022/png/21723656/1646213697030-fe0800c9-16ee-43e8-a1ee-d86967714276.png#clientId=ue0b1fab6-d199-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=204&id=jS1yJ&margin=%5Bobject%20Object%5D&name=image.png&originHeight=204&originWidth=994&originalType=binary&ratio=1&rotation=0&showTitle=false&size=189635&status=done&style=none&taskId=u9485818d-da88-4530-abf3-557d5a3ead0&title=&width=994)

3. 编写controller
```java
package com.jungle.controller;

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

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello(Model model){
        // 向模型中添加属性msg,可以在jsp页面中被取出然后渲染
        model.addAttribute("msg", "Hello SpringMVC Annotation");

        // 会被视图解析器解析成 WEB-INF/jsp/hello.jsp
        return "hello";
    }
}
  1. 编写视图层
    <%--
    Created by IntelliJ IDEA.
    User: PC
    Date: 2022-03-02
    Time: 17:35
    To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
     <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    

步骤:

  1. 新建一个项目
  2. 导入相关jar 包
  3. 编写web.xml,注册 DispatcherServlet
  4. 编写springmvc 配置文件
  5. 接下来就是去创建对应的控制类,controller
  6. 最后完善前端视图和 controller 之间的对应
  7. 测试运行调试

使用 SpringMVC 必须配置的三大件:

  1. 处理器映射
  2. 处理器适配器
  3. 视图解析器

通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,而省去了大段的 xml 配置。

2.4、小结

controller层实现Controller的方式是一种比较老的方法,尽量不用。而且它还有一个缺点,就是一个类只能实现一个bean,这样效率太低了,并且每次写完一个controller,都要去springmvc-servlet.xml 配置一个bean。太麻烦!

@Controller 注解一个类:代表这个类会被Spring接管,被这个注解的类,中的所有方法,如果返回值是String,并且有具体的页面可以跳转,那么就会被视图解析器解析;

我们可以将多个请求指向一个视图,但页面结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱耦合关系。、

3、RestFul风格

优点:简洁、高效、安全

原来的风格:localhost:8080/method?add=1 & name=
Restful风格:localhost:8080/method/add/1/2/3

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

image.png
image.png

学习测试:

  1. 新建一个类RestFulController

    @Controller
    public class RestFulController {
    }
    
  2. 在SpringMVC中可以使用 @PathVariable 注解,让方法的值对应绑定到一个URL模板变量上。 ```java package com.jungle.controller;

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

@Controller public class RestFulController { // 映射访问路径 @RequestMapping(“/commit/{p1}/{p2}”) public String index(@PathVariable int p1, @PathVariable int p2, Model model){ int result = p1 + p2; // SpringMVC会自动实例化一个Model对象用于向视图中传值 model.addAttribute(“msg”, “结果为:” + result); // 返回视图位置 return “test”; } }


3. 通过不同去请求方式来实现不同的效果
```java
@Controller
public class RestFulController {
    // 映射访问路径    源码默认使用 value 或者 path
    @RequestMapping(value = "/commit/{p1}/{p2}", method = RequestMethod.GET)
    public String index(@PathVariable int p1, @PathVariable int p2, Model model){
        int result = p1 + p2;
        // SpringMVC会自动实例化一个Model对象用于向视图中传值
        model.addAttribute("msg", "结果为:" + result);
        // 返回视图位置
        return "test";
    }
}

小结:
SpringMVC的@RequestMapping 注解能够处理 HTTP 请求的方法,比如 GET,PUT,POST,DELETE 以及 PATCH。
所有的地址栏请求默认都会时 HTTP GET 类型的。
方法级别的注解变体有如下几个:组合注解

@GetMapping
@PostMapping
@PutMapping
@DeleteMappint
@PatchMapping

例如:

@Controller
public class RestFulController {

    @GetMapping(value = "/commit/{p1}/{p2}")
    public String index(@PathVariable int p1, @PathVariable int p2, Model model){
        int result = p1 + p2;
        // SpringMVC会自动实例化一个Model对象用于向视图中传值
        model.addAttribute("msg", "结果为:" + result);
        // 返回视图位置
        return "test";
    }

    @PostMapping(value = "/commit/{p1}/{p2}")
    public String index2(@PathVariable int p1, @PathVariable int p2, Model model){
        int result = p1 + p2;
        // SpringMVC会自动实例化一个Model对象用于向视图中传值
        model.addAttribute("msg", "结果为:" + result);
        // 返回视图位置
        return "test";
    }
}

4、转发和重定向

SpringMVC:结果跳转方式

  1. 转发
  2. 重定向

4.1、ModelAndView

设置ModelAndVIew对象,根据View的名字,和视图解析器跳到指定的页面

页面:{视图解析器前缀} + viewName + {视图解析器后缀}

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

对应的controller类

public class HelloController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();
        // 业务代码
        String str = "Hello SpringMVC!";
        mv.addObject("msg", str);
        // 视图跳转
        mv.setViewName("test");
        return mv;
    }
}

4.2、ServletAPI

通过设置ServletAPI,不需要视图解析器

  1. 通过HttpServletResponse进行输出
  2. 通过HttpServletResponse实现重定向
  3. 通过HttpServletResponse实现转发

之前JavaWeb实现转发和重定向
image.png

4.3、SpringMVC

在不配置视图解析器的情况下:

  1. 转发

    @Controller
    public class ModelTest1 {
     @RequestMapping("/m1/t1")
     public String test1(Model model){
         model.addAttribute("msg", "ModelTest1");
         return "forward:/WEB-INF/jsp/test.jsp";     // 如果是/WEN-INF/jsp/test.jsp,那么默认的也是转发
     }
    }
    
  2. 重定向

    @Controller
    public class ModelTest1 {
     @RequestMapping("/m1/t1")
     public String test1(Model model){
         return "redirect:/index.jsp";
     }
    }
    

配置了视图解析器:

@Controller
public class ModelTest1 {
    // 1. 转发
    @RequestMapping("/m1/t1")
    public String test1(Model model){
        model.addAttribute("msg", "ModelTest1");
        return "test";
    }

    // 2. 重定向
    @RequestMapping("/m1/t2")
    public String test1(Model model){
        return "redirect:/index.jsp";
    }
}

5、SpringMVC数据处理

5.1、接收请求参数

  1. 提交的域名称和处理方法的参数名一致

提交数据:http://localhost:8080/hello?name=kuangshen

处理方法:

@RequestMapping("/hello")
public String hello(String name){
    System.out.println(name);
    return "hello";
}

后台输出:kuangshen

  1. 提交的域名称和处理方法的参数名不一样

提交数据:http://localhost:8080/hello?usernName=kuangshen

处理方法:

@RequestMapping("/hello")
public String hello(@RequestParam("userName")String name){
    System.out.println(name);
    return "hello";
}

后台输出:kuangshen

  1. 提交的是一个对象

要求提交的表单域和对象的属性名一致,参数使用对象即可

  1. 实体类

    public class User{
    private int id;
    private String name;
    private int age;
    // 构造
    // get/set
    // toString()
    }
    
  2. 提交数据:http://localhost:8080/mvc04/user?name=kuangshen%id=1&age=15

  3. 处理方法:
    @RequestMapping("/user")
    public String user(User user){
    System.out.println(user);
    return "hello";
    }
    
    后台输出:User(id=1, name=qinjiang, age=15);

说明:如果使用对象的话,前端传递的参数名和对象的属性名必须一致,否则返回null。

5.2、数据显示到前端

第一种:通过 ModelAndVIew
我们一直都是如此,就不必多说了。

第二种:通过ModelMap

第三种:通过Map

SpringMVC - 图11

对于新手来说,区别就是:

Model    只有寥寥几个方法只适合用于存储数据,简化了新手对于Model对象的操作和理解
ModelMap    继承了LinkedHashMap,除了实现自身的一些方法,同样继承了LinkedHashMap的方法和特性
ModelAndView    可以在存储数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转

6、JSON

SpringMVC - 图12

6.1、什么是JSON

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

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

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

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

{"name" : "Jungle"}
{"age" : "3"}
{"sex" : "man"}

JSON初体验

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

    <script type="text/javascript">
        // 编写一个JavaScript对象  ES6
        let user = {
            name : "Jungle",
            age : 3,
            sex : "男"
        };
        // 将js对象转换为Json对象
        let json = JSON.stringify(user);
        console.log(json);
        console.log("==================");
        // 将Json对象转换为JavaScript对象
        let parse = JSON.parse(json);
        console.log(parse);
    </script>

</head>
<body>

</body>
</html>

6.2、Jackson使用

  • Jackson应该是目前比较好的json解析工具了
  • 当然工具不止这一个,比如还有阿里巴巴的 fastjson 等等。
  • 我们这里使用 Jackson,使用它需要导入它的 jar 包;
  1. 导入jar包

    <dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-databind</artifactId>
     <version>2.13.1</version>
    </dependency>
    
  2. 编写web.xml和springmvc-servlet.xml 配置文件

  3. 编写实体类 User

  4. 编写controller

在不用 jackson 的情况下,直接返回一个 toStirng() 也可以将字符串返回到前端

@Controller
public class UserController {

    @RequestMapping("/j1")
    @ResponseBody   // 添加了这个注解,类就不会走视图解析器,会直接返回一个字符串
    public String json1(){
        // 创建一个对象
        User user = new User("Jungle01", "20", "男");
        return user.toString();
    }
}

image.png

使用 jackson

@Controller
public class UserController {
    @RequestMapping("/j1")
    @ResponseBody   // 添加了这个注解,类就不会走视图解析器,会直接返回一个字符串
    public String json1() throws JsonProcessingException {
        // jackson,  ObjectMapper方法
        ObjectMapper mapper = new ObjectMapper();
        // 创建一个对象
        User user = new User("Jungle01", "20", "男");
        String s = mapper.writeValueAsString(user);     // 需要抛出一个异常
        return s;
    }
}

image.png
但还是有乱码的情况
手动解决乱码:@RequestMapping ( value = “/j1”, produces = “application/json;charset=utf-8” )
image.png

  1. 解决乱码

    6.2.1、乱码同一解决

上一种方法比较麻烦,如果项目中有许多请求则每一个都要添加,可以通过Spring配置同一指定,这样就不用每次都去处理了!

我们可以在 SpringMVC 的配置文件上添加一段消息 StringHttpMessageConverter 转换配置!

springmvc-servlet.xml

<!--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>

6.2.2、@RestController

@RestController == @Controller + @ResponseBody
区别:

  • @RestController 不会走视图解析器,直接返回 JSON 字符串(前后端分离)
  • @Controller 会走视图解析器。
    //@Controller
    @RestController     // 不走视图解析器,直接返回JSON字符串
    public class UserController {
      @RequestMapping("j1")
    //    @ResponseBody   // 添加了这个注解,类就不会走视图解析器,会直接返回一个字符串
      public String json1() throws JsonProcessingException {
          // jackson,  ObjectMapper方法
          ObjectMapper mapper = new ObjectMapper();
          // 创建一个对象
          User user = new User("Jungle01", "20", "男");
          String s = mapper.writeValueAsString(user);     // 需要抛出一个异常
          return s;
      }
    }
    
    image.png

如果返回的是多个对象,那么用List封装

// 如果返回的多个对象怎么办?用List
    @RequestMapping("j2")
    public String json2() throws JsonProcessingException {
        List<User> list = new ArrayList();
        // 创建一个对象
        User user1 = new User("Jungle01", "20", "男");
        User user2 = new User("Jungle02", "20", "男");
        User user3 = new User("Jungle03", "20", "男");
        User user4 = new User("Jungle04", "20", "男");
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);

        return new ObjectMapper().writeValueAsString(list);     // 需要抛出一个异常
    }

image.png

输出时间对象:

// 输出时间对象
    @RequestMapping("j3")
    public String json3() throws JsonProcessingException {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return new ObjectMapper().writeValueAsString(sdf.format(date));     // 需要抛出一个异常
    }

image.png

通过 jackson ,屏蔽java代码原本默认使用时间戳输出的方式

// 输出时间对象
    @RequestMapping("j3")
    public String json3() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        // 不使用时间戳的方式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // 自定义日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        mapper.setDateFormat(sdf);
        Date date = new Date();
        // 这个时候输出的就不是时间戳的格式了
        return mapper.writeValueAsString(date);      // 需要抛出一个异常
    }

6.2.3、提取工具类

JsonUtils 工具类

public class JsonUtils {

    public static String getJson(Object object){
        return getJson(object, "yyyy-MM-dd HH:mm:ss");
    }

    public static String getJson(Object object, String dateFormat){
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        mapper.setDateFormat(sdf);
        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
// 提取了工具类之后,使用更加方法
    @RequestMapping("j4")
    public String json4() {
        Date date = new Date();
        // 自定义时间格式
        return JsonUtils.getJson(date, "yyyy-MM-dd HH:mm:ss");
    }

提取公共类和方法的复用,这是一种编码思想,大量出现在源码中。值得学习!!

6.3、fastJson

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

fastjson 的 pom.xml 依赖

<!--fastjson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.79</version>
</dependency>

image.png

// 使用fastjson
    @RequestMapping("j5")
    public String json5() {
        List<User> list = new ArrayList();
        // 创建一个对象
        User user1 = new User("Jungle01", "20", "男");
        User user2 = new User("Jungle02", "20", "男");
        User user3 = new User("Jungle03", "20", "男");
        User user4 = new User("Jungle04", "20", "男");
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);

        String jsonString = JSON.toJSONString(list);
        return jsonString;
    }

7、AJAX

  • AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
  • AJAX 是一种无需在重新加载整个网页的情况下,能够更新部分网页的技术
  • AJAX 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 web 应用程序的技术

image.png

7.1、关于 / 和 /*

URL匹配注意事项

Servlet对URL的匹配既不是Ant风格,也不是Regex。特殊符号只有单个的,且使用位置有强约束,切忌*想当然的随意拼凑。
举例两种典型的错误理解,应该能帮助到你:

  • /api/.jsp:该urlPatterns是非法的,启动时会报错“IllegalArgumentException: servlet映射中的[/api/.jsp]无效”。原因为:
    • 若当路径匹配,/*后面不能再有任何东西
    • 若当后缀名匹配,*.必须是最前面
  • /api//demo:这个urlPatterns是合法的。只不过它属于精确匹配,也就是说别看它中间有,仍旧有且仅能匹配/api/*/demo这个请求路径

    匹配顺序

    有时候一个URL会被多个urlPatterns所匹配,这时谁优先呢?
    Servlet同样遵循“国际惯例”:越精确越优先,越模糊越靠后。站在pattern模式的角度换句话讲就是:范围越小越优先,范围越大越靠后。
    因此Servlet四种匹配方式顺序按范围从小到大(优先级从高到底)排序为:精确匹配 > 路径匹配 > 后缀名匹配 > 缺省匹配

    /和/*的区别

    终于,来到了今天的主菜。
    从上至下的阅读到这里,再看这个问题,是不是觉得答案已经浮出水面?那么,最后我还是来总结一下它俩的异同点:
    相同点
    绝大部分场景下具有相同的表现:匹配所有
    不同点
    就是由于它们的相同点(如此相似),所以才让我们难以区分。
    关于/:

  • servlet中特殊的匹配模式(用在Filter中无效),

  • 因为是缺省匹配代表匹配所有路径,所以只可能存在一个实例(若存在多个就覆盖)
  • 优先级最低(兜底),这是和/*的最大区别。它不会覆盖任何其它的url-pattern,只会覆盖Servlet容器(如Tomcat)内建的DefaultServlet

关于/*:

  • 属于4中匹配模式中的路径匹配,可用于Servlet和Filter
  • 优先级很高(仅次于精确匹配)。所以它会覆盖所有的后缀名匹配,从而很容易引起404问题,所以这种模式的“伤害性”是非常强的,一般有且仅用在Filter上

    DispatcherServlet不拦截.jsp请求根因分析

    /只能用于Servlet上,/*一般只用于Filter上。
    大家熟悉的Spring MVC的DispatcherServlet的匹配路径默认就是/,它会拦截各种各样的请求,诸如下面这种请求都会拦截:

  • /api/demo1

  • /html/demo1.html
  • /static/main.js

但是,它不会拦截/api/demo1.jsp这种以.jsp结尾的请求。据此现象就出现了:/不拦.jsp请求而/拦截(/的范围比/大)这种“错误”言论。
下面告诉你此现象的根因:Servlet容器(如Tomcat)内置有专门匹配.jsp这种请求的Servlet处理器,如下图所示:

image.png

后缀名匹配优先级高于缺省匹配,所以.jsp结尾的请求不会被DispatcherServlet所“截胡”而是交给了JspServlet处理。

7.2、AJAX理解

image.png
AJAX是实现前后端分离里面最重要的一门技术(请求类型xhr)。作为一个java后端程序员,需要掌握的有:

Jquery.ajax(...)
    部分参数:
        url: 请求地址
        data: 要发送的数据
        success: 成功之后执行的回调函数(全局)
        error: 失败之后执行的回调函数(全局)

AJAX理解:
举个例子:实现一个失去焦点后浏览器响应一个请求。
在前端,我们通过js实现一个失去焦点的触发事件,然后这个事件通过 ajax.post()方法,一般我们填上三个参数,url、data、success,将url请求给到后台,以往的 a 标签都是通过url请求给到后台,然后后台去做一些转发或者重定向操作,但是这样会使得整个网页刷新,这不是我们想要的效果。这个时候就需要用到ajax技术,我们将url给到后台,后台只返回我们需要的数据,而没有去刷新整个页面。这个将后台的控制权给到了前端的过程就叫做AJAX技术。

  • 【狂神说Java SpringMVC 24、Ajax初体验】
  • 【狂神说Java SpringMVC 25、Ajax异步加载数据】
  • 【狂神说Java SpringMVC 26、Ajax验证用户名体验】

8、拦截器

概述
SpringMVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
过滤器与拦截器的区别:拦截器是 AOP 思想的具体应用

过滤器

  • servlet 规范中的一部分,任何 java web 工程都可以使用
  • 在 url-pattern 中配置了 /* 之后,可以对所有要访问的资源进行拦截

拦截器

  • 拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法,如果访问的是 jsp/html/css/image/js 是不会进行拦截的

自定义拦截器
如何实现拦截器呢?
想要自定义拦截器,必须实现 HandlerInterceptor 接口

全在代码里:D:\IntelliJ IDEA workSpace\Maven\javaEE\SpringMVC\springmvc-07-interceptor

9、文件上传和下载

准备工作
文件上传是项目开发中最常见的功能之一,springMVC 可以很好的支持文件上传,但是 SpringMVC 上下文中默认没有装配 MultipartResolver,因此默认情况下不能处理文件上传工作。如果想使用 Spring 的文件上传功能,则需要在上下文中装配 MultipartResolver。
前端表单要求:为了能上传文件,必须将表单的 method 设置为 POST,并将 enctype 设置为 multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;

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

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
  1. 导包
    <!--文件上传-->
         <dependency>
             <groupId>commons-fileupload</groupId>
             <artifactId>commons-fileupload</artifactId>
             <version>1.3.3</version>
         </dependency>
         <!--servlet-api导入高本本的-->
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <version>4.0.1</version>
         </dependency>
    
    代码全在:D:\IntelliJ IDEA workSpace\Maven\javaEE\SpringMVC\springmvc-08-file 中