一、SpringMVC是什么
- 是基于spring的一个框架,实际上就是spring的一个模块,专门做web开发的
- 可以理解为Servlet的升级
- web开发底层就是Servlet,框架是在Servlet基础上面添加一些功能,做web项目更方便
- SpringMVC能够创建对象,放入到容器中(SpringMVC的容器) SpringMVC里面放的是控制器对象
- 我们要做的是使用
**@Controller**
注解创建控制器对象,把对象放入到SpringMVC容器中,把创建的对象作为控制器使用 - 这个控制器对象能接收用户的请求,显示处理结果,就当作是一个servlet使用【并不是servlet,就是普通对象】
- 使用@Controller创建的对象,其实就是个普通的对象,springMVC赋予了他控制器的能力
Springmvc底层访问依然是
DispatcherServlet
他的两大作用:web开发底层时servlet,springmvc中有一个对象是Servlet:
DispatcherServlet
(中央调度器) 负责接收用户所有请求,然后把请求转发给Controller对象,最后是Controller对象处理请求- Index.jsp———>DispatherServlet———>转发,分配给——->Controller对象(@Controller注解创建的对象)
三、SpringMVC入门案例
1、创建maven项目,webapp
因为maven创建的web项目中的 web.xml文件是 2.3版本的,需要重新创建一下
<?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">
</web-app>
2、加入依赖 spring-mvc 、servlet、
<!--springMVC的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
会间接的把spring的依赖都加入到项目中,
3、在web.mxl文件中,注册springmvc框架的核心对象 DispatcherServlet
DispatcherServlet
:中央调度器【父类继承Servlet,本身就是Servlet,具备所有servlet的功能】DispatcherServlet
:也叫前端控制器负责去接收用户提交的请求,调用其他的控制器对象 ,并把请求的处理结果显示给用户
<servlet>
<!--springmvc在创建容器对象时,读取的配置文件默认是 /WEB-INF/<servlet-name>-servlet.xml
也就是说 读取的就是下面这个name 后面加 -servlet.xml,
因为这个规则不方便,所以就不用这个默认的规则,去自定义文件所在的位置-->
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--设置在服务器启动时,马上创建DispatcherServlet对象-->
<!--load-on-startup:在启动时进行加载,
数字表示tomcat启动后创建的顺序,数制越小创建的对象时机越早 >=0的整数-->
<!--自定义文件所在位置-->
<init-param>
<!--指定SpringMVC的配置文件的属性,必须是contextConfigLocation-->
<param-name>contextConfigLocation</param-name>
<!--自定义文件的位置 classpath:-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--设置servlet-mapping-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--可以使用两种值,
1.使用扩展名的方式,语法*.xxxxxx是自定义的拓展名常用的有*.do*.action*.mvc
表示以.do结尾的请求,都会被该servlet-mapping处理 (没有/)
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2.使用"/"的方式
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
4、创建发起请求的页面
5、创建控制器类/后端控制器
@Controller
- 在类的上面加入
@Controller
注解,创建对象,放入到SpringMVC容器中 在中的方法上加入
@RequestMapping
注解声明组件扫描器,指定controller注解所在的包名
- 在resources目录下创建springmvc配置文件,和spring配置文件一样
- 声明组件扫描器
**<context:component-scan base-package="com.yixuexi"/>**
声明视图解析器,帮助开发人员设置视图文件的路径
- 为了防止用户恶意访问show.jsp, 把show.jsp放到 WEB-INF目录下,这样用户不可能在地址框搜索到
配置视图解析器
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀,视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"/>
<!--后缀,视图文件的拓展名-->
<property name="suffix" value=".jsp"/>
</bean>
在WEB-INF目录下有一个 view目录,里面放着 show.jsp
- 注意:springmvc配置文件的名字要和web.xml文件中的
这个属性中的值一致
为什么要在**tomcat**
启动后,创建**DispatcherServlet**
对象实例
- 因为,
DispatcherServlet
在他创建的过程中,会同时创建springmvc
容器对象, - 读取springmvc的配置文件,把配置文件中的对象都创建好,当用户发起请求时就可以直接使用对象了
总结
- 先导入依赖
- 然后在web.xml文件中,创建
DispachterServlet
对象 - 然后创建类 在类上添加@
Controller
注解, - 然后创建方法,方法返回值类型为
M``odelAndView
, 方法名随意 - 方法上添加@
RequestMapping
注解value
属性写 请求的uri地址,就是/login.do 或者 /register.do - 在springmvc的配置文件中,声明组件扫描器,和视图解析器
- 然后处理完之后,可以在mv那个对象上,进行添加数据 addObject(key,value); setViewName(“视图文件”)
四、返回值ModelAndView对象使用
ModelAndView mv = new ModelAndView();
//指定数据,添加数据,框架在最后把数据放到了request作用域中
//相当于 request.setAttribute("key","value"); 框架帮你做了
mv.addObject("key","欢迎使用mvc做web开发");
mv.addObject("function","执行的时doSome()方法");
//指定视图,指定视图的完整路径
//框架对视图执行的是forward操作,(请求转发)//request.getRequestDispatcher().forward() 框架帮你做了
mv.setViewName("/show.jsp"); //没有配置视图解析器的时候,show.jsp还在webapp目录下
//配置了视图解析器的时候,show.jsp在WEB-INF/view下,后缀也省了
//直接使用文件的名称就可以了,框架自己去/WEB-INF/view/下去找show.jsp
mv.setViewName("show")
说明:
使用@RequestMapping修饰的方法叫做处理器方法/通知器方法
使用@RequestMapping修饰的方法是可以处理请求的,类似于Servlet中的doGet() doPost()
返回值:ModelAndView : 本次请求的处理结果
Model:数据,请求处理完成后,要显示给用户的数据
View:视图,比如jsp/html等等的
五、SpringMVC web.xml 固定配置
web.xml 固定代码
<!--web.xml固定代码-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--设置在服务器启动时,马上拆功能键DispatcherServlet对象-->
<!--load-on-startup:在启动时进行加载,
数字表示tomcat启动后创建的顺序,数制越小创建的对象时机越早>=0的整数-->
<init-param>
<!--指定SpringMVC的配置文件的属性,必须是contextConfigLocation-->
<param-name>contextConfigLocation</param-name>
<!--自定义文件的位置classpath:-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--设置servlet-mapping-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--可以使用两种值,
1.使用扩展名的方式,语法*.xxxxxx是自定义的拓展名常用的有*.do*.action*.mvc
表示以.do结尾的请求,都会被该servlet-mapping处理(没有/)
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2.使用"/"的方式
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--注册过滤器,解决post请求中文乱码问题 web.xml文件配置-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<!--CharacterEncodingFilter-->
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--设置项目中使用的字符编码-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<!--强制请求对象,使用encoding编码的值-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制响应对象,使用encoding编码的值-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!--表示强制所有的请求先通过过滤器处理-->
<url-pattern>/*</url-pattern>
</filter-mapping>
springmvc.xml 固定代码
<!--组件扫描器,用来扫描注解创建对象的-->
<context:component-scan base-package="com.yixuexi"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀,视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"/>
<!--后缀,视图文件的拓展名-->
<property name="suffix" value=".jsp"/>
</bean>
<!--springmvc响应AJAX 请求返回一个json数据的对象 添加注解驱动-->
<mvc:annotation-driven/>
<!--url-pattern是/时,配置访问静态文件,目录要求:静态文件都在static文件下-->
<!--同时也需要上面那一句 <mvc:annotation-driven>-->
<mvc:resources mapping="/static/**" location="/static/"/>
拦截器
<!--声明拦截器,在框架中拦截器可以有0或多个-->
<mvc:interceptors>
<!--声明第一个-->
<mvc:interceptor>
<!--指定拦截的请求uri地址的
path:就是uri地址,可以使用通配符 **
**:表示任意的字符,文件,或者多级目录和目录中的文件
/** : 表示所有的请求都会被拦截
-->
<mvc:mapping path="/**"/>
<!--声明拦截器对象,表示访问上面那个地址的时候都由下面这个拦截器处理-->
<bean class="com.yixuexi.handler.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
六、@RequestMapping注解
准备使用doSome()
方法处理前端页面发来的login.do
的请求@RequestMapping
:请求映射,作用是把一个请求地址和一个方法绑定在一起
一个请求指定一个方法处理
1、属性:
1.**value**
: 表示请求的uri地址 唯一值【login.do】
- 在使用时,推荐以“/”开头
- 可以是数组,{“/xxx”,”/xxx”,”/xxx”} (多个请求一个方法处理)
2.method
:表示请求的方式,他的值是RequestMethod
的枚举值
- 如果没有指定的话,所有的请求方式都可以访问
- 例如表示
get
请求方式,RequestMethod.GET ``或者``.POST
- 如果该方法用的post请求,那么请求必须是post才能访问该方法【表单可以发post请求】 不按照对应的请求方式 报错 405
- 前端发送请求 开头不需要 /
2、位置
- 在方法的上面【表示这个方法处理这个请求】
- 在类的上面 【集中的定义,把公用的提取出来】
- value: 所有请求地址的公共部分 ,也叫模块名称
- 如果请求是这样的 test/login.do test/register.do 的话就可以在类的上面添加
@RequestMapping("/test")
注解,那么他的方法就不用加 “/test” 了 直接 /login.do就行了
七、请求的处理流程
1、springmvc请求的处理流程
第⼀步:⽤户发送请求⾄前端控制器DispatcherServlet
第⼆步:DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截器(如果 有则⽣成)⼀并返回DispatcherServlet
第四步:DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
第五步:处理器适配器执⾏Handler
第六步:Handler执⾏完成给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个底层对 象,包括 Model 和 View
第⼋步:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
第九步:视图解析器向前端控制器返回View
第⼗步:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第⼗⼀步:前端控制器向⽤户响应结果
2、springmvc执行过程分析 init源码分析
2.1 tomcat启动,创建容器的过程
- tomcat启动,先执行web.xml文件,然后通过load-on-startup标签中的1创建DispatcherServlet对象
- DispatcherServlet他的父类是继承HttpSerlvet的,他是一个Serlvet,在被创建时,会执行init方法。
- 在inut方法中会执行 new ClassPathWebApplicationContext(“文件地址”); 并把它放到全局作用域中
- 创建了容器,容器同时也创建了@Controller注解所在的对象,
- 这个controller对象放入到了springmvc的容器中,
3、组件说明
**DispatcherServlet**
前端控制器 用户请求到达前端控制器,它就相当于mvc中的c,DispatcherServlet是整个流程控制的中心,由他调用其他组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合度
**HandlerMapping**
处理器映射器 HandlerMapping 负责根据用户发送的请求 uri 来找到对应的controller中的方法 springmvc提供了不同的映射器实现不同的映射方式,HandlerMapping会把找到映射返回给DispatcherServlet
**Handler / controller**
后端控制器 在DispatcherServlet的控制下,Handler对具体的用户请求进行处理
**HandlerAdapter**
通过HandlerAdapter对处理器进行执行,处理器适配器
**ViewResolver**
View Resolver 负责将结果转换成view 视图
**View**
SpringMVC框架提供了很多的View视图类型的支持 包括:jstlView,freemarkerView,pdfView,我们最常用的视图就是JSP
八、处理器方法的形参
处理器方法可以包含以下4类参数,这些参数springmvc会给自动赋值,直接使用就行
直接在处理器方法里面定义形参即可
- HttpServletRequest 请求
- HttpServletResponse 响应
- HttpSession 会话
- 请求中锁携带的请求参数
@RequestMapping(value="/login.do",method=RequestMethod.POST)
//直接在这里声明 上请求对象,响应对象,session对象就行
Public ModelAndView test(HttpSession session,
HttpServletRequest request,
HttpServletResponse response){
//从请求中得到数据 getParameter()
String username = request.getParameter("username");
ModelAndView mv=new ModelAndView();
//添加到mv对象中,也就是请求作用域对象
mv.addObject("key",username);
//请求转发,到login.jsp, 这里配置了视图解析器,本来应该写 /WEB-INF/view/login.jsp的
mv.setViewName("login");
returnmv;
}
1、获取请求中携带的请求参数
1.1 逐个获取
要求:
- 处理其方法的形参名,和请求中参数的name必须一致。
- 同名的请求参数会自动赋值给同名的形参 ```java @RequestMapping(value=”/login.do”,method=RequestMethod.POST) //逐个接收,形参名必须和参数名一致才行 public ModelAndView login(String username,String password){ ModelAndView mv=new ModelAndView(); mv.addObject(“key”,username); mv.setViewName(“login”); return mv; }
- 框架内部完成了 `String username = request.getParameter("username");`
- 框架会提供类型转换的功能,能把`String`转换成`int`,`long`,`float`,`double `等类型和他的包装类型。
- 注意:当提交的是空字符串 "",或者是"abc"时,无法进行int类型转换,所以会报错400
- **状态码400:所有4xx报错都是客户端问题,400表示提交请求参数的时候,发生了问题。**
- 建议使用包装类,不要使用基本数据类型,容易出错(但还是不能转换adc这种的,"" 不会报错)
<a name="Kc1qV"></a>
### 1.2 对象接收
> (参数多的话,使用对象接收会比较好)
创建一个类,类中的属性名和参数名一致,提供get set 方法和无参构造。<br />框架自动调用无参构造创建对象,再调用set方法 设置值。<br />前端发来的参数名称是name,框架自动调用 `setName()`方法<br />形参中也可以有多个对象,比如`Student`, `School`,`Address`
<a name="puXaI"></a>
## 2、解决请求中参数和方法形参不一致
前端发来的请求,参数名是 `xingming`,`mima`<br />只能用在逐个接收的方案中
<a name="c58e7047"></a>
#### 2.1 `@RequsetParam`注解:
解决请求中参数名形参名不一致的问题<br />**属性:**<br />value:请求中参数的名称。<br />required:是要给布尔类型 [必须的]<br />true:表示这个请求中必须包含此参数。<br />false:如果没有这个参数也可以
**位置**:在处理器方法的形参定义前面
```java
@RequestMapping("/login")
public String login(@RequestParam("xingming") String username
@RequestParam("mima") String password
){
}
九、处理器方法的返回值
1、返回ModelAndView
ModelAndView mv = new ModelAndView();
- 返回数据部分
mv.addObject("key","zhangsan");
最后是放到了request作用域中 - 返回视图部分
mv.setViewName("show");
前提是SpringMvc配置了视图解析器。 - 如果页面发送的是
AJAX
请求,页面不会进行跳转,那么setViewName()
方法就显得很多余,所以这里使用ModeAndView
作为返回值就不在合适。 - 同时需要保存数据,同时需要跳转页面,这样的话就使用ModelAndView
2、返回String
表示视图部分
如果本次请求,仅仅是要做的一个页面跳转的功能,那么使用String
作为返回值最好
可以使用逻辑名称【文件名】,也可以是完整路径。
程序内部执行的是 forward() 请求 请求转发
使用逻辑名称:
return "doSome"
【配置了视图解析器,拼接后的完整路径为/WEB-INF/view/doSome.jsp
】
使用过完整视图路径:
- 项目中不能配置视图解析器
return "/WEB-INF/view/doSome.jsp"
请求转发:
return "redirect:/doSome"
; 【调转到doSome路由】
3、返回值void
既不能表示数据,也不能表示视图
在处理AJAX时,可以使用void
,通过HttpServletResponse
来输出数据,响应AJAX请求。
AJAX请求服务器端返回的就是数据,和视图无关。
当接受到ajax请求时,需要返回数据,只需要往HttpServletResponse
的print();
方法里写就行。
修改响应字符集
response.setContentType("text/html,charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("响应AJAX请求");
4、返回值Object 【处理AJAX】
返回:String
、Integer
、Map
、List
、Student
等等都是对象。
对象有属性,属性就是数据,所以返回Object表示返回数据,和视图无关
可以使用对象表示的数据,来响应AJAX请求
现在做AJAX
,主要是使用JSON
的数据格式,处理器返回Json数据实现步骤:
①、加入Json
springmvc默认使用的是jackson 【一个自动生成json的jar包】
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
②、把java对象转换成json
在springmvc配置文件中加入**<mvc:annotation-driven>**
注解驱动。这样就好了
在加入到springmvc中,会自动创建HttpMessageConverter接口,和他的7个实现类对象
③、在处理器方法的上面加入 @ResponseBody
注解
放在处理器方法的上面,通过HttpServletResponse
来输出数据,响应AJAX请求的
处理器方法返回一个Student,通过框架转为json,响应ajax请求
@ResponseBody
- 作用:把处理器方法返回的对象转换为json数据后,通过
HttpServletResponse
输出给浏览器 - 位置:在方法定义的上面
例如:处理器方法返回一个Student对象,通过框架转为json,响应ajax请求
@ResponseBody
@RequestMapping("/test")
public Student test(){
return new Student();
}
4、处理器方法返回一个String,并不是视图信息,而是数据
- 区分返回值字符是数据还是视图,看有没有@ResponseBody注解
- 如果有就是数据,如果没有就是视图
出现的问题: 返回中文的时候出现乱码问题 “???”@RequestMapping("处理地址")
@ResponseBody
public String returnString(){
return "登录失败";
} //有@ResponseBody注解,是数据,不是视图
解决方案:给@RequestMapping注解增加一个属性**@RequestMapping(value = "/returnStudentJson.do",produces = "text/html;charset=utf-8")**