1. Spring简介
1.1 MVC 体系结构
三层架构
我们的开发架构⼀般都是基于两种形式,⼀种是 C/S 架构,也就是客户端/服务器;另⼀种是 B/S 架构,也就是浏览器服务器。在 JavaEE 开发中,⼏乎全都是基于 B/S 架构的开发。那么在 B/S 架构中,系统标准的三层架构包括:表现层、业务层、持久层。三层架构在我们的实际开发中使⽤的⾮常多,所以我们课程中的案例也都是基于三层架构设计的。
- 表现层 :也就是我们常说的web 层。它负责接收客户端请求,向客户端响应结果,通常客户端使⽤http 协议请求web 层,web 需要接收 http 请求,完成 http 响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
表现层依赖业务层,接收到客户端请求⼀般会调⽤业务层进⾏业务处理,并将处理结果响应给客户端。
表现层的设计⼀般都使⽤ MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系) - 业务层 :也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项⽬的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。
业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务⼀致性。(也就是我们说的, 事务应该放到业务层来控制) - 持久层 :也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进⾏持久化的载体,数据访问层是业务层和持久层交互的接⼝,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进⾏增删改查的。
MVC设计模式
MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 Web 应⽤程序表现层的模式。MVC 中每个部分各司其职:
- Model(模型):模型包含业务模型和数据模型,数据模型⽤于封装数据,业务模型⽤于处理业务。
- View(视图): 通常指的就是我们的 jsp 或者 html。作⽤⼀般就是展示数据的。通常视图是依据模型数据创建的。
- Controller(控制器): 是应⽤程序中处理⽤户交互的部分。作⽤⼀般就是处理程序逻辑的。
MVC提倡:每⼀层只编写⾃⼰的东⻄,不编写任何其他的代码;分层是为了解耦,解耦是为了维护⽅便和分⼯协作。
1.2 Spring MVC 是什么?
SpringMVC 全名叫 Spring Web MVC,是⼀种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级Web 框架,属于 SpringFrameWork 的后续产品。
SpringMVC 已经成为 ⽬前最主流的 MVC 框架 之⼀,并且 随着 Spring3.0 的发布,全⾯超越 Struts2,成为最优秀的 MVC 框架。servlet、struts实现接⼝、springmvc中要让⼀个java类能够处理请求只需要添加注解就ok
它通过⼀套注解,让⼀个简单的 Java 类成为处理请求的控制器,⽽⽆须实现任何接⼝。同时它还⽀持RESTful 编程⻛格的请求。
总之:Spring MVC和Struts2⼀样,都是 为了解决表现层问题 的web框架,它们都是基于 MVC 设计模式的。⽽这些表现层框架的主要职责就是处理前端HTTP请求。
Spring MVC 本质可以认为是对servlet的封装,简化了我们serlvet的开发
作⽤:1)接收请求 2)返回响应,跳转⻚⾯
2. Spring Web MVC ⼯作流程
开发过程:
1)配置DispatcherServlet前端控制器
<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.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2)开发处理具体业务逻辑的Handler(@Controller、@RequestMapping)
@Controller
@RequestMapping("/demo")
public class DemoController {
@RequestMapping("/handle01")
public ModelAndView handle01(){
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
//逻辑视图名
modelAndView.setViewName("sucess");
return modelAndView;
}
}
3)xml配置⽂件配置controller扫描,配置springmvc三⼤件
<?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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 开启controller扫描-->
<context:component-scan base-package="com.lagou.edu.controller" />
<!-- 配置springmvc视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- springmvc三大件:Spring视图解析器,处理映射器,处理适应器-->
<mvc:annotation-driven/>
</beans>
4)将xml⽂件路径告诉springmvc(DispatcherServlet)
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
- web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<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.xml</param-value>
</init-param>
</servlet>
<!-- 路径拦截,交给springmvc处理 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
方式一:带后缀,比如*.action *.do *.aaa
该种方式比较精确、方便,在以前和现在企业中都有很大的使用比例
方式二:/ 不会拦截 .jsp,但是会拦截.html等静态资源(静态资源:除了servlet和jsp之外的js、css、png等)
为什么配置为/ 会拦截静态资源???
因为tomcat容器中有一个web.xml(父),你的项目中也有一个web.xml(子),是一个继承关系
父web.xml中有一个DefaultServlet, url-pattern 是一个 /
此时我们自己的web.xml中也配置了一个 / ,覆写了父web.xml的配置
为什么不拦截.jsp呢?
因为父web.xml中有一个JspServlet,这个servlet拦截.jsp文件,而我们并没有覆写这个配置,
所以springmvc此时不拦截jsp,jsp的处理交给了tomcat
如何解决/拦截静态资源这件事?
方式三:/* 拦截所有,包括.jsp
-->
<!--拦截匹配规则的url请求,进入springmvc框架处理-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
/**
* url: http://localhost:8080/demo/handle01
*/
@RequestMapping("/handle01")
public ModelAndView handle01(@ModelAttribute("name") String name) {
int c = 1/0;
Date date = new Date();// 服务器时间
// 返回服务器时间到前端页面
// 封装了数据和页面信息的 ModelAndView
ModelAndView modelAndView = new ModelAndView();
// addObject 其实是向请求域中request.setAttribute("date",date);
modelAndView.addObject("date",date);
// 视图信息(封装跳转的页面信息) 逻辑视图名
modelAndView.setViewName("success");
return modelAndView;
}
/**
* SpringMVC在handler方法上传入Map、Model和ModelMap参数,并向这些参数中保存数据(放入到请求域),都可以在页面获取到
*
* 它们之间是什么关系?
* 运行时的具体类型都是BindingAwareModelMap,相当于给BindingAwareModelMap中保存的数据都会放在请求域中
*
* Map(jdk中的接口) Model(spring的接口)
*
* ModelMap(class,实现Map接口)
*
*
*
*
* BindingAwareModelMap继承了ExtendedModelMap,ExtendedModelMap继承了ModelMap,实现了Model接口
*
*/
/**
* 直接声明形参ModelMap,封装数据
* url: http://localhost:8080/demo/handle11
*
* =================modelmap:class org.springframework.validation.support.BindingAwareModelMap
*/
@RequestMapping("/handle11")
public String handle11(ModelMap modelMap) {
Date date = new Date();// 服务器时间
modelMap.addAttribute("date",date);
System.out.println("=================modelmap:" + modelMap.getClass());
return "success";
}
/**
* 直接声明形参Model,封装数据
* url: http://localhost:8080/demo/handle12
* =================model:class org.springframework.validation.support.BindingAwareModelMap
*/
@RequestMapping("/handle12")
public String handle12(Model model) {
Date date = new Date();
model.addAttribute("date",date);
System.out.println("=================model:" + model.getClass());
return "success";
}
/**
* 直接声明形参Map集合,封装数据
* url: http://localhost:8080/demo/handle13
* =================map:class org.springframework.validation.support.BindingAwareModelMap
*/
@RequestMapping("/handle13")
public String handle13(Map<String,Object> map) {
Date date = new Date();
map.put("date",date);
System.out.println("=================map:" + map.getClass());
return "success";
}
- springmvc.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
https://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
">
<!--开启controller扫描-->
<context:component-scan base-package="com.lagou.edu.controller"/>
<!--配置springmvc的视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--
自动注册最合适的处理器映射器,处理器适配器(调用handler方法)
-->
<mvc:annotation-driven conversion-service="conversionServiceBean"/>
<!--注册自定义类型转换器-->
<bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.lagou.edu.converter.DateConverter"></bean>
</set>
</property>
</bean>
<!--静态资源配置,方案一-->
<!--
原理:添加该标签配置之后,会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler对象
这个对象如同一个检查人员,对进入DispatcherServlet的url请求进行过滤筛查,如果发现是一个静态资源请求
那么会把请求转由web应用服务器(tomcat)默认的DefaultServlet来处理,如果不是静态资源请求,那么继续由
SpringMVC框架处理
-->
<!--<mvc:default-servlet-handler/>-->
<!--静态资源配置,方案二,SpringMVC框架自己处理静态资源
mapping:约定的静态资源的url规则
location:指定的静态资源的存放位置
-->
<mvc:resources location="classpath:/" mapping="/resources/**"/>
<mvc:resources location="/WEB-INF/js/" mapping="/js/**"/>
<mvc:interceptors>
<!--拦截所有handler-->
<!--<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>-->
<mvc:interceptor>
<!--配置当前拦截器的url拦截规则,**代表当前目录下及其子目录下的所有url-->
<mvc:mapping path="/**"/>
<!--exclude-mapping可以在mapping的基础上排除一些url拦截-->
<!--<mvc:exclude-mapping path="/demo/**"/>-->
<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.lagou.edu.interceptor.MyIntercepter02"/>
</mvc:interceptor>
</mvc:interceptors>
<!--多元素解析器
id固定为multipartResolver
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置上传文件大小上限,单位是字节,-1代表没有限制也是默认的-->
<property name="maxUploadSize" value="5000000"/>
</bean>
</beans>
2.1 Spring MVC 请求处理流程
- 流程说明
第⼀步:⽤户发送请求⾄前端控制器DispatcherServlet
第⼆步:DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截器(如果 有则⽣成)⼀并返回DispatcherServlet
第四步:DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
第五步:处理器适配器执⾏Handler
第六步:Handler执⾏完成给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个底层对 象,包括 Model 和 View
第⼋步:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
第九步:视图解析器向前端控制器返回View
第⼗步:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第⼗⼀步:前端控制器向⽤户响应结果
3. 请求参数绑定(串讲)
请求参数绑定:说⽩了SpringMVC如何接收请求参数
http协议(超⽂本传输协议): 他的意思就是所有得文件传输都是文本类型
接收时用字符串接收然后再用字符串类型转换成其他类型
原⽣servlet接收⼀个整型参数:
1)String ageStr = request.getParameter(“age”);
2) Integer age = Integer.parseInt(ageStr);
SpringMVC框架对Servlet的封装,简化了servlet的很多操作
SpringMVC在接收整型参数的时候,直接在Handler⽅法中声明形参即可
@RequestMapping(“xxx”)
public String handle(Integer age) {
System.out.println(age);
}
参数绑定:取出参数值绑定到handler⽅法的形参上
- 默认⽀持 Servlet API 作为⽅法参数
当需要使⽤HttpServletRequest、HttpServletResponse、HttpSession等原⽣servlet对象时,直接在handler⽅法中形参声明使⽤即可。
/**
*
* SpringMVC 对原⽣servlet api的⽀持 url:/demo/handle02?id=1
*
* 如果要在SpringMVC中使⽤servlet原⽣对象,⽐如
HttpServletRequest\HttpServletResponse\HttpSession,直接在Handler⽅法形参中声
明使⽤即可
*
*/
@RequestMapping("/handle02")
public ModelAndView handle02(HttpServletRequest request, HttpServletResponse response,HttpSession session) {
String id = request.getParameter("id");
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
- 绑定简单类型参数
简单数据类型:⼋种基本数据类型及其包装类型
参数类型推荐使⽤包装数据类型,因为基础数据类型不可以为null
整型:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
说明:对于布尔类型的参数,请求的参数值为true或false。或者1或0
注意:绑定简单数据类型参数,只需要直接声明形参即可(形参参数名和传递的参数名要保持⼀致,建议 使⽤包装类型,当形参参数名和传递参数名不⼀致时可以使⽤@RequestParam注解进⾏⼿动映射)@RequestParam()括号里面得参数名称和url里面得名称一样得
SpringMVC 接收简单数据类型参数 url:/demo/handle03?id=1
注意:接收简单数据类型参数,直接在handler⽅法的形参中声明即可,框架会取出参数值然后绑定到对应参数上
要求:传递的参数名和声明的形参名称保持⼀致
@RequestMapping("/handle03")
public ModelAndView handle03(@RequestParam("ids") Integer id,Boolean flag) {
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
- 绑定Pojo类型参数
SpringMVC接收pojo类型参数 url:/demo/handle04?id=1&username=zhangsan
接收pojo类型参数,直接形参声明即可,类型就是Pojo的类型,形参名⽆所谓
但是要求传递的参数名必须和Pojo的属性名保持⼀致
@RequestMapping("/handle04")
public ModelAndView handle04(User user) {
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
- 绑定Pojo包装对象参数
包装类型 QueryVo
package com.lagou.edu.pojo;
public class QueryVo {
private String mail;
private String phone;
// 嵌套了另外的Pojo对象
private User user;
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Handler⽅法
/*
* SpringMVC接收pojo包装类型参数 url:/demo/handle05?user.id=1&user.username=zhangsan
* 不管包装Pojo与否,它⾸先是⼀个pojo,那么就可以按照上述pojo的要求来
* 1、绑定时候直接形参声明即可
* 2、传参参数名和pojo属性保持⼀致,如果不能够定位数据项,那么通过属性名 + "." 的⽅式进⼀步锁定数据
*
*/
@RequestMapping("/handle05")
public ModelAndView handle05(QueryVo queryVo) {
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
- 绑定⽇期类型参数(需要配置⾃定义类型转换器)
前端jsp
<fieldset>
<p>测试⽤例:SpringMVC接收⽇期类型参数</p>
<a href="/demo/handle06?birthday=2019-10-08">点击测试</a>
</fieldset>
后台Handler⽅法
/**
* 绑定⽇期类型参数
* 定义⼀个SpringMVC的类型转换器
接⼝,扩展实现接⼝接⼝,注册你的实现
* @param birthday
* @return
*/
@RequestMapping("/handle06")
public ModelAndView handle06(Date birthday) {
Date date = new Date();ModelAndView modelAndView = new
ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
⾃定义类型转换器
package com.lagou.edu.converter;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author 应癫
* ⾃定义类型转换器
* S:source,源类型
* T:target:⽬标类型
*/
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
// 完成字符串向⽇期的转换
SimpleDateFormat simpleDateFormat = new
SimpleDateFormat("yyyy-MM-dd");
try {
Date parse = simpleDateFormat.parse(source);
return parse;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
注册⾃定义类型转换器
<!--
⾃动注册最合适的处理器映射器,处理器适配器(调⽤handler⽅法)
-->
<mvc:annotation-driven conversion-service="conversionServiceBean"/>
<!--注册⾃定义类型转换器-->
<bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.lagou.edu.converter.DateConverter">
</bean>
</set>
</property>
</bean>
附录⼀ 乱码问题解决
- Post请求乱码,web.xml中加⼊过滤器
<!-- 解决post乱码问题 -->
<filter>
<filter-name>encoding</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<!-- 设置编码参是UTF8 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- Get请求乱码(Get请求乱码需要修改tomcat下server.xml的配置)
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080"
protocol="HTTP/1.1" redirectPort="8443"/>