Spring MVC 框架基础
Spring MVC 基本应用
MVC体系结构
三层架构
-
表现层
web层。负责接收客户端请求,向客户端返回相应结果。
-
业务层
service层。负责处理业务逻辑。表现层依赖业务层,但业务层不依赖表现层。
-
持久层
dao层。负责数据持久化。和数据库进行交互。
MVC设计模式
MVC:Model View Controller。是一种用于设计创建 Web 应用程序的表现层模式。
-
Model:模型。包括业务模型和数据模型。业务模型用于封装业务数据,数据模型用于处理业务数据。
-
View:视图。用于展示数据。一般依赖于模型数据创建。
-
Controller:控制器。用于处理程序逻辑。
每一层都编写自己的东西,不编写任何其他代码。分层是为了解耦,解耦是为了维护方便和分工协作。
Spring MVC 是什么
Spring MVC:Spring Web MVC。是基于 Java 实现 MVC 设计模式的轻量级 web 框架。是 Spring FrameWork 的后续产品。本质可以认为是对 Servlet 的封装,简化了 Servlet 的开发。
spring MVC 的工作流程
请求处理流程
- 用户发送请求到前端控制器 DispatcherServlet。
- DispatchServlet 收到请求后调用 HandlerMapping(处理器映射器)。
- HandlerMapping(处理器映射器)根据请求的 URL 找到具体的 Handler(后端控制器),返回处理器执行链(处理器拦截器 + 处理器对象)给 DispatcherServlet。
- DispatcherServlet 调用 HandlerAdapter(处理器适配器)去调用 Handler(后端控制器)。
- HandlerAdapter(处理器适配器)执行 Handler(后端控制器)。
- Handler(后端控制器)执行完成后,返回 **ModelAndView ** 对象给 HandlerAdapter(处理器适配器)。
- HandlerAdapter(处理器适配器)将 **ModelAndView ** 对象返回给 DispatcherServlet。
- DispatcherServlet 请求 ViewResolver(视图解析器),根据逻辑视图名来解析真正的视图。
- ViewResolver(视图解析器)返回 View 对象给 DispatcherServlet 。
- DispatcherServlet 进行视图渲染,将 ModelAndView 中的数据模型填充到 request 域中。
- DispatcherServlet 向用户响应结果。
九大组件
组件名 | 描述 |
---|---|
HandlerMapping 处理器映射器 | 在请求到达后,查找请求相对应的 Handler(处理器)+ Interceptor(拦截器)。 标注了 @RequestMapping 的每个⽅法都可以看成是⼀个 Handler。 |
HandlerAdapter 处理器适配器 | 让固定的 Servlet 方法调用 Handler 来进行处理。 Servlet 的方法结构都是 doService(HttpServletRequest req,HttpServletResponse resp) 形式的。 Spirng MVC 的 Handler 可以是任意形式的,只要能处理请求即可。 |
HandlerExceptionResolver 处理程序异常解析器 | ⽤于处理 Handler 产⽣的异常情况。 根据异常设置 ModelAndView,之后交给渲染方法进行渲染。 |
ViewResolver 视图解析器 | Controller 层返回的 String 类型视图名 viewName 会被解析成 View 对象。找到渲染所用的模板 和 所用的技术 并填入参数。spring MVC 会默认配置一个 InternalResourceViewResolver 用于解析 JSP 视图。 |
RequestToViewNameTranslator 请求视图名称转换器 | 由于有的 Handler 没有设置 ViewName,ViewResolver 根据 ViewName 查询不到 View 。从请求中获取 viewName。 |
LocalResolver | 区域设置解析器 |
ThemeResolver | 主题解析器 |
MultipartResolver | 将普通请求包装成 MultipartHttpServletRequest 。使其拥有上传文件的功能。 |
FlashMapManager | 用于重定向时的参数传递。 |
请求参数绑定
http 协议:超文本传输协议。
原生 Servlet 接受一个整型的参数:
String ageStr = request.getParameter("age");
Integer age = Integer.parseInt(ageStr);
Spring MVC 对 Servlet 进行了封装,简化了 Servlet 的许多操作。
Spring MVC 环境搭建
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
web.xml
<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:applicationContext.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
applicationContext.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
">
<context:component-scan base-package="com.demo"/>
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 返回值 ModelAndView
@RequestMapping("/handle01")
// 返回 ModelAndView;Model 里面数数据模型;View 是需要跳转的页面地址
public ModelAndView handle01() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date", new Date());
modelAndView.setViewName("success");
return modelAndView;
}
// ModelMap;Model;Map;最终都会转换为==>BindingAwareModelMap
/**
* 继承体系如下:
* Model ——> ExtendedModelMap ——> BindingAwareModelMap
* Map ——> ModelMap ——> ExtendedModelMap ——> BindingAwareModelMap
*/
@RequestMapping("/handle11")
public String handle11(ModelMap modelMap) {
modelMap.addAttribute("date", new Date());
return "success";
}
@RequestMapping("/handle12")
public String handle12(Model model) {
model.addAttribute("date", new Date());
return "success";
}
@RequestMapping("/handle13")
public String handle13(Map<String, Object> map) {
map.put("date", new Date());
return "success";
}
@RequestMapping("/handle14")
public String handle14(ExtendedModelMap extendedModelMap) {
extendedModelMap.addAttribute("date", new Date());
return "success";
}
@RequestMapping("/handle15")
public String handle15(BindingAwareModelMap bindingAwareModelMap) {
bindingAwareModelMap.addAttribute("date", new Date());
return "success";
}
- 绑定简单数据类型
// 对八种基本数据类型的支持
// 除了 boolean 型的推荐用包装类 可以为 null
// 对于 boolean,Boolean 0 —— false;1 —— true;
// 对于 String 直接 传就行了
// 127.0.0.1:8080/demo/handle03?hello=127&s=60&id=1&uid=2&money=3&age=4&letter=0&flag=1
@RequestMapping("/handle03")
public String handle03(@RequestParam(value = "hello", required = false) Byte b, Short s, Integer id, Long uid, Double money, Float age, Character letter, Boolean flag) {
System.out.println(b);
System.out.println(s);
System.out.println(id);
System.out.println(uid);
System.out.println(money);
System.out.println(age);
System.out.println(letter);
System.out.println(flag);
return "success";
}
- 对原生 Servlet 参数的支持
@RequestMapping("/handle02")
// 对原生 servlet 的支持
public String handle02(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
request.setAttribute("date", new Date());
return "success";
}
- 绑定 POJO 类型参数
// 对 pojo 类型的支持
// 如果传两个对象 属性名相同的会同时赋值 属性名不同的会单独赋值
// 127.0.0.1:8080/demo/handle04?user.id=1&username=myName&otherAttribute=1233
@RequestMapping("/handle04")
public String handle04(User user, Substance substance) {
System.out.println(user.getId());
System.out.println(user.getUsername());
System.out.println(substance.getId());
System.out.println(substance.getUsername());
System.out.println(substance.getOtherAttribute());
return "success";
}
- 绑定 POJO 包装对象参数
// 对 pojo 里面包含 pojo 的支持
// 127.0.0.1:8080/demo/handle05?id=1&username=myName&otherAttribute=1233&user.id=2&user.username=username&user.subUser.age=1001
@RequestMapping("/handle05")
public String handle05(Substance substance) {
System.out.println(substance);
return "success";
}
- 绑定日期类型参数
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<!-- 配置自定义的类型转换器 -->
<bean id="conversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.demo.converter.DateConverter"/>
</set>
</property>
</bean>
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;
}
}
@RequestMapping("/handle06")
public String handle06(Date birthday) {
System.out.println(birthday);
return "success";
}
Restful 风格请求支持
// 对 rest 风格接口的支持
@RequestMapping(value = "/handle/{id}", method = RequestMethod.GET)
public String handleGet(@PathVariable Integer id) {
System.out.println("GET:" + id);
return "success";
}
@RequestMapping(value = "/handle/{id}", method = RequestMethod.POST)
public String handlePost(@PathVariable Integer id) {
System.out.println("POST:" + id);
return "success";
}
@RequestMapping(value = "/handle/{id}", method = RequestMethod.DELETE)
public String handleDelete(@PathVariable Integer id) {
System.out.println("DELETE:" + id);
return "success";
}
@RequestMapping(value = "/handle/{id}", method = RequestMethod.PUT)
public String handlePut(@PathVariable String id) {
System.out.println("PUT:" + id);
return "success";
}
JSON 交互
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
// 对 JSON 数据的支持
// 需要导入 jackson 的 pom
@RequestMapping("/handle07")
public @ResponseBody User handle07(@RequestBody User user) {
user.setUsername("张三丰");
return user;
}
Spring MVC 高级应用
拦截器的使用
监听器、过滤器、拦截器
- Servlet:处理 request 请求 和 response 响应。
- Filter:过滤器。对 request 请求起到过滤作用,作用在 Servlet 之前。配置为 /* 可以对所有的资源(servlet、js/css静态资源等)访问进行过滤。
- Listener:监听器。实现了javax.servlet.ServletContextListener 接口的服务器端组件,它随Web应用的启动而启动,只初始化⼀次,然后会⼀直运行监视,随Web应用的停止而销毁。
- Interceptor:拦截器。是 Spring MVC 等表现层自己的,不会拦截 JSP/HTML/CSS/IMAGE 等的访问。只会拦截访问的控制方法(Handler)。
从配置的角度来看,Servlet、Filter、Listener 是配置在 web.xml 中的。而 Interceptor 是配置在表现层自己的配置文件中。
拦截器的执行流程
- 在 handler 方法业务逻辑执行之前执行
- 在 handler 业务逻辑执行之后 尚未跳转页面时执行
- 页面已经跳转渲染完毕之后执行
public class MyInterceptor01 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在 handler 方法业务逻辑执行之前执行
// 返回值 返回值 boolean 代表是否放行,true 代表放行,false 代表中止
System.out.println("MyInterceptor01 preHandle......");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在 handler 业务逻辑执行之后 尚未跳转页面时执行
System.out.println("MyInterceptor01 postHandle......");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 页面已经跳转渲染完毕之后执行
System.out.println("MyInterceptor01 afterCompletion......");
}
}
<mvc:interceptors>
<!--配置当前拦截器的url拦截规则,**代表当前⽬录下及其⼦⽬录下的所有url-->
<!--<mvc:mapping path="/**"/>-->
<!--exclude-mapping可以在mapping的基础上排除⼀些url拦截-->
<!--<mvc:exclude-mapping path="/demo/**"/>-->
<bean class="com.demo.interceptor.MyInterceptor01"/>
<!--<bean class="com.demo.interceptor.MyInterceptor02"/>-->
</mvc:interceptors>
多个拦截器的执行顺序
MyInterceptor01 preHandle......
MyInterceptor02 preHandle......
POST:1
MyInterceptor02 postHandle......
MyInterceptor01 postHandle......
MyInterceptor02 afterCompletion......
MyInterceptor01 afterCompletion......
处理 multipart 形式的数据
pom.xml
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
applicationContext.xml
<!-- 此处 id 必须写 且为 multipartResolver 不然拿不到 文件内容 会有 null 异常-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
@RequestMapping("/upload")
public String upload(MultipartFile uploadFile) {
String originalFilename = uploadFile.getOriginalFilename();
try {
uploadFile.transferTo(new File("D:\\learning\\spring-mvc", originalFilename));
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
在控制器中处理异常
@ControllerAdvice
public class GlobalExceptionResolver {
@ExceptionHandler(ArithmeticException.class)
public ModelAndView handleException(ArithmeticException exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", exception.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}
基于 Flash 的跨重定向数据传递
转发:url 不会变,参数也不会丢,一个请求。
重定向:url 会变,参数会丢失,需要重新携带参数。两个请求。
@RequestMapping("/handle99")
public String handle99(String name) {
// 利用 flash 的时候此处拿不到name;因为在页面跳转后值已经销毁了
System.out.println(name);
return "success";
}
@RequestMapping("/handleRedirect")
public String handleRedirect(String name, RedirectAttributes redirectAttributes) {
// return "redirect:handle99?name=" + name + "";
redirectAttributes.addFlashAttribute("name", name);
return "redirect:handle99";
}
利用 RedirectAttributes 设置一个 flash 类型的属性,该属性会暂时被存在 session 中,在页面跳转后进行销毁。
参考git:https://gitee.com/zhangyizhou/learning-spring-mvc-demo.git
1.自定义 mvc 项目:custom-mvc
1.spring mvc demo 项目:spring-mvc-demo