同上一篇博客,复习梳理SpringMVC知识点,这次的梳理比较快,很多细节没有顾虑到,后期可能会回来补充

1. 整体架构

1.1 在学习了SSM框架后我们来理清三者的应用层面

浏览器发送请求,请求到达SpringMVC处理,然后调用业务层逻辑实现,跟着持久层操作获取数据,最后逆序响应到浏览器。前面我们复习了Mybaits和Spring框架,我们当然不陌生了,现在就来了解下SpringMVC到底有什么作用

1.2 MVC

MVC模型中,M是把浏览器传的参数封装成的pojo类型,V则代表视图,C就是控制器也是重点。SpringMVC是以组件的形式来形成整体的,下面也是画图来解释



网图,侵删

  • 核心控制器被Tomcat初始化并主动加载applicationContext配置文件
  • 用户发送请求
  • 请求到达核心控制器
  • 核心控制器交由映射器处理映射地址
  • 核心控制器找到适配器来适配处理器(适配器模式)
  • 将请求过来的数据进行转换
  • 将转好的数据给处理器处理并沿路返回
  • 最后通过视图解析器解析
  • 响应对应的页面

从上面可以看出 DispatcherServlet 是核心指挥中心,MVC框架围绕其来设计的,处理所有的http请求和响应

DispatcherServlet 收到请求后根据HandlerMappering来选择并且调用映射的控制器

控制器接收到请求后基于GET、POST调用适当的Servce方法后将数据返回到DispatcherServlet中

上面所说的HandlerMapping、Controller是WebApplicationContext的一部分,其是ApplicationContext的扩展,也是BeanFactory的扩展

启动Tomcat,初始化web.xml中的 DispatcherServlet ,而DispatcherServlet 框架则尝试加载applicationContext.xml配置文件内容

2. 映射关系

MVC作用在表现层用来处理请求,所以地址映射也在这里,即在Controller中,请求是在方法上处理的,不是类上(这也是单例的原因,类上使用映射即为分模块作用),方法的返回值默认为返回的网页地址(现在前后端分离使用得比较少了,下面讲解都是用前后端分离模式),其映射关系使用注解的过程为:

@Controller
@RequestMapping("/user")
public class HelloController { @RequestMapping(value = "/hello",method = RequestMethod.GET)
public String sayHello(){
System.out.println("Hello World");
return "success";
}
}

3. 参数绑定

这里是重点,因为请求一般都带数据的,然后在这里绑定成Model,方便我们使用,不用再像JavaWeb程序中request.getParameter()了,支持基本类型、String类型,bean类型以及集合类型

这里一个小插曲,如果要获取request、response,则在方法参数上自己添加即可

3.0 这里先给出需要用到的Bean

public class User {

    private int id;
private String name;
private String email;
private InnerBean innerBean;
private List<InnerBean> innerBeanList;
private String[] array; // 省略各种getter / setter
}
public class InnerBean {

    private String inner;

    public void setInner(String inner) {
this.inner = inner;
}
}

3.1 简单参数绑定

MVC框架会在方法参数中绑定请求中名字相同的变量(使用了反射),简单参数为基本类型和String,参数名若不同则使用@RequestParam注解绑定

<form action="param/param1" method="get">
id:<input type="text" name="id">
name:<input type="text" name="name">
email:<input type="text" name="email">
<input type="submit" value="submit1">
</form> // 表单中name和形参名字相同 // 简单参数
@RequestMapping(value = "/param1")
public String getParameter1(String id, String name, String email) {
System.out.println(id + name + email) ;
return "success";
}

3.2 Bean封装

bean类型封装是用过里面的setter实现的,而且还有bean中有bean的情况

<form action="param/param2" method="get">
id:<input type="text" name="id">
name:<input type="text" name="name">
email:<input type="text" name="email">
inner:<input type="text" name="innerBean.inner">
<input type="submit" value="submit2">
</form> // 封装Bean对象,依靠setter方法
@RequestMapping(value = "/param2")
public String getParameter2(User user) {
System.out.println(user) ;
return "success";
}

3.3 集合封装

<form action="param/param3" method="get">
id:<input type="text" name="id">
name:<input type="text" name="name">
email:<input type="text" name="email">
inner:<input type="text" name="innerBeanList[0].inner">
inner:<input type="text" name="innerBeanList[1].inner">
inner:<input type="text" name="array[0]">
inner:<input type="text" name="array[1]">
<input type="submit" value="submit2">
</form> // 封装集合
@RequestMapping(value = "/param3")
public String getParameter3(User user) {
System.out.println(user) ;
return "success";
}

4. 类型转换器

请求传过来的数据都是字符串,那么我们使用的时候为什么可以获取其他类型呢?这里是使用了框架内部的默认转换器所以才可以取得其他类型数据,但如果默认转换器识别不了,那么我们就要自己配置类型转换器来实现功能

这里有个场景:前端传2020/3/2过来让Date类型接收是没问题的,但是如果前端传了2020-3-2呢?这样就会报错,因为2020-3-2框架没有这个转换器来转成Date类型,那么就需要我们手动来设置

@Controller
@RequestMapping(value = "/converter")
public class ConverterController { @RequestMapping(value = "/converter")
public String converterMethod(Date date){
System.out.println(date);
return "success";
}
}

4.1 创建转换器类

这个类实现了Converter<S,T>接口,这个泛型要自己添加,返回类型为转换好的类型

public class StringToDateConverter implements Converter<String, Date> {

    /**
* 需要自己手动添加泛型,s指传进来的字符串
*/
public Date convert(String s) {
s = s.replace('/','-');
DateFormat sdf = new SimpleDateFormat("yyyy-MM-DD");
try {
return sdf.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}

4.2 将自定义转换器注册在转换器服务工厂中,并给容器管理

<!--  自定义类型转换器  -->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<!-- set标签 -->
<set>
<bean class="com.howl.util.StringToDateConverter"></bean>
</set>
</property>
</bean>

4.3 注册组件

因为MVC是基于组件的,所以使用了组件就要在配置文件中注册

<!--  开启mvc注解支持,并且组件生效,默认使用适配器和映射器  -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>

5. 文件上传

要求:

1、表单要是enctype="multipart/form-data"

2、方法要是POST

3、输入框要是<input type="file">

当上传表单为多个文件时,根据hppt请求体来分割很复杂,所以要借助第三方jar,也就是传统的上传方法,该方法依赖 commons-fileupload(当然下面的MVC的简化文件上传也要该依赖),传统的文件上传笔者已经写过一篇博文了,请点击这里

至于MVC的上传呢,更加简便。MVC提供了MultipartFiled对象,需要表单的name与之对应

<p>文件上传</p>
<form action="upload/upload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="uploads">
选择文件:<input type="file" name="uploads">
<input type="submit" value="submit">
</form> @RestController
@RequestMapping(value = "/upload")
public class FileUploadController { @RequestMapping(value = "/upload")
public String fileUploadMethod(HttpServletRequest httpServletRequest, MultipartFile[] uploads) throws IOException { // 创建目录
String path = httpServletRequest.getSession().getServletContext().getRealPath("/uploads/");
File file = new File(path);
if(!file.exists()){
file.mkdirs();
} for(MultipartFile value : uploads){ // 原名
String originalFilename = value.getOriginalFilename();
// 生成随机名
String fileName = UUID.randomUUID().toString().replace("-","") + "_" + originalFilename;
// 和传统文件上传不同,参数为File,差别看博客
value.transferTo(new File(path,fileName));
} // 笔者这里返回了地址,一般返回成功消息的
return path;
}
}

MVC是基于组件的,所以文件解析器也是一个组件需要配置

<!--  文件解析器,名字必须是multipartResolver  -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="1024102410"></property>
</bean>

6. 异常处理

正常操作是底层发生异常会一直向上抛,直到发给浏览器用户看到,我们要避免这种事情发生,就需要异常处理,所以我们要把流程改成下面这样

6.1 编写自定义异常类(做提示信息)

public class MyException extends Exception {

    private String msg;

    public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
} public MyException(String msg) {
this.msg = msg;
}
}

6.2 编写全局异常处理器(注解版)

// 获取所有异常
@RestControllerAdvice()
public class GlobalExceptionHandler { @ExceptionHandler(value = Exception.class)
public String handleException(Exception e){ // 如果是自定义异常,则发送自己的消息
if(e instanceof MyException){
return ResponseHelper.error(((MyException) e).getMsg(),new User());
} // 否则发送系统错误
return ResponseHelper.error("系统错误");
}
}

7. 拦截器

类似于Filter,但拦截器是对处理器Controller进行预处理和后处理,不同于Filter拦截Servlet。拦截器是MVC内部的,使用MVC框架才有拦截器,而过滤器是javaWeb内部的。范围不同,Filter中配置 /*会过滤所有请求,拦截器应用场景有:权限检查和日志处理

7.1 实现HandlerInterceptor

这个接口要自己手动输入重写,因为1.8之后接口变了,笔者在这里配置权限检查

public class MyInterceptor implements HandlerInterceptor {

    /**
* @return true表示放行,执行下一个拦截器,false表示拦截
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("user");
if(user == null){
// 没登录,重定向
response.sendRedirect("/admin/login.html");
}
// 否则放行
return true;
} public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { }
}

7.2 applicationContext配置拦截器

万物皆组件

<!--  配置拦截器,注意path是写url地址  -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/interceptor/*"/>
<bean class="com.howl.interceptor.MyInterceptor"></bean>
</mvc:interceptor> <!-- 第二个拦截器应该这里配 -->
<mvc:interceptor>
<mvc:mapping path="/interceptor/*"/>
<bean class="com.howl.interceptor.MyInterceptor"></bean>
</mvc:interceptor> </mvc:interceptors>

8. 注解总览

@Controller
@RequestMapping(value = "/hello",method = RequestMethod.GET)
@RequestParam(value = "name") // 用于匹配名字不一致
@PathVariable(value = "sid") // 绑定url中的占位符,主要用于Restful风格,下面有这里 @ResponseBody // 主要用于响应json数据,即Controller方法的返回值通过适当转换器后,写入Response不走视图解析器,笔者用fastjson将bean转换成json即String类型返给前端,即前后端分离 @RequestBody // 若异步请求,则发送给后端的是json数据无法绑定参数,用了这个注解,将获取请求体中全部参数,以key=value的形式,get方法不在请求体中,无法使用,当以键值对出现时,则是换成普通请求的数据格式,使用setter将绑定参数 @@RestController // @Controller和@ResponseBody的结合,用于前后分离,不走视图解析器,可放于类上,则类中的全部方法适用,而@RequestBody则不行

@PathVariable

@RequestMapping("/anno2/{sid}")
public String annoMethod2(@PathVariable(value = "sid") int id) {
System.out.println(id);
return "success";
}

9. 补充

@RestController中文乱码

其默认使用tomcat的编码,而且直接返回给前端所以会乱码

方法一:映射注解上加属性

@RequestMapping(value = "/user", produces = "application/json;charset=utf-8")

方法二:全局配置编码问题

<!--  开启mvc的注解支持,并且在Responsebody上使用UFT-8  -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

DispatcherServlet在配置映射关系中用/,则会拦截所有请求,包括静态资源而导致无法访问,所以要在applicationContext中配置不拦截

<!--  配置静态资源不拦截  -->
<mvc:resources mapping="/pages/*" location="/pages/"></mvc:resources>

MVC 三大组件:适配器,映射器,解析器

在Spring的基础上需要的额外jar包:spring-web、spring-mvc

约束

<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> </beans>

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.howl</groupId>
<artifactId>SpringPractice</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging> <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency> <dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency> <dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency> <!-- HttpServletRequest要用,一般Tomcat自带 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency> </dependencies>
</project>

Spring MVC知识梳理的更多相关文章

  1. spring远程服务知识梳理

    序:本文主要是总结和归纳spring的远程服务相关知识,可作为入门学习笔记.写博客目的也是为了进行知识梳理,便于以后查看.本文主要参考资料 spring 实战第三版 本文主要讨论内容如下: 远程调度概 ...

  2. 浅谈Spring MVC知识

    关于MVC框架,我相信大家都不陌生,都会说也就是模型-视图-控制器这三层的框架结构,如果你参加面试的时候考官会问:“MVC框架是什么?你说一说.”其实我们都知道这个问题还需要问的,只要你是一个开发人员 ...

  3. Spring核心组件知识梳理

    Spring的一些概念和思想 Spring的核心:AOP.IOC. 简单点说,就是把对象交给Spring进行管理,通过面向切面编程来实现一些"模板式"的操作,使得程序员解放出来,可 ...

  4. Spring MVC知识

    f-sm-1. 讲下SpringMvc和Struts1,Struts2的比较的优势 性能上Struts1>SpringMvc>Struts2 开发速度上SpringMvc和Struts2差 ...

  5. Spring MVC 知识总结

    参考文章:http://www.oschina.net/question/84460_9608 孔浩视频 1. 几个关键类: RequestMappingHandlerMapping 和 Reques ...

  6. Spring框架知识梳理(一) IOC

    1 写在前面 Spring框架是在大一的时候学习的,但是经过几个项目下来发现自己只不过会用某些常用的东西,对于Spring家族,虽然现在大都使用Spring Boot开发,但是我发现Spring框架的 ...

  7. spring、spring mvc、mybatis框架整合基本知识

    学习了一个多月的框架知识了,这两天很想将它整合一下.网上看了很多整合案例,基本都是基于Eclipse的,但现在外面公司基本都在用Intellij IDEA了,所以结合所学知识,自己做了个总结,有不足之 ...

  8. Spring MVC基础知识整理➣拦截器和自定义注解

    概述 Spring MVC中通过注解来对方法或者类进行动态的说明或者标注,类似于配置标识文件的属性信息.当标注的类或者方式被使用时候,通过提取注解信息来达到对类的动态处理.在 MVC中,我们常用的注解 ...

  9. spring笔记4 spring MVC的基础知识4

    //todo 5,spring MVC的本地化解析,文件上传,静态资源处理,拦截器,异常处理等 spring MVC 默认使用AcceptHeaderLocalResolver,根据报文头的Accep ...

随机推荐

  1. mysql之存储过程(三)

    带参数的存储过程: 特别说明: 在游标中是不支持对形参的判断的,外部可以 调用操作: call settlexxxxx_common("1970-11",999); 定义如下:   ...

  2. The website is API(1)

    Requests 自动爬取HTML页面 自动网路请求提交 robots 网络爬虫排除标准 Beautiful Soup 解析HTML页面 实战 Re 正则表达式详解提取页面关键信息 Scrapy*框架 ...

  3. 数据结构与算法——认识O(NlogN)的排序(1)

    归并排序 1) 整体就是一个简单递归,左边排好序.右边排好序.让其整体有序 2) 让其整体有序的过程里用了外排序方法 3) 利用master公式来求解时间复杂度 4) 归并排序的实质 时间复杂度0(N ...

  4. 华为鸿蒙系统pk安卓系统

    Harmony OS Vs Android Comparison It isn’t based on Linux kernel The key difference between HarmonyOS ...

  5. MDS算法及其matlab实现

    问题背景: 在求解MTSP问题的时候,因为已知的为各个巡检点之间路径耗时长度,而这个具体描述采用无向图结构可以很好的描述,在matlab中通过函数(graphallshortestpaths)可以得到 ...

  6. 2. Unconstrained Optimization

    2.1 Basic Results on the Existence of Optimizers 2.1. Let \(f:U->\mathbb{R}\) be a function on a ...

  7. python学习笔记(22)-os文件操作模块

    疑问: 如果打开操作一个文件,是用绝对路径好还是相对路径好? os模块,在lib下面,可以直接引入的,直接使用import. 一.新建一个目录,新建一个文件夹 import os #新建一个文件夹 o ...

  8. EmguCV从位图(Bitmap)加载Image<Gray,byte>速度慢的问题

    先说背景.最近在用C#+EmguCV(其实就是用P/Invoke封闭了OpecCV,与OpenCVDotNet差不多) 做一个视频的东西.视频是由摄像头采集回来的1f/s,2048X1000大小,其实 ...

  9. Qt QString的arg()方法的使用

    1.QString的arg()方法用于填充字符串中的%1,%2...为给定的参数,如 QString m = tr("); // m = "12:60:60: 2.它还有另外一种重 ...

  10. pycharm里配置了项目虚拟环境,terminal打开并不是在虚拟环境下,如何解决

    在pycharm里,点开下方的Terminal,此时默认目录为当前项目对应的根目录, 第一步:输入  cd   venv\Scripts  进入Scripts目录 第二步:然后运行activate.b ...