Spring MVC

Model(业务模型) View(用户视图) Controller(控制器)

官网:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-servlet

以前是每一个servlet都要配置对应的请求,而现在使用一个中转站即前端控制器DispatcherServlet,它可以调用对应的servlet

一些基础配置:

  • 父项目的基本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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tang</groupId>
    <artifactId>SpringMVC</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.6</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>

    </project>
  • 子项目的web项目创建,直接右击子项目,选择Add Framework Support,然后勾选web

  • 然后去项目结构–Artifacts中,在右侧Available Elements中右击对应文件夹,选择put into Output Root即可自动生成lib并导入包

    参考:https://blog.csdn.net/qq_35437792/article/details/112796283

  • 配置tomcat,可以在文件管理中将out文件更换目录到target中,方便maven清理


Spring MVC请求流程

2.png

  1. request请求:。 用户发起请求,请求会被前端控制器DispatherServlet处理
  2. 查找请求Handler:DispatherServlet请求处理器映射器HandlerMapping来查找Handler
  3. 返回一个执行链:HandlerMapping根据配置找到相应的Handler(可能包含若干个拦截器Interceptor),然后返回DispatcherServlet
  4. 请求适配器执行Handler:DispatcherServlet请求处理器适配器HandlerAdapter执行相应的Handler(也称为Controller)
  5. 执行Handler:HandlerAdapter执行Handler
  6. 返回一个ModelAndView:Handler执行后将一个ModelAndView对象(SpringMVC底层对象,包括数据模型和视图信息)返回给HandlerAdapter
  7. 返回ModelAndView:适配器返回ModelAndViewHandlerAdapter接收ModelAndView对象后返回给DispatcherServlet
  8. 请求视图解析:DispatcherServlet接收到对象后,请求视图解析器View Resolver对视图进行解析
  9. 返回View:View Resolver根据View信息信息匹配相应的视图结果,然后反馈给DispatcherServlet
  10. 视图渲染,数据模型填充request域:DispatcherServlet收到View具体视图后,进行视图渲染,将Model中的模型数据填充到View视图中的request域,生成最终的视图
  11. reponse响应:DispatcherServlet向用户返回请求的结果

HelloSpringMVC(web.xml)

在web.xml中配置前端控制器DispatcherServlet

  • 过滤配置:url-pattern
    • / :匹配所有请求,不包括jsp
    • /* :匹配所有请求,包括jsp
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
39
40
41
<?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>
<!-- DispatcherServlet绑定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>

<!-- 过滤配置:url-pattern
/ :匹配所有请求,不包括jsp
/* :匹配所有请求,包括jsp-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- springmvc提供的乱码过滤器 -->
<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>

springmvc-servlet.xml配置映射器、适配器、视图解析器

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

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

<bean id="/hello" class="com.tang.controller.HelloController"/>
</beans>

Controller,写业务,返回ModelAndView

return配合视图解析器拼接,返回对应资源(jsp)路径

1
2
3
4
5
6
7
8
9
10
11
12
13
//Controller执行Handler返回ModelAndView
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
//业务代码
String result = "HelloSpringMVC";
mv.addObject("msg",result);

//视图跳转
mv.setViewName("hello");
return mv;
}
}

jsp调用

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

${msg}

</body>
</html>

注解开发SpringMVC

编写web.xml注册DispatcherServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>

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

编写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
<?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.tang.controller"/>

<!-- 让SpringMVC不处理静态资源 css、js等-->
<mvc:default-servlet-handler/>

<!-- 为了使用@RequestMapping注解,需要映射器+适配器,这里用此句导入 -->
<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>

自行创建控制类,Controller

1
2
3
4
5
6
7
8
9
10
@Controller
public class HelloController {
@RequestMapping("/h1")
public String hello(Model model){
//封装
model.addAttribute("msg","HelloSpringMVCAnnotation");
//被视图解析器处理,会拼接相应的前后缀,这里只用打jsp的文件名即可
return "hello";
}
}

然后编写视图层(jsp等)

Controller配置

  • 实现Controller接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //实现Controller即控制器
    public class ControllerDemo1 implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    ModelAndView mv = new ModelAndView();
    mv.addObject("msg","测试COntroller");
    mv.setViewName("test");
    return mv;
    }
    }

    在springmvc配置文件中声明

    1
    <bean name="/test" class="com.tang.controller.ControllerDemo1"/>

    实现接口的配置,只能一个控制器对应一个方法,多个方法配置会很麻烦

  • 注解配置Controller接口

    @Controller

    1
    2
    3
    4
    5
    6
    7
    8
      @Controller
    public class ControllerDemo2 {
    @RequestMapping("/test2")
    public String test(Model model){
    model.addAttribute("msg","测试Controller2");
    return "test";
    }
    }

    springmvc配置文件中添加启用注解的声明

    1
    <context:component-scan base-package="com.tang.controller"/>

RequestMapping

1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/demo")
public class ControllerDemo3 {
@RequestMapping("/test")
public String test(Model model){
model.addAttribute("msg","测试");
return "test";
}
}

@RequestMapping可在类与方法上添加注解

访问对应方法时路径位/demo/test


RESTful

表现层状态转换,RESTful是一种设计风格,而不是标准。RESTful的风格的设计允许参数通过URL拼接传到服务器,让URL更简洁。

  • 原本传参方式:

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

    网页输入

    1
    http://localhost:8080/springmvc_03/add?a=5&b=8
  • RESTful风格(拼接字符串)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Controller
    public class RestFulController {
    @RequestMapping(value = "/add/{a}/{b}",name = "加法,这就是一个注释",method = RequestMethod.GET)
    public String test1(@PathVariable int a, @PathVariable int b, Model model){
    int res = a+b;
    model.addAttribute("msg","计算结果是"+res);
    return "test";
    }
    }

    注解自定义提交的方法

    1
    2
    3
    4
    5
    6
    //可以使用RequestMapping,调用不同的提交类型
    @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
    //也可以使用固定的提交类型的注解
    @GetMapping("/add/{a}/{b}")
    @PostMapping("/add/{a}/{b}")
    @DeleteMapping("/add/{a}/{b}")

    网页输入

    1
    http://localhost:8080/springmvc_03/add/4/5

@RequestMapping中各属性解释:https://blog.csdn.net/lzb348110175/article/details/88552507


重定向、转发

https://www.cnblogs.com/fengbingshui/p/13506049.html

转发是服务器级别,重定向是客户端级别。WEB-INF下资源只有服务器级别才可访问。

  • 没有启用视图解析器,要使用文件全路径

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Controller
    public class ModelTest1 {
    @RequestMapping("/model")
    public String test(Model model){
    model.addAttribute("msg","ttt");
    //默认路径是转发形式
    // return "/WEB-INF/jsp/test.jsp";
    //手动选择forward转发形式
    // return "forward:/WEB-INF/jsp/test.jsp";
    //手动选择redirect重定向形式,注意不能访问WEB-INF下文件
    return "redirect:/hello.jsp";
    }
    }
  • 启用视图解析器,可拼接文件路径

    1
    2
    return "test";//转发
    return "redirect:/hello.jsp";//使用重定向形式和没有视图解析器时一样

springmvc的xml文件中配置视图解析器

1
2
3
4
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>

数据处理

前端接收参数

  • 单独参数,默认网页拼接名和变量名相等,也可以使用@RequestParam()

    比方这里定义为name,拼接时使用?name=···,使用注解@RequestParam(“username”)后,则拼接使用?username=···

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @RequestMapping("/t1")
    public String test1(@RequestParam("username") String name, Model model){
    //1、接收前端参数
    System.out.println("接收到前端参数为:"+name);
    //2、将结果返回给前端
    model.addAttribute("msg",name);
    //3、视图跳转
    return "test";
    }
  • 接收一个对象,拼接是要和对象属性变量名相同

    拼接时使用?id=···&name=··&age=···

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @RequestMapping("/t2")
    public String test2(User user, Model model){
    //1、接收前端参数
    System.out.println("接收到前端参数为:"+user);
    //2、将结果返回给前端
    model.addAttribute("msg",user);
    //3、视图跳转
    return "test";
    }
    1
    2
    3
    4
    5
    6
    7
    8
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    private int id;
    private String name;
    private int age;
    }

数据显示到前端

注意使用@Requestparam注解后,直接访问不填值,不传空值而是报错

  • ModelAndView(继承Controller),然后去配置bean,前面有写

    1
    2
    3
    4
    5
    6
    7
    8
    public class ControllerDemo1 implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    ModelAndView mv = new ModelAndView();
    mv.addObject("msg","测试Controller");
    mv.setViewName("test");
    return mv;
    }
    }
  • Model()

    1
    2
    3
    4
    5
    6
    7
    8
    @Controller
    public class ControllerDemo2 {
    @RequestMapping("/test")
    public String test(Model model){
    model.addAttribute("msg","测试Controller2");
    return "test2";
    }
    }
  • ModelMap

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @RequestMapping("/t3")
    public String test3(String name,ModelMap map){
    //1、接收前端参数
    System.out.println("接收到前端参数为:" + name);
    //2、将结果返回给前端
    map.addAttribute("msg",name);
    //3、视图跳转
    return "test";
    }

对比

1
2
3
model: 简化的Model对象,只有常用的几个方法
Modelmap: 继承LinkedHashMap,除了自身方法还实现了LinkedHashMap的特性和方法
ModelAndView: 实现Controller接口,然后配置bean实现跳转

乱码解决

from.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>

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

</body>
</html>

表单处理类

1
2
3
4
5
6
7
8
@Controller
public class FormController {
@PostMapping("/form")
public String test(String name, Model model){
model.addAttribute("msg",name);
return "test";
}
}

然后发现提交后乱码

解决:在web.xml中配置官方过滤器,注意要使用 / 包含jsp*

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- springmvc提供的乱码过滤器 -->
<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就是一种轻量级的数据转换格式,json相对于是js对象的字符串表达式,本质是字符串。

json和js互转:

  • JSON.strinigify() json转js
  • JSON.parse() js转json
1
2
3
4
5
6
7
8
9
10
11
12
13
<script type="text/javascript">
var user = {
name : "TC",
age : 21,
sex : "男"
};
//js转json
var json = JSON.stringify(user);
console.log(json);
//json转js
var js = JSON.parse(json);
console.log(js);
</script>

JACKSON

maven依赖,注意后加入依赖要去更新lib目录!

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

使用jackson的ObjectMapper创建一个对象的字符串

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
//@RestController
public class UserController {
@RequestMapping("/j1")
@ResponseBody //不走视图解析器?直接返回字符串
public String JSON1() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user = new User("TC", 21, "男");
String s = mapper.writeValueAsString(user);
return s;
}
}

然后网页不会走视图解析器,直接将json字符串传到网页上,但要添加注解

1
2
1、@Controller + @ResponseBody 
2、@RestController

乱码配置

在springmvc配置xml中添加固定内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<context:component-scan base-package="com.tang.controller"/>
<mvc:default-servlet-handler/>
<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>

使用工具类简化操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class JsonUtils {
public static String getJson(Object object, String dateFormat){
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
SimpleDateFormat s = new SimpleDateFormat(dateFormat);
mapper.setDateFormat(s);
try {
return mapper.writeValueAsString(object);
}catch (JsonProcessingException e){
e.printStackTrace();
}
return null;
}
}
1
2
3
4
5
@RequestMapping("j3")
public String json3() {
Date date = new Date();
return JsonUtils.getJson(date,"yyyy-MM-dd HH-mm-ss");
}

使用一个工具类,我们再次传json是不用在设置mapper

Fastjson

maven依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>

调用方法就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RequestMapping("/j4")
public String json4() {
List<User> userList = new ArrayList<User>();
User user1 = new User("老1", 188, "男");
User user2 = new User("老2", 188, "男");
User user3 = new User("老3", 188, "男");
User user4 = new User("老4", 188, "男");

userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);

String s = JSON.toJSONString(userList);
return s;
}

小结

出门右转SSM简单整合