SpringMVC (1)
一、SpringMVC介绍
Spring为展现层提供的基于MVC设计理念的web框架,是目前最主流的MVC框架之一
二、SpringMVC的实现思想
三、SpringMVC的使用流程
- 创建工程
- 向WEB-INF文件夹中的lib目录导包(右键Build Path)
- 编辑WEB-INF文件夹中的web.xml文件内容
SpringMVC有一个前端控制器,能拦截所有请求,并智能派发,这个前端控制器是一个Servlet 程序,在web.xml中配置 (代码可使用ATL + /提示,不要打尖括号)
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- contextConfigLocation:指定SpringMVC配置文件的位置;
配置文件就是src下右键new Spring Bean Configuration File,
与之前Spring的ioc容器配置文件创建方式类似
-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--
load-on-startup:服务器启动的时候创建对象;值越小优先级越高,越先创建对象;
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!--
url - pattern标签的用法与Filter过滤器中的url - pattern标签用法一致,注:
/*和/都是拦截所有请求;
/:会拦截所有请求,但是不会拦截*.jsp,能保证jsp访问正常;
/*的范围更大,会拦截到*.jsp这些请求,一但拦截jsp页面就不能显示了;
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
- WebContent目录下创建一个index.jsp页面
<a href="hello">helloworld</a> //相对路径,即访问地址为/hello,“/”表示工程路径
//点击连接之后应当向处理器发送请求,然后处理器返回资源,故需要配置处理器
- src目录下创建一个包,包下创建一个类,表示处理器
/**
* 告诉SpringMVC这是一个处理器,用来处理请求
* @Controller:标识哪个组件是处理器
*/
@Controller
public class MyFirstController {
/**
* @RequestMapping:告诉SpringMVC这个方法(任意编写)处理什么请求;
* “/” 代表从WebContent开始,处理当前项目下的hello请求
* (相当于之前JavaWeb所学的配置Servlet程序的url - pattern标签中的内容,即输入指定地址才可以访问到此方法)
*/
@RequestMapping("/hello")
public String myfirstRequest(){
//处理器返回对应目录下的success.jsp页面
return "/WEB-INF/pages/success.jsp";
// “/” 表示工程路径,对应WebContent目录
}
}
- 在右键new Spring Configuration File创建的SpringMVC.xml配置文件中添加
<!-- 扫描所有组件 -->
<context:component-scan base-package="com.qizegao"></context:component-scan>
- 在WEB-INF/pages目录下创建success.jsp文件,内容如下:
<h1>成功!</h1>
- 运行结果
点击helloworld出现成功
- 处理器的return语句返回资源的简化写法
使用视图解析器拼串,在SpringMVC.xml配置文件中添加
<!-- 配置一个视图解析器,能拼接页面地址-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
- 修改处理器中的方法(更简便)
@RequestMapping("/hello")
public String myfirstRequest(){
/*视图解析器自动拼串:
(前缀)/WEB-INF/pages/+返回值(success)+后缀(.jsp) */
return "success";
}
注:
-
一个方法处理一个请求,同一个请求不能让多个方法处理
-
如果web.xml中的前端控制器配置信息中没有使用init - param标签指定SpringMVC的配置文件 所在位置时会默认去找一个文件:
/WEB - INF/[servlet - name标签中的值] - servlet.xml
这个文件与web.xml在相同等级的目录下
四、@RequestMapping注解的详细介绍
- @RequestMapping注解使用在类上表示为此类中所有方法的请求路径添加了一个基准,如:
@RequestMapping("/haha")
@Controller
public class MyFirstController {
@RequestMapping("/hello")
public String myfirstRequest(){
//方法体
}
}
//表示请求地址为/haha/hello才可访问到此方法,若只使用/hello访问不到此方法
- @RequestMapping注解的其他属性
(1) 请求地址是value属性值
(2) method属性
限定请求方式,只有请求使用的是method属性限定的方式才可以访问到此方法
用法:
(3) params属性
String[]类型,其值如下:
/**
* (1) param: 表示请求必须包含名为 param 的请求参数
* eg:params={"username"}
* 发送请求的时候必须带上一个名为username的参数,才可以执行此方法
*
* (2) !param: 表示请求不能包含名为 param 的请求参数
* eg: params={"!username"}
* 发送的请求不能有名为username的参数,才可以执行此方法
*
* (3) param != value: 表示请求中名为param的参数其值不可以是value
* eg:params={"username!=123"}
* 发送的请求携带的username值不可以是123
(不带username或者username不是123,不带username表示username值为null,username=(什么都不写)表示带了username但值为空串),才可以执行此方法
*
* (4) {“param1=value1”, “param2”}:
* 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1
* eg:params={"username!=123","pwd","!age"}
* 请求中username不能是123,必须有名为pwd的参数,不能有名为age的参数
*/
(4) headers属性
规定请求头,用法:headers={“xxx”},请求头满足此属性才可以执行此方法
(5) consumes属性
只接受内容为指定类型的请求,相当于规定请求头中的Content - type
(6) produces属性
指定响应内容的类型,相当于规定响应头的Content - type
- @RequestMapping注解的模糊匹配功能
? 代替任意一个字符
* 代替任意多个字符或一层路径
** 代替多层路径
用法:
@RequestMapping("/antTest0?")
@RequestMapping("/a/**/antTest01")
注意:当某一请求符合多个情况时,越精确的越优先
五、@PathVariable注解的使用
@RequestMapping注解的路径上可以使用占位符,用法:将某一层路径写为{变量名},如:
@RequestMapping("/user/{id}") 表示请求地址为/user/xxx访问到的是此方法
@PathVariable注解就是获取这个占位符的值,用法:
@RequestMapping("/user/{id}")
public String pathVariableTest(@PathVariable("id")String id){
System.out.println("路径上的占位符的值"+id);
return "xxx";
}
//当浏览器的地址为/user/abc时,输出:路径上的占位符的值abc
注意:占位符只可以占一层路径
六、Resultful风格
用请求方式来区分对一个资源是CRUD中的哪一种
如果是GET请求方式-----查询
如果是PUT请求方式------更新
如果是DELETE请求方式-----删除
如果是POST请求方式-----添加
但是从页面上只可以发起GET、POST两种请求,故应当使用如下步骤来发送DELETE 和PUT请求:
- SpringMVC中有一个Filter,它可以把普通的请求转化成为规定形式的请求
在web.xml中配置这个Filter
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<!-- 拦截所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
-
所要发起请求的页面中创建一个method = post类型的form表单,表单项中携带一个名为 _method的参数,这个参数的值是DELETE或PUT
-
代码示例
(1) index.jsp页面
<!-- 发送GET请求 -->
<form action="book/1" method="get">
<input type="submit" value="查询"/>
</form><br/>
<!-- 发送POST请求 -->
<form action="book/1" method="post">
<input type="submit" value="添加"/>
</form><br/>
<!-- 发送DELETE请求 -->
<form action="book/1" method="post">
<input name="_method" value="delete"/>
<input type="submit" value="删除"/>
</form><br/>
<!-- 发送PUT请求 -->
<form action="book/1" method="post">
<input name="_method" value="put"/>
<input type="submit" value="更新"/>
</form><br/>
(2) 处理器
@Controller
public class BookController {
//处理查询请求
@RequestMapping(value="/book/{bid}",method=RequestMethod.GET)
public String getBook(@PathVariable("bid")Integer id) {
System.out.println("查询到了"+id+"号图书");
return "xxx";
}
//处理删除请求
@RequestMapping(value="/book/{bid}",method=RequestMethod.DELETE)
public String deleteBook(@PathVariable("bid")Integer id) {
System.out.println("删除了"+id+"号图书");
return "xxx";
}
//处理更新请求
@RequestMapping(value="/book/{bid}",method=RequestMethod.PUT)
public String updateBook(@PathVariable("bid")Integer id) {
System.out.println("更新了"+id+"号图书");
return "xxx";
}
//处理添加请求
@RequestMapping(value="/book",method=RequestMethod.POST)
public String addBook() {
System.out.println("添加了新的图书");
return "xxx";
}
}
以上请求都可以调用到对应的方法,以上请求的地址都是一样的,但功能各不相同
注意:高版本的Tomcat服务器(如8.0)对Restful支持有问题;
使用了DELETE和PUT两种请求会出现以下错误:
解决方案:
jsp页面首部添加:
表示此页面可能出现异常,出了异常之后封装到九大对象中的Exception中,不报异常
SpringMVC (2)
一、获取请求参数
-
默认情况下在方法的参数中声明一个和请求参数同名的变量,这个变量可以自动获取对应的请 求参数的值,如果请求中无此参数,则值为null
-
@RequestParam注解
此注解用来获取请求参数的值,默认情况下注解中声明的参数请求中必须携带,否则报错
方法中的注解表示:String username = request.getParameter(“user”);
注解中的属性:
(1) value:指定要获取的参数名
(2) required:boolean类型,用于表示这个参数请求中是否必须携带,默认是true,修改为 false则请求中无此参数不报错
(3) defaultValue:修改请求参数的默认值,默认是null
注意:
@RequestParam获取的是请求参数的值 (地址中?之后的内容)
@PathVariable获取的是请求路径的值 (地址中?之前的内容)
- @RequestHeader注解
此注解用来获取请求头中某个key的值,与@RequestParam注解的用法一致
方法中的注解表示:String userAgent = request.getHeader(“User - Agent”)
- @CookieValue注解
此注解用来获取请求中某个cookie的值,与@RequestParam注解的用法一致
二、SpringMVC自动封装自定义类的对象
要求请求中的参数名与对象中的属性名要对应
index.jsp页面中声明了:
声明处理器
@RequestMapping("/book")
public String addBook(Book book){
System.out.println("我要保存的图书:"+book);
return "success";
}
Book类和Address类
运行结果:成功将表单中的数据封装到book对象中
三、处理器中使用原生API
使用了@RequestMapping注解的方法可以像使用Servlet程序中的doGet或doPost方法一样在形 参中使用HttpSession、HttpServletRequest、HttpServletResponse等类型的参数,用法及作用与doGet 或doPost方法的一致
四、解决乱码问题
- 响应乱码
response.setContentType("text/html;charset=utf-8");
- 请求乱码
(1) GET请求:在服务器的server.xml文件中修改,如图:
(2) POST请求:
在第一次获取请求参数之前设置
request.setCharacterEncoding("UTF-8");
但SpringMVC使用注解,一般不使用request,故使用Filter解决乱码问题:
web.xml中配置:
注意:
1. 为了解决乱码问题:
(1) 配置完成前端控制器之后随之配置字符编码过滤器
(2) 安装完Tomcat服务器之后,随之修改server.xml文件中的编码格式
2. 解决乱码问题的Filter一定要在其余Filter之前配置
五、将数据保存到request域中
除了使用传统的request、session对象外,
还可以在方法处使用Map、Model、ModelMap类型的对象,如下:
@RequestMapping("/handle01")
public String handle01(Map<String, Object> map) {
map.put("msg", "value"); //将数据保存到request域中
return "xxx";
}
@RequestMapping("/handle02")
public String handle02(Model model) {
model.addAttribute("msg", "value"); //将数据保存到request域中
return "xxx";
}
@RequestMapping("/handle03")
public String handle03(ModelMap modelMap) {
modelMap.addAttribute("msg", "value"); //将数据保存到request域中
return "xxx";
}
三者之间的关系:
三者getClass( )得到的都是BindingAwareModelMap类型,相当于给BindingAwareModelMap中 保存的数据都会被放在request域中
三者之间的继承关系如图所示:
六、处理器的方法返回值为ModelAndView类型
Model指的是保存在request域中的数据,View指的是要跳转的页面
用法一:
@RequestMapping("/handle04")
public ModelAndView handle04(){
//创建对象时构造器中传入要跳转的页面地址(视图解析器会自动拼串)
ModelAndView mv = new ModelAndView("success");
mv.addObject("msg", "value"); //将数据保存到request域中
return mv;
//访问到此方法时会自动跳转到success页面,且request域中保存了数据
}
用法二:
@RequestMapping("/handle04")
public ModelAndView handle04(){
ModelAndView mv = new ModelAndView(); //调用空参构造器
mv.setViewName("success"); //要跳转的页面
mv.addObject("key", "value"); //将数据保存到request域中
return mv;
//访问到此方法时会自动跳转到success页面,且request域中保存了数据
}
注意:
给Session域中保存数据使用给方法中传入原生的HttpSession对象的方式,虽然可以使用 @SessionAttributes注解,但容易出异常
七、跳转的资源添加forward / redirect前缀
用法一:
用法二:
用法三:
八、Jackson的使用
-
Jackson是JSON的解析工具
-
使用步骤
(1) 导入jar包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
(2) 创建ObjectMapper对象进行转换
ObjectMapper mapper = new ObjectMapper();
//User类中有三个属性
User user = new User(1, "秦疆一号", 12);
String str = mapper.writeValueAsString(user);
//str成为对应的JSON字符串
- 解决JSON字符串乱码的问题
在SpringMVC的xml配置文件加上如下代码
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
九、其余注解的介绍
- @RequestController
代替@Controller使用在类上表示此类中所有的方法返回的String值不作为页面与视图解析器拼 串,而仅仅是String型的数据
- @ResponseBody
使用在方法上表示此方法的String型返回值不作为页面与视图解析器拼串,仅是String型数据
注意:此注解应当在类没有使用@RequestController时使用
十、文件上传
- 单文件上传
(1) form标签的method属性值为post
(2) form标签的enctype = “multipart / form - data”
(3) 导入两个jar包commons - fileupload - 1.2.1.jar、commons - io - 2.0.jar
(4) 在SpringMVC的配置文件中配置文件上传解析器
(5) 配置处理器,处理上传的文件
- 代码演示
(1) index.jsp中编写
<form method="post" enctype="multipart/form-data" action="/upload">
用户头像:<input type = "file" name = "headerimg"/> <br/>
用户名:<input type = "text" name = "username"/> <br/>
<input type = "submit"/>
</form>
(2) springmvc.xml中配置文件上传解析器
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 限制文件大小为20MB -->
<property name="maxUploadSize" value="#{1024 * 1024 * 20}"></property>
<!-- 设置默认编码格式 -->
<property name="defaultEncoding" value="utf-8"></property>
</bean>
(3) 配置处理器
@Controller
public class FileUploadController {
//MultipartFile封装了当前文件的信息
public String upload(String username, Model model, @RequestParam("headerimg") MultipartFile file) {
//transferTo将文件下载到磁盘中
//getOriginalFilename得到文件的名称
try {
//上传正常的情况
file.transferTo(new File("G:\\haha\\" + file.getOriginalFilename()));
model.addAttribute("msg", "文件上传成功!");
}catch (IOException e) {
//上传出错的情况
model.addAttribute("msg", "文件上传失败!" + e.getMessage());
}
return "forward:/index.jsp";
}
}
- 多文件上传代码演示
@Controller
public class FileUploadController {
//多个文件时使用数组MultipartFile[],每个元素对应一个文件
public String upload(@RequestParam("headerimg") MultipartFile[] file) {
for (MultipartFile multipartFile : file) {
try {
//上传正常的情况
}catch (Exception e) {
//上传出错的情况
}
}
return "forward:/index.jsp";
}
}
十一、SpringMVC的拦截器
SpringMVC提供了拦截器HandlerInterceptor,允许目标方法执行之前进行一些拦截工作,或者目标 方法执行之后进行一些其他处理
HandlerInterceptor是一个接口,其中的方法:
preHandle:目标方法执行之前自动调用;返回true,允许执行目标方法,返回false,不允许执 行目标方法
postHandle:目标方法执行之后自动调用
afterCompletion:整个请求过程完成,来到目标页面之后自动调用
- 单拦截器的运行流程
(1) index.jsp页面中编写
<a href="/test01">test01</a>
(2) 配置处理器
@Controller
public class InterceptorTestController {
@RequestMapping("/test01")
public String test01(){
System.out.println("test01....");
return "success";
}
}
(3) 创建拦截器
public class MyFirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyFirstInterceptor...preHandle...");
return true; //允许执行目标方法
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyFirstInterceptor...postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyFirstInterceptor...afterCompletion");
}
}
(4) springmvc.xml中配置拦截器
<mvc:interceptors>
<!-- 配置某个拦截器,默认是拦截所有请求的 -->
<!-- <bean class="com.atguigu.controller.MyFirstInterceptor"></bean> -->
<!-- 配置某个拦截器的更详细信息 -->
<mvc:interceptor>
<!-- 只拦截test01请求 -->
<mvc:mapping path="/test01"/>
<bean class="com.atguigu.controller.MyFirstInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
运行结果:
注意:
(1) 如果preHandle返回false,就不会有以上后续的流程
(2) 一旦preHandle返回true,中间无论哪个流程出异常,一定会执行afterCompletion方法
- 多个拦截器的运行流程
(1)
(2) 由上图可知preHandle正序,postHandle、afterCompletion逆序
(3) 一旦有一个拦截器的preHandle方法返回false,则整个拦截系统(不仅仅是返回false的那一 个拦截器)以后的流程都不会执行
(4) 某个拦截器的preHandle返回true,无论其余拦截器是否出异常,此拦截器的afterCompletion 一定会执行
十二、异常处理
- @ExceptionHandler注解的使用
在处理器的类的某一方法上使用这个注解表示这个方法用来处理这个类发生的异常
//注解中写上用来处理哪一种异常,多个用逗号隔开
@ExceptionHandler(value = {
NullPointerException.class })
//方法中参数的异常对象用来接收发生的异常
public ModelAndView handleException01(Exception exception) {
//myerror是自定义的jsp页面(展示错误信息)
ModelAndView view = new ModelAndView("myerror");
view.addObject("ex", exception);
return view; //出异常之后会跳转到自定义页面,而不是servlet的错误页面
}
//第二个处理异常的方法
@ExceptionHandler(value = {
Exception.class })
public ModelAndView handleException02(Exception exception) {
//执行其中的方法
}
//注:当类中有多个方法处理异常时,异常类型越精确的越优先
- @ExceptionHandler注解全局处理异常
/**
* 1. 集中处理所有异常的类需要使用@ControllerAdvice注解
* 2. 此类可以处理所有处理器出现的异常
*/
@ControllerAdvice
public class MyException {
@ExceptionHandler(value={
ArithmeticException.class})
public ModelAndView handleException01(Exception exception){
ModelAndView view = new ModelAndView("myerror");
view.addObject("ex", exception);
return view;
}
}
//注:全局处理异常和本类处理异常同时存在时,本类优先
- @ResponseStatus注解的使用
此注解使用在自定义异常类上,可以让出现此异常的请求跳转至自定义的错误页面
(1) 创建一个自定义异常类
//value属性指定状态码
@ResponseStatus(reason="用户被拒绝登陆",value=HttpStatus.NOT_ACCEPTABLE)
public class UserNameNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
(2) 创建一个处理器的方法
@RequestMapping("/handle02")
public String handle02(@RequestParam("username") String username) {
if (!"admin".equals(username)) {
throw new UserNameNotFoundException();
//如果出现异常会跳转至此异常指定的页面
}
return "success";
}
出现异常跳转至如下页面:
- 通过配置的方式进行异常处理
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- exceptionMappings:配置哪些异常去哪些页面 -->
<property name="exceptionMappings">
<props>
<!-- key:异常全类名;value:要去的页面 -->
<prop key="java.lang.NullPointerException">myerror</prop>
</props>
</property>
<!-- exceptionAttribute指定封装错误信息的key,此key可以被错误页面获取从而显示错误信息
value值就是封装错误信息的key
-->
<property name="exceptionAttribute" value="ex"></property>
</bean>