SpringMVC
SpringMVC简介
一、三层架构和MVC
1、三层架构概述
(1)开发架构:一是 C/S 架构 (客户端/服务器),二是B/S架构(浏览器/服务器)。在JavaEE开发中,几乎全部是基于 B/S架构的开发。在B/S 架构中,系统标准的三层架构。
(2)三层架构
- 表现层(web):负责接收客户端请求,向客户端响应结果。通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。
- 业务层(Service, Business,biz):负责业务逻辑处理,和开发项目的需求息息相关。
- 持久层(dao mapper):负责数据持久化,和数据库做交互。
2、Model1
这种模式十分简单。页面显示、控制分发。业务逻辑、数据访问全部通过jsp实现。
3、Model2
这种模式通过两部分去实现,
- 页面显示:JSP
- 控制分发、业务逻辑、数据访问:Servlet
4、MVC模式介绍
MVC分为三大块,
- 视图(View):负责页面显示
- 控制器(Control):Servlet负责的控制分发
- 模型(MOdel):Service和Dao两个部分,分别负责业务逻辑和数据访问。
5、三种模型对比
模型 | 优点 | 缺点 |
---|---|---|
Model1 | 架构简单,比较适合小型项目开发 | JSP 职责不单一,职责过重,不便于维护 |
Model2 | 职责清晰,较适合于大型项目架构 | 不适合小型项目开发 |
MVC | 分工明确,各司其职,互不干涉。适用于大型项目架构,有利于组件的重构 | 增加了系统开发的复杂度 |
二、SpringMVC概述
1、SpringMVC是什么?
SpringMVC 属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web应用程序的全功能 MVC模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架。
- 小结:SpringMVC 是应用Web层的基于MVC设计模式的轻量级的Web框架。对Servlet封装,支持restrestful风格。
2、SpringMVC的位置
SpringMVC 位于三层架构中的表现层。作用:接收请求、响应数据,响应的数据通过驶入、模板展示给用户。
3、SPringMVC的优势
- 对Servlet进行封装,简化 Web层开发
- 天生集成Spring框架
- 支持灵活的URL到页面控制器的映射。
- 支持静态模板技术: jsp FreeMark Velocity
- 非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API;
- 更加简单的异常处理;
- 支持Restful风格。
SpringMVC入门
1、入门案例需求分析
构建页面index.jsp发起请求,在服务器端处理请求,控制台打印处理请求成功,跳转main.jsp成功页面;
2、构建maven项目并添加依赖
pom.xml文件
<?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.etime</groupId>
<artifactId>day04011</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<spring.version>5.2.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
3、web.xml中配置核心控制器DispatcherServlet
<?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 dispatcher:调度程序-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载dispatcherServlet初始化资源-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern><!-- 根路径下的,所有请求都经过dispatcher-->
</servlet-mapping>
</web-app>
4、配置SpringMVC的配置文件application.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--上下文组件扫描-->
<context:component-scan base-package="com.etime"></context:component-scan>
<!--处理器映射器:根据请求路径匹配映射器路径找到对应的执行器(Controller)-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<!--处理器适配器:根据处理器映射器返回的执行器对象,去执行器对象(Controller)-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
<!--视图解析器:解析视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
5、创建index.jsp并发送请求,创建main.jsp
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="firstController/test01">测试</a>
</body>
</html>
main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>main</title>
</head>
<body>
<h1>这里是main页面</h1>
</body>
</html>
6、编写控制器并使用注解配置
FirstController.java
package com.etime.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/firstController")
public class FirstController {
@RequestMapping("test01")
public String test01(){
System.out.println("测试 第一个Controller");
return "main";
}
}
7、启动服务器测试
第一次测试出现错误
解决办法
结果
SpringMVC执行过程和原理
1、案例的执行过程
浏览器客户端发起请求,请求到达服务器tomcat,tomcat将请求相关信息参数封装到对象request和response中,再将request和response对象交给service方法处理,在service方法中会根据请求路径将请求交给对应的controller执行器处理。
2、SpringMVC的请求响应流程
浏览器发送请求,被DispatcherServlet捕获,DispatcherServlet没有直接处理请求,而是将请求交给HandlerMapping处理器映射器,处理器映射器根据请求路径去controller控制层中匹配对应的执行器,并将匹配结果返回给DispatcherServlet,由DispacherServlet调用HandlerAdapter处理器适配器来执行控制层执行器方法;
执行器方法执行后的返回结果,由DispatcherServlet交给视图解析器ViewResolver来处理,找到对应的结果视图,渲染视图,并将结果响应给浏览器。
SpringMVC常用组件
1、DispatcherServlet:前端控制器
用户请求到达前端控制器(mvc 模式中的 controller),dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
- 小结: DispatcherServlet本质上就是Servlet 配置信息在web.xml当中
2、HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3、Handler:后端处理器【自定义的,类似于servlet】
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。
4、HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
5、View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6、View:视图
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jsp、freemarkerView、pdfView,FreeMark ,thymeleaf 等。我们最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
总结:
- SpringMVC是一个基于组件开发的框架
- 一个中心三个基本点: SpringMVC提供的, 不需要用户开发。
- 一个中心: 前端控制器: DispatcherServlet
- 三个基本点:
- 处理器映射器
- 处理器适配器
- 视图解析器
7、mvc:annotation-driven标签说明
- driven:驱动
- SpringMVC中的三大组件:处理器映射器、处理器适配器、视图解析器
- mvc:annotation-driven的使用:自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter ( 处 理 适 配 器 )
- 使用场景:application.xml配置文件中替代映射器和适配器的配置。
- 注意:需要填写处理具体业务的控制器以及视图
application.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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<!-- 上下文组件扫描-->
<context:component-scan base-package="com.etime"></context:component-scan>
<!--视图解析器:解析视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--将映射器和适配器签替换成<mvc:annotation-driven/>-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
SpringMVC的RequestMapping注解
1、RequestMapping注解用途
用于定义映射路径,建立url 和 控制层方法之间的对应关系
2、RequestMapping 注解源码解读
@Target({ElementType.METHOD, ElementType.TYPE}) // 注解在方法和类上有效
@Retention(RetentionPolicy.RUNTIME)// runtime :当前的注解在运行阶段生效
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
}
3、RequestMapping 注解的描述
- @Target-约束注解的位置:
- ElementType.TYPE–方法和接口
- ElementType.METHOD–成员方法
- @RequestMapping的属性
- value:指定映射url。它和path属性的作用是一样的。
- method:指定请求的方式。method = {RequestMethod.POST },只能处理post请求
- params:用于指定限制请求参数的条件。它支持简单的表达式,要求请求参数的 key 和 value 必须和配置的一模一样。例:params = {“username”},表示请求参数必须有 username。如果没有携带username的参数:前端浏览器400 bad request
- headers:用于指定限制请求消息头的条件。
- 案例: headers = “Request URL” 在请求当中方法的时候, 必须携带一个指定的请求头信息。
注意:多个属性之间是与的关系;
SpringMVC的请求参数绑定
一 绑定说明
1、绑定的体制
表单中请求参数都是基于 key=value的。SpringMVC绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
请求方式:
get: localhost:8080/xxx?username==lisi&age=21
post: username==lisi&age=21 封装到请求体:
servlet:
String name = request.getParameter(“username”);
String age = request.getParameter(“age”);
String [] xxx = request.getParameterValues(“hobby”);
Map<String.String[]> map = request.getParameterMap();
参数绑定:
就是将获得参数绑定在方法的形式参数上, 取代了request.getParameter(“xxx”)
2、支持的数据类型
- 简单类型参数:包括基本类型和 String 类型
- POJO 类型参数:包括实体类 复杂POJO
- 数组和集合类型参数:包括 List 结构和 Map 结构的集合(包括数组)
3、使用要求
- 若是基本数据类型或者String类型,要求我们的参数名必须和控制器中方法的形参名称保持一致。(严格区分大小写)
- 若是 POJO类型 或 它的关联对象,要求表单中参数名称 和 POJO属性名称 保持一致,且控制器方法的参数类型是 POJO类型。
- 若是集合,有两种方式
- 一,要求集合属性名称的请求参数必须在 POJO中。在表单中请求参数名称要和 POJO 中集合属性名称相同。给 List 集合中的元素赋值,使用下标。若是 Map ,集合中的元素赋值,使用键值对。
- 二,接收的请求参数是 json 格式数据。需要借助一个注解实现,@RequestBody注解。
二 参数绑定示例
1、基本类型和String类型作为参数
(1)页面定义请求test02.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>test02</title>
</head>
<body>
<form action="firstController/test02" method="get" enctype="application/x-www-form-urlencoded">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
年龄:<input type="number" name="age"><br>
<input type="submit">
</form>
</body>
</html>
(2)执行器方法绑定参数
@Controller
@RequestMapping("/firstController")
public class FirstController {
@RequestMapping("test01")
public String test01(){
System.out.println("测试 第一个Controller");
return "main";
}
@RequestMapping(value = "test02",method = {RequestMethod.GET})
public String test02(String username,String password,int age){
System.out.println("username = " + username);
System.out.println("password = " + password);
System.out.println("age = " + age);
return "main";
}
}
2、POJO 类型参数
简单的POJO
(1)User实体
public class User {
private String username;
private String password;
private int age;
public User() {
}
public User(String username, String password, int age) {
this.username = username;
this.password = password;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
(2)页面定义请求 test02.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>test02</title>
</head>
<body>
<form action="firstController/test03" method="post" enctype="application/x-www-form-urlencoded">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
年龄:<input type="number" name="age"><br>
<input type="submit">
</form>
</body>
</html>
(3) 执行器方法绑定参数
@RequestMapping(value = "test03",method = {RequestMethod.POST})
public String test03(User user){
System.out.println("user = " + user);
return "main";
}
复杂的POJO
(1)User实体
public class User {
private String username;
private String password;
private int age;
private Car car;
private List<Car> carList;
public User() {
}
public User(String username, String password, int age) {
this.username = username;
this.password = password;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public List<Car> getCarList() {
return carList;
}
public void setCarList(List<Car> carList) {
this.carList = carList;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", car=" + car +
", carList=" + carList +
'}';
}
}
(2)Car实体
public class Car {
private String id;
private String name;
private double price;
public Car() {
}
public Car(String id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
(3)页面定义请求 test02.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>test02</title>
</head>
<body>
<form action="firstController/test04" method="post" enctype="application/x-www-form-urlencoded">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
年龄:<input type="number" name="age"><br>
<h1>第一种</h1>
车编号:<input type="text" name="car.id">
车名称:<input type="text" name="car.name">
车价格:<input type="number" name="car.price">
<h1>第二种</h1>
车编号:<input type="text" name="carList[0].id">
车名称:<input type="text" name="carList[0].name">
车价格:<input type="number" name="carList[0].price"><br>
车编号:<input type="text" name="carList[1].id">
车名称:<input type="text" name="carList[1].name">
车价格:<input type="number" name="carList[1].price"><br>
车编号:<input type="text" name="carList[2].id">
车名称:<input type="text" name="carList[2].name">
车价格:<input type="number" name="carList[2].price">
<input type="submit">
</form>
</body>
</html>
(4) 执行器方法绑定参数
@RequestMapping(value = "test04",method = {RequestMethod.POST})
public String test04(User user){
System.out.println("user = " + user);
return "main";
}
3、数组类型参数
(1)页面定义请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>test03</title>
</head>
<body>
<form action="firstController/test05" method="post" enctype="application/x-www-form-urlencoded">
爱好:
<input type="checkbox" name="hobby" value="篮球">篮球
<input type="checkbox" name="hobby" value="足球">足球
<input type="checkbox" name="hobby" value="乒乓球">乒乓球
<input type="submit">
</form>
</body>
</html>
(2) 执行器方法绑定参数
@RequestMapping(value = "test05",method = {RequestMethod.POST})
public String test05(String[] hobby){
System.out.println("hobby = " + Arrays.toString(hobby));
return "main";
}
4、使用ServletAPI对象作为方法参数
(1)引入ServletAPI的依赖jar包,注意:jar包作用范围provided(规定),不参与部署
<?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.etime</groupId>
<artifactId>day04011</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<spring.version>5.2.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
(2)页面定义请求 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="firstController/test06">测试</a>
</body>
</html>
(3) 执行器方法绑定参数
@RequestMapping(value = "test06",method = {RequestMethod.GET})
public void test06(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("请求转发 进行页面跳转");
request.getRequestDispatcher("../main.jsp").forward(request,response);
}
5、请求参数乱码问题
- POST 请求的编码问题,要在web.xml文件中配置编码过滤器:
<filter>
<filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
三、参数绑定默认类型
- 参数绑定默认类型:Map<String,Object>、ModelAndView、Model、ModelMap类型,能够给前台传递。
方式一
(1) 页面定义请求
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="firstController/test07">测试</a>
</body>
</html>
main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>main</title>
</head>
<body>
<h1>这里是main页面</h1>
<span>${name}</span>
</body>
</html>
(2) 执行器方法绑定参数
@RequestMapping(value = "test07",method = {RequestMethod.GET})
public ModelAndView test07(Map<String,Object> map){
ModelAndView modelAndView = new ModelAndView();
map.put("name","tom");
modelAndView.addObject(map);
modelAndView.setViewName("main");
return modelAndView;
}
方式二
(1)页面定义请求
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="firstController/test08">测试</a>
</body>
</html>
main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>main</title>
</head>
<body>
<h1>这里是main页面</h1>
<span>${name}</span>
</body>
</html>
(2) 执行器方法绑定参数
@RequestMapping(value = "test08",method = {RequestMethod.GET})
public ModelAndView test08(ModelMap modelMap){
ModelAndView modelAndView = new ModelAndView();
modelMap.addAttribute("name","mary");
modelAndView.setViewName("main");
return modelAndView;
}
dView.addObject(map);
modelAndView.setViewName(“main”);
return modelAndView;
}
#### 方式二
(1)页面定义请求
index.jsp
```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="firstController/test08">测试</a>
</body>
</html>
main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>main</title>
</head>
<body>
<h1>这里是main页面</h1>
<span>${name}</span>
</body>
</html>
(2) 执行器方法绑定参数
@RequestMapping(value = "test08",method = {RequestMethod.GET})
public ModelAndView test08(ModelMap modelMap){
ModelAndView modelAndView = new ModelAndView();
modelMap.addAttribute("name","mary");
modelAndView.setViewName("main");
return modelAndView;
}