SpringMVC的三层架构和MVC
SpringMVC简介
三层架构概述:
一种是 C/S 架构,也就是客户端/服务器,另一种是 B/S 架构,也就是浏览器服务器。在 JavaEE 开发中,几乎全都是基于 B/S 架构的开发。那么在 B/S 架构中,系统标准的三层架构
包括:表现层、业务层、持久层。三层架构在我们的实际开发中使用的非常多,所以我们课程中的案例也都是基于三层架构设计的。
三层架构中,每一层各司其职,接下来我们就说说每层都负责哪些方面:
(1)表现层(web):也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
(2)业务层(Service, Business,biz):也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。
(3)持久层(dao mapper):也就是我们是常说的 dao 层。负责数据持久化,和数据库做交互。
MVC模式介绍
MVC模式则分为三大块,即视图(View),控制器(Control) ,模型(model) ,视图是jsp负责的页面显示,控制器则是Servlet负责的控制分发,模型包括Service和Dao两个部分,分别负责业务逻辑和数据访问。
MVC模式的优缺点:
优点:分工明确,各司其职,互不干涉。适用于大型项目架构,有利于组件的重构
缺点:增加了系统开发的复杂度
SpringMVC概述
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等等
总结: springmvc 是应用web层的 基于MVC设计模式的轻量级的web框架。 对Servlet封装,支持restful风格。
SpringMVC的优势
对Servlet进行了封装,简化了web层开发
天生集成Spring框架
支持灵活的URL到页面控制器的映射。
支持静态模板技术:jsp FreeMark Velocity
非常灵活的数据验证、格式化和数据绑定机制,能时使用任何对象进行数据绑定,不必实现特定框架的API;
更加简单的异常处理
支持Restful风格
SpringMVC入门项目框架:
pom.xml
构建maven项目并添加依赖
<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>
配置springMVC的配置文件spplication.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"
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>
<!--处理器映射器:根据请求路径匹配映射路径找到对应的执行器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<!--处理器适配器:根据处理器映射器返回的执行器对象,去执行执行器对象-->
<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>
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">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>
</servlet-mapping>
</web-app>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="firstController/test01">测试</a>
</body>
</html>
main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
这是main页面
</body>
</html>
编写控制器注解
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 show(){
System.out.println("firstController");
return "main";
}
}
SpringMVC执行过程及原理
执行过程
浏览器客户端发起请求,请求到达服务器tomcat,tomcat将请求相关信息参数封装到对象request和response中,再将request和response对象交给service方法处理,在service方法中会根据请求路径将请求交给对应的controller执行器处理。
SpringMVC的请求响应流程
浏览器发送请求,被DispatcherServlet捕获,DispatcherServlet没有直接处理请求,而是将请求交给HandlerMapping处理器映射器,处理器映射器根据请求路径去controller控制层中匹配对应的执行器,并将匹配结果返回给DispatcherServlet,由DispacherServlet调用HandlerAdapter处理器适配器来执行控制层执行器方法;
执行器方法执行后的返回结果,由DispatcherServlet交给视图解析器ViewResolver来处理,找到对应的结果视图,渲染视图,并将结果响应给浏览器。
SpringMVC常用组件
DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 controller,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
DispatcherServlet本质上就是servlet web.xml当中
HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
Handler:后端处理器【自定义的,类似于servlet】
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。
HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
View:视图
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jsp、freemarkerView、pdfView,FreeMark ,thymeleaf 等。我们最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
总结: SpringMVC是一个基于组件开发的框架
一个中心三个基本点: SpringMVC提供的, 不需要用户开发。
一个中心: 前端控制器: DispatcherServlet
三个基本点:
处理器映射器
处理器适配器
视图解析器
< mvc:annotation-driven>标签说明:
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。使用 <mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter ( 处 理 适 配 器 ),可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>替代处理映射器和适配器的配置(一般开发中都需要该标签)。注意:我们只需要编写处理具体业务的控制器以及视图。
xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
<mvc:annotation-driven> 标签相当于以下配置:
<!-- HandlerMapping处理器映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerM
apping"></bean>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- HandlerAdapter处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerA
dapter"></bean>
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- HadnlerExceptionResolvers异常处理器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExcept
ionResolver"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolv
er"></bean>
<bean class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver"
></bean>
SpringMVC的RequeestMapping注解
用于定义映射路径,建立请求url和控制层方法之间的对应关系
Requestmapping 注解源码解读
@Target({ElementType.METHOD, ElementType.TYPE}) // class interface METHOD
@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 {};
}
RequestMapping注解的描述
注解位置:
类上:定义一级映射路径
方法上:定义二级映射路径
注解属性:
value:用于指定映射路径url.它和path属性的作用是一样的。
method:用于指定请求的方式。method={RequestMethod.Post}只能处理post请求。状态码为405
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数 key 和 value 必须和配置的一模一样。eg:params = {“username” },表示请求参数必须有username;
如果没有携带username 的参数:前端浏览器400 bad request
headers:用于指定限制请求消息头的条件。 案例:headers = “Request URL” 在请求当中的方法的时候,必须携带一个指定的请求头消息。
注意:多个属性之间是与的关系;
SpringMVC的请求参数绑定
绑定说明
绑定的机制
表单中请求参数都是基于 key=value的。SpringMVC绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
servlet:
get: localhost:8080/xxx?username==lisi&age=12
post: username == llisi & age=12 封装到请求体
后端:String name = request.getParameter(“username”);
String age = request.getParameter(“age”);
String [] xxx = request.getParameterValues(“hobby”);
Map<String.String[]> map = request.getParameter(“xxx”);
参数绑定,就是将获得参数绑定在方法的形式参数上,取代了request.getParameter(“xxx”)
支持的数据类型
简单类型参数:包括基本数据类型和String 类型
POJO 类型参数:包括实体类 复杂POJO
数组和集合类型参数:包括 List 结构和 Map 结构的集合 (包括数组)
使用要求
如果是基本数据类型或者 String类型:要求我们的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)
如果是POJO数据类型
如果是集合类型,有两种方式:
第一种:要求集合类型的请求参数必须在POJO中。在表单中请求参数名称要和POJO中集合属性名称 相同。给List 集合中的元素赋值,使用下标。给Map集合中的元素赋值,使用键值对。
第二种方式:接收的请求参数是json格式数据。需要借助一个注解实现。@RequestBody注解
二、参数绑定示例
application.xml
添加配置:< mvc:annotation-driven/>
FirstController.java 添加:
@RequestMapping(value = "test02",method = {RequestMethod.POST})
public String test02(int id,String name,String password,String email){
System.out.println(name);
System.out.println(password);
System.out.println(id);
System.out.println(email);
return "main";
}
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="firstController/test01">测试</a>
<form action="firstController/test02" method="post">
编号:<input type="text" name="id"><br>
姓名:<input type="text" name="name"><br>
密码:<input type="password" name="password"><br>
邮件:<input type="text" name="email"><br>
<input type="submit">
</form>
</body>
</html>
运行结果:
POJO类型作为参数
user:
package com.etime.entity;
import java.util.List;
public class User {
private int id;
private String name;
private String password;
private String email;
private Car car;
private List<Car> carList;
public User() {
}
public User(int id, String name, String password, String email, Car car) {
this.id = id;
this.name = name;
this.password = password;
this.email = email;
this.car = car;
}
// public User(int id, String name, String password, String email, List<Car> carList) {
// this.id = id;
// this.name = name;
// this.password = password;
// this.email = email;
// this.carList = carList;
// }
public User(int id, String name, String password, String email) {
this.id = id;
this.name = name;
this.password = password;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
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{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", car=" + car +
'}';
}
// @Override
// public String toString() {
// return "User{" +
// "id=" + id +
// ", name='" + name + '\'' +
// ", password='" + password + '\'' +
// ", email='" + email + '\'' +
// ", carList=" + carList +
// '}';
// }
// @Override
// public String toString() {
// return "User{" +
// "id=" + id +
// ", name='" + name + '\'' +
// ", password='" + password + '\'' +
// ", email='" + email + '\'' +
// '}';
// }
}
Car.java
package com.etime.entity;
public class Car {
private int id;
private String name;
private double price;
public Car() {
}
public Car(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public int getId() {
return id;
}
public void setId(int 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 +
'}';
}
}
FirstController.java
@RequestMapping(value = "test03",method = {RequestMethod.POST})
public String test02(User user){
System.out.println(user);
return "main";
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="firstController/test01">测试</a>
<%-- <form action="firstController/test02" method="post">--%>
<%-- 编号:<input type="text" name="id"><br>--%>
<%-- 姓名:<input type="text" name="name"><br>--%>
<%-- 密码:<input type="password" name="password"><br>--%>
<%-- 邮件:<input type="text" name="email"><br>--%>
<%-- <input type="submit">--%>
<%-- </form>--%>
<form action="firstController/test03" method="post">
编号:<input type="text" name="id"><br>
姓名:<input type="text" name="name"><br>
密码:<input type="password" name="password"><br>
邮件:<input type="text" name="email"><br>
车的编号:<input type="text" name="car.id"><br>
车的名称:<input type="text" name="car.name"><br>
车的价格:<input type="text" name="car.price"><br>
<input type="submit">
</form>
</body>
</html>
运行结果:
四、添加集合
如List
User是实体类
package com.etime.entity;
import java.util.List;
public class User {
private int id;
private String name;
private String password;
private String email;
//private Car car;
private List<Car> carList;
public User() {
}
// public User(int id, String name, String password, String email, Car car) {
// this.id = id;
// this.name = name;
// this.password = password;
// this.email = email;
// this.car = car;
// }
public User(int id, String name, String password, String email, List<Car> carList) {
this.id = id;
this.name = name;
this.password = password;
this.email = email;
this.carList = carList;
}
public User(int id, String name, String password, String email) {
this.id = id;
this.name = name;
this.password = password;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
// 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{" +
// "id=" + id +
// ", name='" + name + '\'' +
// ", password='" + password + '\'' +
// ", email='" + email + '\'' +
// ", car=" + car +
// '}';
// }
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", carList=" + carList +
'}';
}
// @Override
// public String toString() {
// return "User{" +
// "id=" + id +
// ", name='" + name + '\'' +
// ", password='" + password + '\'' +
// ", email='" + email + '\'' +
// '}';
// }
}
FirstController.java
@RequestMapping(value = "test04",method = {RequestMethod.POST})
public String test04(User user){
System.out.println(user);
return "main";
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="firstController/test01">测试</a>
<%-- <form action="firstController/test02" method="post">--%>
<%-- 编号:<input type="text" name="id"><br>--%>
<%-- 姓名:<input type="text" name="name"><br>--%>
<%-- 密码:<input type="password" name="password"><br>--%>
<%-- 邮件:<input type="text" name="email"><br>--%>
<%-- <input type="submit">--%>
<%-- </form>--%>
<%-- <form action="firstController/test03" method="post">--%>
<%-- 编号:<input type="text" name="id"><br>--%>
<%-- 姓名:<input type="text" name="name"><br>--%>
<%-- 密码:<input type="password" name="password"><br>--%>
<%-- 邮件:<input type="text" name="email"><br>--%>
<%-- 车的编号:<input type="text" name="car.id"><br>--%>
<%-- 车的名称:<input type="text" name="car.name"><br>--%>
<%-- 车的价格:<input type="text" name="car.price"><br>--%>
<%-- <input type="submit">--%>
<%-- </form>--%>
<form action="firstController/test03" method="post">
编号:<input type="text" name="id"><br>
姓名:<input type="text" name="name"><br>
密码:<input type="password" name="password"><br>
邮件:<input type="text" name="email"><br>
车的编号:<input type="text" name="carList[0].id"><br>
车的名称:<input type="text" name="carList[0].name"><br>
车的价格:<input type="text" name="carList[0].price"><br>
车的编号:<input type="text" name="carList[1].id"><br>
车的名称:<input type="text" name="carList[1].name"><br>
车的价格:<input type="text" name="carList[1].price"><br>
车的编号:<input type="text" name="carList[2].id"><br>
车的名称:<input type="text" name="carList[2].name"><br>
车的价格:<input type="text" name="carList[2].price"><br>
<input type="submit">
</form>
</body>
</html>
运行结果:
数组类型参数
index.jsp
<form action="/firstController/test04" method="post">
爱好:
<input type="checkbox" name="hobby" value="bb">篮球
<input type="checkbox" name="hobby" value="fb">足球
<input type="checkbox" name="hobby" value="ppb">乒乓球
<input type="submit">
</form>
FirstController.java
执行器方法绑定参数
@RequestMapping(value = "test04",method = {RequestMethod.POST})
public String test04(String[] hobby){
System.out.println(Arrays.toString(hobby));
return "main";
}
运行结果:
使用ServletAPI对象作为方法参数
在pom.xml中引入servletAPI的依赖jar包:(注意jar包作用范围provided:不参与项目部署)
<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>
index.jsp
<a href="firstController/test05">测试</a>
FirstController.java
@RequestMapping(value = "test05",method = {RequestMethod.GET})
public void test05(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("这是使用请求转发来进行跳转");
request.getRequestDispatcher("../main.jsp").forward(request,response);
}
运行结果:
请求参数乱码问题
POST 请求的编码问题,要在web.xml文件中配置编码过滤器:
<filter>
<filter-name>characterEncoding</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>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
运行结果:
参数绑定默认类型
参数绑定:默认类型:Map<String,Object>ModelAndView Model ModelMap类型,能够给前台传递
方式一
@RequestMapping(value = "test06",method = {RequestMethod.GET})
public ModelAndView test06(Map<String,Object> map){
ModelAndView modelAndView= new ModelAndView();
map.put("name","mary");
modelAndView.addAllObjects(map);
modelAndView.setViewName("main");
return modelAndView;
}
index.jsp
<a href="firstController/test06">测试</a>
main.jsp
这是main页面<br>
${name}
方式二
@RequestMapping(value = "test07",method = {RequestMethod.GET})
public ModelAndView test07(ModelMap modelMap){
ModelAndView modelAndView = new ModelAndView();
modelMap.addAttribute("name","tom");
modelAndView.setViewName("main");
return modelAndView;
}
运行结果:
参数绑定默认类型
参数绑定:默认类型:Map<String,Object>ModelAndView Model ModelMap类型,能够给前台传递
方式一
@RequestMapping(value = "test06",method = {RequestMethod.GET})
public ModelAndView test06(Map<String,Object> map){
ModelAndView modelAndView= new ModelAndView();
map.put("name","mary");
modelAndView.addAllObjects(map);
modelAndView.setViewName("main");
return modelAndView;
}
index.jsp
<a href="firstController/test06">测试</a>
main.jsp
这是main页面<br>
${name}
方式二
@RequestMapping(value = "test07",method = {RequestMethod.GET})
public ModelAndView test07(ModelMap modelMap){
ModelAndView modelAndView = new ModelAndView();
modelMap.addAttribute("name","tom");
modelAndView.setViewName("main");
return modelAndView;
}