MVC 概念

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

**Model(模型):**数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。

**View(视图):**界面设计人员进行图形界面设计。

**Controller(控制器):**负责转发请求,对请求进行处理。


MVC 程序(配置版)

目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|-- pom.xml
|-- src
| |-- main
| | |-- java
| | | `-- com
| | | `-- kanxz
| | | `-- controller
| | | `-- HelloController.java
| | `-- resources
| | `-- springmvc-servlet.xml
| `-- test
| `-- java
`-- web
|-- WEB-INF
| |-- jsp
| | `-- hello.jsp
| `-- web.xml
`-- index.jsp

maven添加依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.4.2</version>
<scope>test</scope>
</dependency>
</dependencies>

首先配置web.xml,注册DispatcherServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
<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>

<!--/ 匹配所有的请求;(不包括.jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

编写SpringMVC 的 配置文件:springmvc-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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 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"/>

<!--视图解析器:DispatcherServlet给他的ModelAndView
1. 获取了ModelAndView的数据
2. 解析ModelAndView的视图名字
3. 拼接视图名字,找到对应视图 /WEB-INF/jsp/hello.jsp
4. 将数据渲染到这个视图上-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>

</beans>

编写我们要操作业务Controller ,要么实现Controller接口,要么增加注解;需要返回一个ModelAndView,装数据,封视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HelloController implements Controller {

@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();

//封装对象,放在ModelAndView中。Model
mv.addObject("msg", "Hello SpringMVC!");

//封装要跳转的视图,放在ModelAndView中
mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
return mv;
}
}

将自己的类交给SpringIOC容器,注册bean

1
2
<!--Handler-->
<bean id="/hello" class="com.kanxz.controller.HelloController"/>

写要跳转的jsp页面,显示ModelandView存放的数据,以及我们的正常页面

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
${msg}
</body>
</html>

配置Tomcat 启动测试

(404解决:https://www.codenong.com/cs111051809/)


MVC 原理

Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。

Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)

SpringMVC的原理如下图所示:

​ 当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

SpringMVC的工作原理

1、 用户发送请求至前端控制器DispatcherServlet。

2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、 DispatcherServlet调用HandlerAdapter处理器适配器。

5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

6、 Controller执行完成返回ModelAndView。

7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9、 ViewReslover解析后返回具体View。

10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11、 DispatcherServlet响应用户。


MVC 程序(注解版)

配置web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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>
<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>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

在resource目录下添加springmvc-servlet.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?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
http://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.kanxz.helloannotaition"/>

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


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

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

</beans>

编写一个Java控制类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.kanxz.controller;

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

@Controller
@RequestMapping("/HelloController")
public class HelloController {

// 真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String sayHello(Model model) {

//向模型中添加属性msg与值,可以在JSP页面中取出并渲染
model.addAttribute("msg", "hello, SpringMVC");

//web-inf/jsp/hello.jsp
return "hello";
}
}
  • @Controller是为了让Spring IOC容器初始化时自动扫描到;
  • @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;
  • 方法中声明Model类型的参数是为了把Action中的数据带到视图中;
  • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。

在WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>

配置Tomcat 启动测试


RestFul 风格

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

代码演示:

代码框架同上

首先不使用RestFul风格:

1
2
3
4
5
6
7
8
9
10
@Controller
public class RestFulController {

@RequestMapping("/add")
public String add(int a, int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果:" + res);
return "test";
}
}

运行上述代码,需要在地址栏输入:http://localhost:8080/SpringMVC/add?a=1&b=2,得到:结果:3

然后使用RestFul风格:

1
2
3
4
5
6
7
8
9
@Controller
public class RestFulController {
@RequestMapping("/add/{a}/{b}")
public String add(@PathVariable int a, @PathVariable int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果:" + res);
return "test";
}
}

那么在地址栏输入:http://localhost:8080/SpringMVC/add/1/2,得到:结果:3

使用method属性可以指定请求类型:

1
@RequestMapping(value = "/add/{a}/{b}", method = RequestMethod.GET)

指定请求谓词的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等

也可以简写为:

1
@GetMapping("/add/{a}/{b}")

相应也有:

1
2
3
4
5
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
...

处理提交数据

定义参数

1
2
3
4
5
@GetMapping("/user")
public String getData(@RequestParam("name") String name) {
System.out.println(name);
return "test";
}

当地址栏输入为http://localhost:8080/SpringMVC/user?name=zhangsan时,会输出zhangsan

单个参数的时候可以省略@RequestParam

获取多个参数

1
2
3
4
5
6
@GetMapping("/user")
public String getData(@RequestParam("name") String name, @RequestParam("age") int age) {
System.out.println(name);
System.out.println(age);
return "test";
}

非必须传递参数

1
2
3
4
5
@GetMapping("/user")
public String getData(@RequestParam("name") String name, @RequestParam(value = "age", required = false) int age) {
System.out.println(name);
return "test";
}

对象参数

1
2
3
4
5
@GetMapping("/user")
public String getData(User user) {
System.out.println(user);
return "test";
}

地址栏输入User的属性值:http://localhost:8080/SpringMVC/user?name=lisi&age=11&id=1


表单乱码问题处理

首先定义form.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Form</title>
</head>
<body>
<form action="encode" method="post">
<input type="text" name="name">
<input type="submit">
</form>
</body>
</html>

在定义控制类:

1
2
3
4
5
6
7
8
9
@Controller
public class EncodingController {

@PostMapping("/encode")
public String encodeTest(String name, Model model) {
model.addAttribute("msg", name);
return "test";
}
}

输入中文得到乱码

而SpringMVC给我们提供了一个过滤器 , 可以在web.xml中配置

1
2
3
4
5
6
7
8
9
10
11
12
<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>

Json交互处理

JSON 和 JavaScript 对象互转

要实现从JSON字符串转换为JavaScript 对象,使用JSON.parse()方法:

1
2
var obj = JSON.parse('{"a": "Hello", "b": "World"}');
//结果是 {a: 'Hello', b: 'World'}

要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify()方法:

1
2
var json = JSON.stringify({a: 'Hello', b: 'World'});
//结果是 '{"a": "Hello", "b": "World"}'

Controller返回JSON数据

使用Jackson

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.0</version>
</dependency>

定义User实体类

定义Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Controller
public class UserController {

@RequestMapping("/j1")
@ResponseBody
public String UserJson() throws JsonProcessingException {

// 创建一个jackson的对象映射器,用来解析数据
ObjectMapper mapper = new ObjectMapper();

User user = new User("张三", "男", 18);

// 由于@ResponseBody注解,这里会将str转成json格式返回
return mapper.writeValueAsString(user);
}

}

使用@ResponseBody会将str转成json字符串输出到网页上

也可以在类上加@RestController,这样子,里面所有的方法都只会返回 json 字符串

这样启动Tomcat 会得到乱码

Json乱码处理

  1. 可以通过@RequestMapingproduces属性来实现,修改下代码
1
2
// produces:指定响应体返回类型和编码
@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")

再次测试:http://localhost:8080/SpringMVC/j1,得到:{"name":"张三","sex":"男","age":18}

  1. 在springmvc的配置文件上:springmvc-servlet.xml添加一段消息StringHttpMessageConverter转换配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<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>

学习自: