文章目录
1. SpringMVC概述
SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中
SpringMVC 已经成为目前最主流的MVC框架之一,并且随着Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。它通过完善简单的一套注解,让一个简单的 Java 类成为处理请求的控制器(Servlet),而无须实现任何接口(HTTPServlet)。同时它还支持 RESTful 编程风格的请求
在我们写的Servlet中,有着很多的共有行为:获取请求数据,调用M(服务层)对数据进行分析,得到返回的响应数据,将响应数据交给V(视图)展示等都是Servlet的共有行为
同时在Servlet中又有着一些特有行为,例如校验表单数据等…
因此我们可以抽取,将共有行为抽取出来,在需要执行特有行为时再让共有行为的类去调用特有行为的类
而框架一般就会帮我们去封装这些共有行为来简化我们的开发,因此SpringMVC就帮我们实现了这些共有行为的封装(采用Servlet实现,也称为前端控制器,也是SpringMVC的核心)
简单粗暴的说,就是使用SpringMVC框架之后,我们可以不用写Servlet也能实现之前该有的效果,只需要使用SpringMVC提供的注解与创建一些POJO进行开发即可
2. SpringMVC的开发步骤
2.1 步骤讲解:
- 导入SpringMVC的核心jar包,以及Spring-web,servlet,JSP等的jar包:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- servlet坐标 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- jsp坐标 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
-
配置SpringMVC核心控制器(上面说的前端控制器)DispatcherServlet,让所有的请求都先来找这个核心控制器,例如将这个核心控制器的资源路径配置为"/",这样子所有的请求都会经过这个Servlet
或者是配置为*.xxx,使得对特定请求走这个Servlet
<!-- 在web.xml配置前端控制器 -->
<!-- DispatcherServlet类不是我们自己写的,使用xml配置 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 接着加载spring-mvc.xml进内存 -->
<!-- 表示初始化时会加载这个参数对应的值(配置文件) -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!-- 让他与tomcat一块启动 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
-
在controller包下编写Controller(POJO)类和视图页面
-
根据IOC思想,使用DI依赖注入(注解或者xml)将Controller类配置到Spring容器中(可以使用@Controller注解)
配置完的Controller类示例:
package com.ahua.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
@RequestMapping("/show")
public String show(){
System.out.println("Spring MVC 启动成功啦");
return "a.jsp";
}
}
- 如果使用注解,还需要配置组件扫描,如果是xml配置,则此时我们可以在resources目录创建一个SpringMVC的配置文件:spring-mvc.xml(分模块,与之前的spring配置文件分开)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!-- 注意,由于spring和springMVC在扫描时我们会让他们各自扫描各自的组件,因此我们这里的设置是只让他扫描controller包下的类 -->
<context:component-scan base-package="com.ahua.controller"/>
</beans>
- 接着启动服务器,在虚拟目录后增加/show即可测试效果
2.2 SpringMVC的流程图示:
图中绿色部分就是springMVC的前端控制器
2.3 SpringMVC的底层执行流程:
面试可能会问:
- 用户的请求先被发送至前端控制器DispatcherServlet,而实际上DispatcherServlet的作用主要是调用SpringMVC提供的组件,这些组件才是实现对应功能的
- DispatcherServlet收到请求调用HandlerMapping处理器映射器,HandlerMapping负责解析请求数据
- HandlerMapping处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成),返回处理器执行链HandlerExecutionChain(因为在达到Controller之前可能先经过很多个Servlet)给DispatcherServlet
- DispatcherServlet调用HandlerAdapter处理器适配器
- HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
- Controller执行完成返回ModelAndView
- HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体View
- DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户
流程图:
3. SpringMVC的分析
3.1 SpringMVC的组件分析:
1. 前端控制器:DispatcherServlet:
用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性
2. 处理器映射器:HandlerMapping
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等
3. 处理器适配器:HandlerAdapter
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
4. 处理器:Handler
它就是我们开发中要编写的具体业务控制器(Controller类)。由 DispatcherServlet 把用户请求转发到 Handler,由Handler 对具体的用户请求进行处理
5. 视图解析器:View Resolver
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户
6. 视图:View
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面
3.2 SpringMVC的注解分析:
3.2.1 @RequestMapping
作用:
用于建立请求 URL 和处理请求方法之间的对应关系
方法return “xxx.jsp"时会默认从当前资源所在地址寻找到这个jsp然后跳转,例如只配置了方法上的RequestMapping,则此时会从"http://localhost:8080/虚拟目录/” 下寻找资源,如果配置了类上的RequestMapping,例如为"/User",则此时会从"http://localhost:8080/虚拟目录/User/" 下寻找资源,此时就找不到这个jsp了
如果方法return "/xxx.jsp"时,代表会从当前web项目(webapp)下寻找这个jsp资源,此时上面找不到jsp资源的问题就解决了(可以理解为给服务器端用的地址与给客户端用的地址的区别)
位置:
- 类上,请求URL 的第一级访问目录。此处不写的话,就相当于应用的根目录(虚拟目录)
- 方法上,请求 URL 的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径
主要属性:
- value:用于指定请求的URL。它和path属性的作用是一样的
- path
- method:用于指定请求的方式,采用提供给我们的参数赋值,例如:
@RequestMapping(value="/user", method=RequestMethod.POST)
// 注意:由于使用url进行访问默认会是get,因此如果设置为post会报错
-
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样,属性值是一个数组({})
示例:
params = {“username”},表示请求参数必须有username(即必须为http…/…?username=xxx)
params = {“id!-1”},表示请求参数中id不能是-1
3.3 spring-mvc.xml中配置其他组件
视图解析器:
可以配置转发与重定向的前缀,看源码理解下先
SpringMVC有默认组件配置,默认组件都是DispatcherServlet.properties配置文件中配置的,该配置文件地址:
org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
翻看该解析器源码,可以看到该解析器的默认设置,如下:
REDIRECT_URL_PREFIX = "redirect:" // 重定向前缀
FORWARD_URL_PREFIX = "forward:" // 转发前缀(默认值)
prefix = ""; // 视图名称前缀
suffix = ""; //视图名称后缀
我们可以通过属性注入的方式修改视图的的前后缀:
<!--配置内部资源视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
在springMVC中(spring-webmvc)的web.servlet包下的DispatcherServlet.properties配置文件中配置了相关组件:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
3.4 以上总结
SpringMVC的相关组件
-
前端控制器:DispatcherServlet
-
处理器映射器:HandlerMapping
-
处理器适配器:HandlerAdapter
-
处理器:Handler
-
视图解析器:View Resolver
-
视图:View
SpringMVC的注解和配置
-
请求映射注解:@RequestMapping
-
视图解析器配置:
REDIRECT_URL_PREFIX = "redirect:"
FORWARD_URL_PREFIX = "forward:"
prefix = "";
suffix = "";
3.5 不使用注解实现SpringMVC
- 完成上述配置之后,在spring-mvc.xml中添加 处理映射器与处理器适配器的配置:
<!-- 处理器映射器: -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器: -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
- 编写我们要操作业务得到Controller类 ,该类实现Controller接口,重写handleRequest方法
- 将自己的类交给SpringIOC容器,注册bean
<!--Handler-->
<bean id="/hello" class="com.ahua.controller.HelloController"/>
Controller接口源码:
//实现该接口的类获得控制器功能
public interface Controller {
//处理请求且返回ModelAndView对象
ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}
缺点:
一个控制器中只能有一个方法,如果要多个方法则需要定义多个Controller,较麻烦
3.6 使不使用注解的配置区别:
-
使用注解时不需要实现Controller类,可以直接返回字符串
-
使用springMVC必须配置的三大件:
处理器映射器、处理器适配器、视图解析器
通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置
4. SpringMVC的请求与响应
4.1 SpringMVC的数据响应
SpringMVC的数据响应方式
分为俩种:
1) 页面跳转
-
直接返回字符串
-
通过ModelAndView对象返回
2) 回写数据(相当于response.getWriter.writer())
-
直接返回字符串
-
返回对象或集合
4.1.1 页面跳转形式
4.1.1.1 直接返回字符串
这种方式会将返回的字符串与视图解析器的前后缀拼接后跳转,即前面使用的方式,直接跳转到对应的jsp中
默认情况下为转发,此时会自动加上视图解析器配置内容
当显式的加上forward:
表示转发时,需要写上全路径
当需要重定向时需要加上"redirect:全路径.jsp"
4.1.1.2 通过ModelAndView对象返回
ModelAndView对象是SpringMVC提供的对象,ModelAndView对象返回后会被ViewReslover视图解析器解析后返回具体View,接着DispatcherServlet会根据View进行渲染视图(即将模型数据填充至视图中),最后DispatcherServlet响应用户
ModelAndView对象的获取:
1)直接创建:
ModelAndView modelAndView = new ModelAndView();
2)在方法上加上ModelAndView对象的形参,此时SpringMVC框架会为你直接注入一个ModelAndView对象
ModelAndView对象的设置:
我们可以给ModelAndView对象设置视图名称来使视图解析器找到对应的视图:
// 重定向,redirect:
modelAndView.setViewName("redirect:/WEB-INF/views/index.jsp");
// 转发,forward:可省略forward:与全路径
modelAndView.setViewName("forward:/WEB-INF/views/index.jsp");
示例:
package com.ahua.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class UserController {
@RequestMapping("/show")
public ModelAndView show(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("a.jsp");
return modelAndView;
}
}
也可以通过addObject(attributeName,attributeValue)方法来设置模型数据,同时也可以在方法参数中加上一个Model对象参数,通过Model对象的addObject(attributeName,attributeValue)方法来设置模型数据
Controller类中的方法加response形参:
由于我们可以在Controller类中的方法加形参,springMVC框架会帮我们注入,因此当我们写的Controller需要request对象或者response对象或者session对象时都可以在形参中加上HTTPServletRequest等对象,SpringMVC框架会将Tomcat创建的request对象注入到参数中
但这种方式不常用,因为我们用框架的目的就是解耦,让需要使用request对象与response对象的地方由框架帮我们实现
总结:
当需要返回数据的时候,我们一般使用ModelAndView(例如查询),当不需要返回模型数据,直接通过返回字符串跳转页面即可(增删)
4.1.2 回写数据形式
4.1.2.1 直接返回字符串
Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print(“xxx”) 即可,那么在Controller中想直接回写字符串该怎样呢?
4.1.2.1.1 response.getWriter().print
通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”) 回写数据,此时不需要视图跳转,业务方法返回值为void,并且此时直接将打印的数据展示在页面上:
@Controller
@RequestMapping("/test")
public class UserController {
@RequestMapping("/show2")
public void show2(HttpServletResponse response) throws IOException {
response.getWriter().println("Hello");
}
}
4.1.2.1.2 @ResponseBody方式
将需要回写的字符串直接在方法中返回,但此时需要通过**@ResponseBody**注解告知SpringMVC框架,方法返回的字符串不是页面跳转而是直接在http响应体中返回(效果与在页面中println一致)
@Controller
@RequestMapping("/test")
public class UserController {
@RequestMapping("/show3")
@ResponseBody
public String show3() {
return "Hello";
}
}
4.1.2.1.3 json格式字符串
在异步项目中,客户端与服务器端往往要进行json格式字符串交互,此时我们可以手动拼接json字符串返回:
@RequestMapping("/show4")
@ResponseBody
public String show4() throws IOException {
return "{\"name\":\"zhangsan\",\"age\":18}";
}
上述方式手动拼接json格式字符串的方式很麻烦,开发中往往要将复杂的java对象转换成json格式的字符串,我们可以使用json转换工具jackson进行转换,步骤:
- 导入jackson坐标
<!--jackson-->
<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>
- 通过jackson转换json格式字符串,回写字符串:
@RequestMapping("/show5")
@ResponseBody
public String show5() throws IOException {
User user = new User();
user.setUsername("zhangsan");
user.setAge(20);
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(user);
return s;
}
4.1.2.2 返回对象或集合
以上返回JSON的代码还是有一点的复杂,重复读代码影响美感,不够优雅
因此SpringMVC还提供了返回对象或者集合的方式,会自动帮助我们将对象或集合转为JSON字符串并回写
需要先在在spring-mvc.xml中为处理器适配器配置消息转换参数,告诉他使用jackson进行对象或集合的转换,覆盖默认的处理器适配器(在处理器适配器类中有一个messageConverters参数,用于接收信息的转换器集合)
先看一下其中的方法源码:
private List<HttpMessageConverter<?>> messageConverters;
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
接下来是我们需要配置的代码:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
</list>
</property>
</bean>
接着还是需要在方法上加@ResponseBody注解,表示不直接跳转而是返回数据
此时我们返回JSON数据的代码就可以写的很简单了:
@RequestMapping("/show6")
@ResponseBody
public User show6() throws IOException {
User user = new User();
user.setUsername("zhangsan");
user.setAge(20);
return user;
}
以上在配置文件中的配置较为繁琐,因此SpringMVC提供了返回对象的注解开发:
此时需要在spring-mvc.xml中配置注解驱动:
<!--mvc的注解驱动,需要先导入mvc的命名空间-->
<mvc:annotation-driven/>
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使用<mvc:annotation-driven
>自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter( 处 理 适 配 器 ),可以在spring-mvc.xml中使用<mvc:annotation-driven>替代处理器和适配器的配置
同时使用<mvc:annotation-driven>
默认底层就会集成jackson进行对象或集合到json格式字符串的转换
4.2 SpringMVC获取请求数据
4.2.1 获取请求数据参数
在客户端请求参数的格式是:name=value&name=value……
SpringMVC可以接收如下类型的参数:
基本类型参数
POJO类型参数
数组类型参数
集合类型参数
在SpringMVC中获得参数只需要在Controller中的业务方法的参数名称与请求参数的名称一致即可,SpringMVC会将参数值自动映射匹配
4.2.1.1 获取基本数据类型时:
示例:在客户端请求参数中有这样的参数:username=zhangsan&age=12
则此时要获取请求参数,只需要业务方法中的参数名称为username与age即可
@RequestMapping("/show9")
@ResponseBody
public void show9(String username,int age)
4.2.1.2 获取POJO类型参数时
假设在代码中有这样一个POJO类:
public class User {
private String username;
private int age;
// getter方法与setter方法
...
}
此时在客户端请求参数中还是这样的参数:username=zhangsan&age=12
则此时我们获取请求参数可以将业务方法中的参数写为POJO类,SpringMVC会帮我们自动注入:
@RequestMapping("/show10")
@ResponseBody
public void show10(User user)
4.2.1.3 获取数组参数(不常用)
当在前端表单中填写复选框时,就会出现使用数组或者集合接收参数的情况
示例:在客户端请求参数中有这样的参数:str=a&str=b&str=c
此时的业务方法获取参数写法:
@RequestMapping("/show11")
@ResponseBody
public void show11(String[] str)
4.2.1.4 获取集合参数
想要获取集合参数,与上面直接参数名字一致就会自动注入不同,有以下俩种方法获取:
-
将集合参数包装到一个POJO中才可以获取,这个POJO我们通常称为Vo类:
前端代码示例:
<form action="${pageContext.request.contextPath}/show12" method="post">
<!-- userList[0]表明是第几个list -->
<input type="text" name="userList[0].username"><br>
<input type="text" name="userList[0].age"><br>
<input type="text" name="userList[1].username"><br>
<input type="text" name="userList[1].age"><br>
<input type="submit" value="提交"><br>
</form>
VO代码示例:
import java.util.List;
public class VO {
private List<User> userList;
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
@Override
public String toString() {
return "VO{" +
"userList=" + userList +
'}';
}
}
此时的业务方法:
@RequestMapping("/show12")
@ResponseBody
public void show12(VO vo)
-
当使用异步Ajax通信时,我们一般使用json数据进行交互,此时我们只需要指定contentType为json形式,并且在方法形参位置使用一个注解:@RequestBody就可以实现直接接收集合数据而无需使用POJO进行包装
前端代码(使用JQ)示例:
<script>
//模拟数据
var userList = new Array();
userList.push({username: "zhangsan",age: "20"});
userList.push({username: "lisi",age: "20"});
$.ajax({
type: "POST",
url: "/itheima_springmvc1/quick13",
data: JSON.stringify(userList),
contentType : 'application/json;charset=utf-8'
});
</script>
业务方法示例:
@RequestMapping("/show13")
@ResponseBody
public void show13(@RequestBody List<User> userList)
4.2.2 开放静态资源的访问权限
注意:在上面的前端代码中我们使用了JQ,但如果直接运行上面代码会发现运行并不成功,通过抓包我们可以发现,原因是jq的文件并没有被成功加载
这是为什么呢?
这其实是因为我们在配置DispatcherServlet前端控制器时,配置了"/",表示任何请求路径都会通过这个前端控制器,DispatcherServlet会对所有资源都进行过滤,但是因此他将我们导入script文件的路径以为成是@RequestMapping配置的路径,即动态资源了,而我们并没有配置与编写相关路径的代码,因此找不到资源并报错
因此我们需要在spring-mvc.xml中进行相关配置,开放资源的访问权限(一般就是静态资源)
开放资源的访问权限有以下俩种方式:
1). 通过mvc:resources标签,其中参数mapping映射表示你访问资源时写的路径(代码中写的路径),location表示这个资源会去哪个目录找(实际目录)
<!-- 开放对js文件的访问 -->
<mvc:resources mapping="/js/**" location="/js/"/>
<!-- 开放图片的访问 -->
<mvc:resources mapping="/image/**" location="/image/"/>
2). 第二种方式相对更加简单,使用<mvc:default-servlet-handler/>标签即可, 这个标签表示当找不到对应地址(requestmapping)后会交给原始的容器(tomcat)寻找资源,即在前端控制器筛一遍之后如果找不到资源,则会通过tomcat原始的方法寻找资源
<mvc:default-servlet-handler/>
4.2.3 参数绑定注解@requestParam
上面说的SpringMVC框架自动注入参数只能在请求参数名与方法参数名一致时使用,那么名字不一致时怎么办呢?
这个时候就需要用到参数绑定注解@requestParam了,该注解作用就是当请求的参数名称与Controller的业务方法参数名称不一致时,可以通过@RequestParam注解显示的绑定方法参数对应的请求参数
其中有三个常用参数:
-
value:与请求参数名称绑定, 填入请求参数名
-
required:表示指定的请求参数在请求中是否必须包括,默认是true,则此时提交时如果没有此参数会报错
-
defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
示例:
@RequestMapping("/show14")
@ResponseBody
public void show14(@RequestParam(value="name",required = false,defaultValue = "lisi") String username) {
System.out.println(username);
}
4.2.4 解决数据乱码问题
只需要在web.xml设置一个Spring提供的过滤器来进行编码的过滤即可:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 告诉他使用哪种方式进行编码,其中encoding是CharacterEncodingFilter中的一个成员变量 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
CharacterEncodingFilter.java源码:
package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
public class CharacterEncodingFilter extends OncePerRequestFilter {
@Nullable
private String encoding;
private boolean forceRequestEncoding;
private boolean forceResponseEncoding;
public CharacterEncodingFilter() {
this.forceRequestEncoding = false;
this.forceResponseEncoding = false;
}
public CharacterEncodingFilter(String encoding) {
this(encoding, false);
}
public CharacterEncodingFilter(String encoding, boolean forceEncoding) {
this(encoding, forceEncoding, forceEncoding);
}
public CharacterEncodingFilter(String encoding, boolean forceRequestEncoding, boolean forceResponseEncoding) {
this.forceRequestEncoding = false;
this.forceResponseEncoding = false;
Assert.hasLength(encoding, "Encoding must not be empty");
this.encoding = encoding;
this.forceRequestEncoding = forceRequestEncoding;
this.forceResponseEncoding = forceResponseEncoding;
}
public void setEncoding(@Nullable String encoding) {
this.encoding = encoding;
}
@Nullable
public String getEncoding() {
return this.encoding;
}
public void setForceEncoding(boolean forceEncoding) {
this.forceRequestEncoding = forceEncoding;
this.forceResponseEncoding = forceEncoding;
}
public void setForceRequestEncoding(boolean forceRequestEncoding) {
this.forceRequestEncoding = forceRequestEncoding;
}
public boolean isForceRequestEncoding() {
return this.forceRequestEncoding;
}
public void setForceResponseEncoding(boolean forceResponseEncoding) {
this.forceResponseEncoding = forceResponseEncoding;
}
public boolean isForceResponseEncoding() {
return this.forceResponseEncoding;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String encoding = this.getEncoding();
if (encoding != null) {
if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
if (this.isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
}
4.2.5 Restful风格的参数
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用**“url+请求方式”**表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下:
-
GET:用于获取资源
-
POST:用于新建资源
-
PUT:用于更新资源
-
DELETE:用于删除资源
例如:
-
/user/1 GET : 得到 id = 1 的 user
-
/user/1 DELETE: 删除 id = 1 的 user
-
/user/1 PUT: 更新 id = 1 的 user
-
/user POST: 新增 user
也就是说写请求参数的时候不再使用?,而是通过/将请求参数拼到url中,此时的@RequestMapping注解中末尾需要加上"/{name}"来表示占位符获取url中的参数
而在方法参数中使用@PathVariable注解进行占位符的匹配获取工作, 类似于@RequestParam注解, 但不同的是用于占位符的参数匹配
示例:
@RequestMapping("/show18/{name}")
@ResponseBody
public void show18(@PathVariable(value = "name",required = true) String username){
System.out.println(username);
}
目前了解即可
4.2.6 自定义类型转换器
SpringMVC 默认已经提供了一些常用的类型转换器,例如客户端提交的数值字符串会自动转换成int型进行参数设置
但不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器
自定义类型转换器步骤:
- 定义转换器类实现Converter接口,以日期转换示例:
public class DateConverter implements Converter<String,Date>{
@Override
public Date convert(String source) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = format.parse(source);
return date;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
其中Converter需要俩个泛型,第一个是转换前数据类型,第二个是转换后数据类型
- 在配置文件spring-mvc.xml中声明转换器
<bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.ahua.converter.DateConverter"/>
</list>
</property>
</bean>
- 在spring-mvc.xml的<annotation-driven>中引用转换器
<mvc:annotation-driven conversion-service="converterService"/>
但实际上SpringMVC提供的类型转换器一般已经够用了,不太需要我们自定义类型转换器
4.2.7 获得请求头数据
4.2.7.1 @RequestHeader
在方法参数中使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name), 该参数与上面的@RequestParam同样类似,可以绑定参数
@RequestHeader注解的属性如下:
-
value:请求头的名称
-
required:是否必须携带此请求头
示例:
@RequestMapping("/show19")
@ResponseBody
public void show19(@RequestHeader(value = "Host",required = false) String headerValue){
System.out.println(headerValue);
}
4.2.7.2 @CookieValue
虽然cookie也是一种请求头,但由于比较特殊,所以还另外提供了一个注解用于获取cookie的值
在方法参数中使用@CookieValue可以获得指定Cookie的值
@CookieValue注解的属性如下:
-
value:指定cookie的名称
-
required:是否必须携带此cookie
示例:
@RequestMapping("/show20")
@ResponseBody
public void show20(@CookieValue(value = "JSESSIONID",required = false) String jsessionid){
System.out.println(jsessionid);
}
4.2.8 文件上传
上传文件其实就是客户端将文件封装发给服务器,所以其实也是一种请求数据,也可以使用SpringMVC进行获取
文件上传中客户端的三要素:
-
表单项type=“file”
-
表单的提交方式是post
-
表单的enctype属性是多部分表单形式,及enctype=“multipart/form-data”
示例:
<form action="${pageContext.request.contextPath}/show23" method="post" enctype="multipart/form-data">
名称:<input type="text" name="name"><br>
文件:<input type="file" name="file"><br>
<input type="submit" value="提交"><br>
</form>
文件上传原理:
当form表单修改为多部分表单时,request.getParameter()等方法将失效,因为这些方法都是获得URL编码方式的表单提交
例如当enctype=“application/x-www-form-urlencoded”时,form表单的正文内容格式是:
key=value&key=value&key=value
此时getParameter()等方法就可以获取数据
而当form表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成多部分形式:
SpringMVC封装了解析这些数据的小插件,我们使用这个插件就能完成文件数据的获取,因此我们需要导入fileupload与io坐标
4.2.8.1 单文件上传
步骤:
- 导入fileupload和io坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
- 配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--上传文件总大小-->
<property name="maxUploadSize" value="5242800"/>
<!--上传单个文件的大小-->
<property name="maxUploadSizePerFile" value="5242800"/>
<!--上传文件的编码类型-->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
-
编写文件上传代码
示例:
@RequestMapping("/show23")
@ResponseBody
public void show23(String name,MultipartFile uploadFile) throws IOException {
//获得文件名称
String originalFilename = uploadFile.getOriginalFilename();
//保存文件
uploadFile.transferTo(new File("C:\\upload\\"+originalFilename));
}
4.2.8.2 多文件上传
多文件上传,只需要将页面修改为多个文件上传项(即多个文件name属性一致),并将方法参数MultipartFile类型修改为MultipartFile[]即可
示例:
前端代码:
<form action="${pageContext.request.contextPath}/show24" method="post" enctype="multipart/form-data">
名称:<input type="text" name="name"><br>
文件1:<input type="file" name="uploadFiles"><br>
文件2:<input type="file" name="uploadFiles"><br>
文件3:<input type="file" name="uploadFiles"><br>
<input type="submit" value="提交"><br>
</form>
后端代码:
@RequestMapping("/show24")
@ResponseBody
public void show24(String name,MultipartFile[] uploadFiles) throws IOException {
for (MultipartFile uploadFile : uploadFiles){
String originalFilename = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("C:\\upload\\"+originalFilename));
}
}
5. 注解总结:
基础七注解
使用在类或方法上:
@Controller(“id”) : 使用在web层类上用于实例化Bean
@RequestMapping(“url”): 用于建立请求 URL 和处理请求方法之间的对应关系
@ResponseBoby : 告知SpringMVC框架,方法返回的字符串不是页面跳转而是直接在http响应体中返回
使用在方法参数中(都有value与required参数):
@RequestParam(“name”) : 与请求参数名绑定, 还有一个defaultValue参数
@PathVariable() : 与Restful风格的参数占位符绑定
@RequestHeader : 与请求头名绑定
@CookieValue : 与cookie名绑定获取cookie
6. xml总结
- 注解驱动(使用<mvc:annotation-driven>替代处理器和适配器的配置,默认会进行Jackson与对象的转换)
<!--1、mvc注解驱动-->
<mvc:annotation-driven/>
- 视图解析器:
<!--2、配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
- 静态资源权限开放
<!--3、静态资源权限开放-->
<mvc:default-servlet-handler/>
- 组件扫描(扫描注解)
<!--4、组件扫描 扫描Controller-->
<!-- 注意,由于spring和springMVC在扫描时我们会让他们各自扫描各自的组件,因此我们这里的设置是只让他扫描controller包下的类 -->
<context:component-scan base-package="com.ahua.controller"/>
- 配置自定义类型转换器
<!--声明转换器-->
<bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.ahua.converter.DateConverter"/>
</list>
</property>
</bean>
<!--引用转换器-->
<mvc:annotation-driven conversion-service="converterService"/>
- web.xml配置前端控制器
<!-- 在web.xml配置前端控制器 -->
<!-- DispatcherServlet类不是我们自己写的,使用xml配置 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 接着加载spring-mvc.xml进内存 -->
<!-- 表示初始化时会加载这个参数对应的值(配置文件) -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!-- 让他与tomcat一块启动 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- web.xml配置过滤器
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 告诉他使用哪种方式进行编码,其中encoding是CharacterEncodingFilter中的一个成员变量 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 配置处理器适配器,完成Jackson数据与对象的转换
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
</list>
</property>
</bean>
- 配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--上传文件总大小-->
<property name="maxUploadSize" value="5242800"/>
<!--上传单个文件的大小-->
<property name="maxUploadSizePerFile" value="5242800"/>
<!--上传文件的编码类型-->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
- 配置拦截器示例:
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!--配置不被拦截的资源-->
<mvc:exclude-mapping path="/user/login"/>
<bean class="com.ahua.interceptor.DemoInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
- 配置异常处理器
<!--配置简单映射异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error1"/>
<property name="exceptionMappings">
<map>
<entry key="com.ahua.exception.MyException" value="error2"/>
<entry key="java.lang.ClassCastException" value="error3"/>
</map>
</property>
</bean>