Spring MVC简介以及基本应用
一、Spring MVC前言介绍
Spring MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的 web 应用程序的组件。MVC 模式导致了应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)的分离,同时提供了在这些元素之间的松散耦合。
- 模型封装了应用程序数据,并且通常它们由 POJO 组成。
- 视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。
- 控制器主要用于处理用户请求,并且构建合适的模型并将其传递到视图呈现。
二、第一个Spring MVC案例实现
1、在SpringIOC和AOP简介以及基本使用案例里面继续添加spring-webmvc.jar
<!--spring对webmvc的支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
2、在web.xml文件中配置中央调度器(DispatcherServlet)
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
解释:查看spring-webmvc.jar可以找到DispatcherServlet类,DispatcherServlet查看继承关系他是一个Servlet,加入
1.load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
2.它的值必须是一个整数,表示servlet应该被载入的顺序。;
3.当值为0或者大于0时,表示容器在启动时就加载并初始化这个servlet。
4.当值小于0或者没有指定时,则表示容器在该Servlet被请求时,才会去加载。
5.正数的值越小,该Servlet的优先级就越高,应用启动时就优先加载。
6.当值相同的时候,容器就会自己选择优先加载。
所以,
n 中n的取值1,2,3,4,5代表的是优先级,而非启动延迟时间。通常大多数Servlet是在用户第一次请求的时候由应用服务器创建并初始化,但
n 可以用来改变这种状况,根据自己需要改变加载的优先级!
//分割线开始/
(1) 建议写为*.do 形式
在没有特殊要求的情况下,SpringMVC 的中央调度器 DispatcherServlet 的
JSP页面都不能访问。
(2) 可以写为 /
可以写为 /,因为 DispatcherServlet 会将向静态资源的获取请求,例如.css、.js、.jpg、.png 等资源的获取请求,当作是一个普通的 Controller 请求。中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误。
项目:url-pattern。在上面案例基础上进行修改。
需求:在 index.jsp 页面中存在一个访问图片的链接。该项目用于演示将写为*.do 可以访问到该图片,而写为/,则无法访问。
自行修改就行了,引用个静态资源链接就行了,不在展示繁琐过程。
修改url-pattern的值
保持的值为 *.do,扩展名方式,图片会正常显示。
将的值修改为 / ,则图片将无法显示。
静态资源访问
第一种方式:使用
在 Tomcat 中,有一个专门用于处理静态资源访问的 Servlet – DefaultServlet。其 为 default。可以处理各种静态资源访问请求。该 Servlet 注册在 Tomcat 服务 器的 web.xml 中。在 Tomcat 安装目录/conf/web.xml。
然后在只需要在 springmvc.xml 中添加
表示使用 DefaultServletHttpRequestHandler 处理器对象。 而该处理器调用了 Tomcat 的 DefaultServlet 来处理静态资源的访问请求。如果默认的 servlet 名称不是 default,需要使用属性 default-servlet-name 指定 servlet 的名称
Tomcat, Jetty 默认 Servlet 的名字 -- “default”
WebLogic 默认 Servlet 的名字 -- “FileServlet”
WebSphere 默认 Servlet 的名字 -- “SimpleFileServlet”
第二种方式:
在 Spring3.0.4 版本后,Spring 中定义了专门用于处理静态资源访问请求的处理器 ResourceHttpRequestHandler。并且添加了标签,专门用于解决静态资源无法访问问题。需要在 springmvc.xml 中添加如下形式的配置:
<mvc:resources mapping="/images/**" location="/images/"/>
location 表示静态资源所在目录。当然,如果是普通的web项目这里的目录包含/WEB-INF/目录及其子目录。 mapping 表示对该 资源 的请 求(以 /images/ 开 始 的请 求, 如 /images/qrcode.gif, /images/car.png 等)。注意,后面是两个星号**。
第一种方式需要依赖容器,所以一般情况下使用第二种。
//分割线结束/
3、配置文件位置与名称
部署运行,你会在控制台上看到异常FileNotFoundException:Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]
[demo] 2019-07-08 14:13:25.890 INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader 317 loadBeanDefinitions - Loading XML bean definitions from ServletContext resource [/WEB-INF/springmvc-servlet.xml]
[demo] 2019-07-08 14:13:25.892 ERROR org.springframework.web.servlet.FrameworkServlet 502 initServletBean - Context initialization failed org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/springmvc-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:344) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) ~[spring-web-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) ~[spring-web-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:614) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:515) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:668) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:634) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:682) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:553) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:494) [spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:171) [spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at javax.servlet.GenericServlet.init(GenericServlet.java:158) [servlet-api.jar:4.0.EDR-b01]
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1183) [catalina.jar:9.0.0.M10]
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1099) [catalina.jar:9.0.0.M10]
at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:779) [catalina.jar:9.0.0.M10]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:133) [catalina.jar:9.0.0.M10]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108) [catalina.jar:9.0.0.M10]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) [catalina.jar:9.0.0.M10]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [catalina.jar:9.0.0.M10]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [catalina.jar:9.0.0.M10]
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620) [catalina.jar:9.0.0.M10]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [catalina.jar:9.0.0.M10]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [catalina.jar:9.0.0.M10]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784) [tomcat-coyote.jar:9.0.0.M10]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-coyote.jar:9.0.0.M10]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802) [tomcat-coyote.jar:9.0.0.M10]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1452) [tomcat-coyote.jar:9.0.0.M10]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:9.0.0.M10]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_101]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_101]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:9.0.0.M10]
at java.lang.Thread.run(Thread.java:745) [?:1.8.0_101]
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]
at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:141) ~[spring-web-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:330) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
... 36 more
从 DispatcherServlet 类的父类的源码注释 FrameworkServlet 中可看到相关描述,默认要从项目根下的 WEB-INF 目录下找名称为 Servlet 名称-servlet.xml 的配置文件。这里的“Servlet 名称”指的是注册中央调度器时 标签中指定的 Servlet 的 name 值。本例配置文件名为 springmvc-servlet.xml。
而一般情况下,该配置文件是放在类路径下,即 resources目录下。所以,在注册中央调度器时,还需要为中央调度器设置查找 SpringMVC 配置文件路径,及文件名。
打开 DispatcherServlet 的源码,其继承自 FrameworkServlet,而该类中有一个属性 contextConfigLocation,用于设置 SpringMVC 配置文件的路径及文件名。该初始化参数的属性就来自于这里。
4、创建 SpringMVC 配置文件
在工程的类路径即 resources目录下创建 SpringMVC 的配置文件 springmvc.xml。该文件名可以 任意命名。而该 xml 文件的约束,则使用 Spring 配置文件最全约束。
<?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">
5、定义处理器
该处理器需要实现 Controller 接口。
package com.web.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("进入控制器");
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("welcome","Hello Spring MVC!!!");
modelAndView.setViewName("/index.jsp");
return modelAndView;
}
}
ModelAndView 类中的 addObject()方法用于向其 Model 中添加数据。Model 的底层是 HashMap。
Model 中的数据存储在 request 作用域中,SringMVC 默认采用转发的方式跳转到视图本次请求结束,模型中的数据被销毁。
6、注册处理器
在 springmvc.xml 中注册处理器。不过,需要注意处理器的 id 属性值为一个请求 URI。 表示当客户端提交该请求时,会访问 class 指定的这个处理器。
<!--
注册控制器对象
id:请求的uri,以斜杆 / 开始,表示这是控制器对象
class:指定处理类的全限定类名
-->
<bean id="/hello.do" class="com.web.controller.MyController"/>
7、定义目标页面
在 webapp 目录下新建一个 index.jsp 页面
<html>
<body>
<h2>Server data : ${requestScope.welcome}</h2>
</body>
</html>
8、修改视图解析器的注册
SpringMVC 框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器 InternalResouceViewResolver 中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳 转页面的文件名即可,对于具体的文件路径与文件扩展名,视图解析器会自动完成拼接。
<!--
注册视图解析器:帮助我们处理视图的路径和扩展名,生成视图对象
注册内部资源视图解析器InternalResourceViewResolver
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:表示视图所在的路径-->
<property name="prefix" value="/"/>
<!--后缀:表示视图的扩展名-->
<property name="suffix" value=".jsp"/>
</bean>
9、修改处理器
10、发起请求访问
三、SpringMVC 注解式开发
将上面的案例继续添加
step1:定义处理器
在类上与方法上添加相应注解即可。 @Controller:表示当前类为处理器 @RequestMapping:表示当前方法为处理器方法。该方法要对 value 属性所指定的 URI 进行处理与响应。被注解的方法的方法名可以随意。
package com.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class MyController1 {
@RequestMapping("/hello.do","/world.do")
//@RequestMapping({"/hello.do","/hello.do"}) //数组方式
public ModelAndView doSome(HttpServletRequest request, HttpServletResponse response){
System.out.println("进入控制器");
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("welcome","Hello Spring MVC!!!");
//modelAndView.setViewName("/index.jsp");
//添加视图解析器后配置如下
modelAndView.setViewName("index");
return modelAndView;
}
}
ModelAndView 类中的 addObject()方法用于向其 Model 中添加数据。Model 的底层为个 HashMap。
Model 中的数据存储在 request 作用域中,SringMVC 默认采用转发的方式跳转到视图本次请求结束,模型中的数据被销毁。
若有多个请求路径均可匹配该处理器方法的执行,则@RequestMapping 的 value 属性中 可以写上一个数组。
step2:注册组件扫描器
这里的组件即处理器,需要指定处理器所在基本包
<!--加入注解驱动-->
<mvc:annotation-driven/>
<!--组件扫描器-->
<context:component-scan base-package="com.web.controller"/>
setp3:@RequestMapping 定义请求规则
通过@RequestMapping 注解可以定义处理器对于请求的映射规则。该注解可以注解在方法上,也可以注解在类上,但意义是不同的。value 属性值常以“/”开始,也可不加“/”
(1)在类上使用@RequestMapping
@RequestMapping 的 value 属性用于定义所匹配请求的 URI。但对于注解在方法上与类 上,其 value 属性所指定的 URI,意义是不同的。
一个@Controller 所注解的类中,可以定义多个处理器方法。当然,不同的处理器方法 所匹配的 URI 是不同的。这些不同的 URI 被指定在注解于方法之上的@RequestMapping 的 value 属性中。但若这些请求具有相同的 URI 部分,则这些相同的 URI,可以被抽取到注解在 类之上的@RequestMapping 的 value 属性中。此时的这个 URI 表示模块的名称。URI 的请求 是相对于 Web 的根目录。
换个角度说,要访问处理器的指定方法,必须要在方法指定 URI 之前加上处理器类前定 义的模块名称
(2)对请求提交方式的定义
对于@RequestMapping,其有一个属性 method,用于对被注解方法所处理请求的提交 方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法。
Method 属性的取值为 RequestMethod 枚举常量。常用的为 RequestMethod.GET 与 RequestMethod.POST,分别表示提交方式的匹配规则为 GET 与 POST 提交。
一个注解默认是value给value属性赋值,value可以省略,但是加入其它属性,就必须显示的写value了,不废话,注解常识
以上处理器方法只能处理 POST 方式提交的请求。客户端浏览器常用的请求方式,及其 提交方式有以下几种:
序号 | 请求方式 | 提交方式 |
---|---|---|
1 | 表单请求 | 默认GET,可以指定POST |
2 | AJAX请求 | 默认GET,可以指定POST |
3 | 地址栏请求 | GET请求 |
4 | 超连接请求 | GET请求 |
5 | src路径求其 | GET请求 |
也就是说,只要指定了处理器方法匹配的请求提交方式为 POST,则相当于指定了请求 发送的方式:要么使用表单请求,要么使用 AJAX 请求。其它请求方式被禁用。
当然,若不指定 method 属性,则无论是 GET 还是 POST 提交方式,均可匹配。即对于 请求的提交方式无要求。
//分界线开始
@RequestMapping特殊场景用法
如果想要处理根路径请求(/),可以直接写@RequestMapping其他什么都不写,这样的用法只能有一种请求方式一个@RequestMapping其他,如果
//分界线结束
(3)对请求中携带参数的定义
@RequestMapping 中 params 属性中定义了请求中必须携带的参数的要求。以下是几种 情况的说明。 @RequestMapping(value=”/xxx.do”, params={“name”,”age”}) :要求请求中必须携带请求 参数 name 与 age @RequestMapping(value=”/xxx.do”, params={“!name”,”age”}) :要求请求中必须携带请求 参数 age,但必须不能携带参数 name @RequestMapping(value=”/xxx.do”, params={“name=zs”,”ag=23”}) :要求请求中必须携带 请求参数 name,且其值必须为 zs;必须携带参数 age,其其值必须为 23
setp3:处理器方法的参数
处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序可在方法内直接使用。
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 请求中所携带的请求参数
(1)逐个参数接收
只要保证请求参数名与该请求处理方法的参数名相同即可。
(2)使用@RequestParam,接收请求参数的key,然后赋值给你指定的key
@RequestParam("name") String name1
(3)接收表单传过来的大量参数可以创键pojo来接收参数,spring mvc可以自动为pojo赋值
(4)请求参数中文乱码问题
对于POST所接收的请求参数,若含有中文,则会出现中文乱码问题。Spring 对于请求参数中的中文乱码问题,给出了专门的字符集过滤器:spring-web-4.3.9.RELEASE.jar 的 org.springframework.web.filter 包下的 CharacterEncodingFilter 类。
(4-1)解决方案
在 web.xml 中注册字符集过滤器,即可解决 Spring 的请求参数的中文乱码问题。不过, 最好将该过滤器注册在其它过滤器之前。因为过滤器的执行是按照其注册顺序进行的。
<!--注册字符集过滤器:解决post请求乱发问题-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<!--spring-web.jar-->
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--指定字符集-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--强制request使用字符集encoding-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制response使用字符集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>
查看CharacterEncodingFilter里面有三个set方法
setp3:处理器方法的返回值
使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型:
- 第一种:ModelAndView
- 第二种:String
- 第三种:无返回值 void
- 第四种:返回自定义类型对象
(1)返回 ModelAndView
若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时 处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象。
在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。
(2)返回 String
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址
返回内部资源逻辑视图名
若要跳转的资源为内部资源,则视图解析器可以使用 InternalResourceViewResolver 内部资源视图解析器。此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分。这个字符串与视图解析器中的 prefix、suffix 相结合,即可形成要访问的 URI。 视图解析器上面配置过。
返回 void
对于处理器方法返回 void 的应用场景,AJAX 响应.
若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。
例如,对于 AJAX 的异步请求的响应。
返回对象
处理器方法也可以返回 Object 对象。这个 Object 可以是 Intger,String,自定义对象, Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
加入@ResponseBody
@RequestMapping的produces = “text/html;charset=utf-8”用于设置输出结果类型
返回自定义类型对象
返回自定义类型对象时,不能以对象的形式直接返回给客户端浏览器,而是将对象转换为 JSON 格式的数据发送给浏览器的。 由于转换器底层使用了Jackson转换方式将对象转换为 JSON数据,所以需要导入Jackson 的相关 Jar 包
四、SpringMVC重要概念
1、请求重定向和转发
当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。
注意,对于请求转发的页面,可以是WEB-INF中页面;而重定向的页面,是不能为WEB-INF 中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。
SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。 、forward:表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()
redirect: 表示重定向,实现 response.sendRedirect(“xxx.jsp”) forword 和 redirect 根据他们的目标,可以分为页面和其他处理器。
请求转发
处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:,且 此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图视图页面必须写出相对于项目根的路径。forward 操作不需要视图解析器。 处理器方法返回 String,在视图路径前面加入 forward: 转发到视图页面。
转发到其他处理器的格式:forward:xxx.do
指定视图的时候不会和视图解析器一起工作。如:
请求重定向
在处理器方法返回的视图字符串的前面添加 redirect:,则可实现重定向跳转。
当重定向到目标资源时,若需要向下传递参数值,除了可以直接通过请求 URL 携带参 数,通过 HttpSession 携带参数。
2、异常处理
常用的 SpringMVC 异常处理方式主要有三种:
- 使用系统定义好的异常处理器 SimpleMappingExceptionResolver
- 使用自定义异常处理器
- 使用异常处理注解
SimpleMappingExceptionResolver 异常处理器
该方式只需要在 SpringMVC 配置文件中注册该异常处理器 Bean 即可。该 Bean 比较特 殊,没有 id 属性,无需显式调用或被注入给其它,当异常发生时会自动执行该类。
自定义异常类
定义三个异常类:NameException、AgeException、StudentException。其中 StudentException 是另外两个异常的父类,在你的控制器里面把这些自定义异常抛出(throw new xxxExcetptin();)就行了。
- exceptionMappings:Properties 类型属性,用于指定具体的不同类型的异常所对应的异常响应页面。Key 为异常类的全限定性类名,value 则为响应页面路径
- defaultErrorView:指定默认的异常响应页面。若发生的异常不是 exceptionMappings 中指定的异常,则使用默认异常响应页面。
- exceptionAttribute:捕获到的异常对象。一般异常响应页面中使用。
自定义异常处理器
使用 SpringMVC 定义好的 SimpleMappingExceptionResolver 异常处理器,可以实现发生 指定异常后的跳转。但若要实现在捕获到指定异常时,执行一些操作的目的,它是完成不了的。此时,就需要自定义异常处理器。
自定义异常处理器,需要实现HandlerExceptionResolver 接口,并且该类需要在 SpringMVC 配置文件中进行注册。
step1:定义异常处理器
当一个类实现了 HandlerExceptionResolver 接口后,只要有异常发生,无论什么异常, 都会自动执行接口方法 resolveException()。
package com.web.exception;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("ex",ex);
//设置默认错误响应页面
modelAndView.setViewName("/error/defaultErros.jsp");
//设置NameException响应页面
if(ex instanceof NameException){
modelAndView.setViewName("/error/NameError.jsp");
}
//设置AgeException响应页面
if(ex instanceof AgeException){
modelAndView.setViewName("/error/AgeError.jsp");
}
return modelAndView;
}
}
setp2:注册异常处理器
<!--注册异常处理器-->
<bean class="com.web.exception.MyExceptionResolver"/>
异常处理注解
使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。
而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意,方法参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。
对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中,不过,一般不这样使用。而是将异常处理方法专门定义在一个类中,作为全局的异常处理类。
需要使用注解@ControllerAdvice , 字面理解就是“控制器增强” ,是给控制器对象 增强功能的。使用@ControllerAdvice 修饰的类中可以使用@ExceptionHandler
当使用@RequestMapping 注解修饰的方法抛出异常时,会执行@ControllerAdvice 修饰的 类中的异常处理方法。
@ControllerAdvice 是使用@Component 注解修饰的,可以扫描到@ControllerAdvice 所在的类路径(包名),创建对象。
setp1:创键全局异常处理类
package com.web.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**
*全局异常处理类
* @ControllerAdvice 控制器增强注解
* 是给控制器对象增强的,用来做全局的异常处理使用
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//处理nameException的方法
@ExceptionHandler(NameException.class)
public ModelAndView nameExceptionHandler(Exception ex){
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("ex",ex);
modelAndView.setViewName("/error/nameError.jps");
return modelAndView;
}
//指定默认异常处方法
@ExceptionHandler
public ModelAndView ageExceptionHandler(Exception ex){
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("ex",ex);
modelAndView.setViewName("/error/nameError.jps");
return modelAndView;
}
}
setp2:定义 Spring 配置文件
然后扫描该类所在的包,加入注解驱动就行了。
3、拦截器
SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器, 在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时, 已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。
一个拦截器的执行
自定义拦截器:
package com.web.interceptor;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行MyInterceptor--------preHandle()--------------");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行MyInterceptor--------postHandle()--------------");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("执行MyInterceptor--------afterCompletion()--------------");
}
}
自定义拦截器,需要实现 HandlerInterceptor 接口。而该接口中含有三个方法:
- preHandle(request, response, Object handler):该方法在处理器方法执行之前执行。其返回值为 boolean,若为true,则紧接着会执行处理器方法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行。
- postHandle(request, response, Object handler, modelAndView): 该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。 由于该方法是在处理器方法执行完后执行,且该方法参数中包含ModelAndView,所以该方法可以修 改处理器方法的处理结果数据,且可以修改跳转方向。
- afterCompletion(request, response, Object handler, Exception ex): 当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事。
拦截器中方法与处理器方法的执行顺序如下图:
换一种一表现方式,也可以这样理解:
注册拦截器 :
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.web.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
用于指定当前所注册的拦截器可以拦截的请求路径,而/**表示拦截所 有请求。
多个拦截器
当有多个拦截器时,形成拦截器链。拦截器链的执行顺序,与其注册顺序一致。需要再次强调一点的是,当某一个拦截器的 preHandle()方法返回 true 并被执行到时,会向一个专门的方法栈中放入该拦截器的 afterCompletion()方法。
多个拦截器中方法与处理器方法的执行顺序如下图:
从图中可以看出,只要有一个 preHandle()方法返回 false,则上部的执行链将被断开, 其后续的处理器方法与 postHandle()方法将无法执行。但无论执行链执行情况怎样,只要方法栈中有方法,即执行链中只要有 preHandle()方法返回 true,就会执行方法栈中的afterCompletion()方法。最终都会给出响应。
换一种一表现方式,也可以这样理解:
查看中央调度器 DispatcherServlet 的 doDispatch()方法源码:在执行处理器方法之前, 会执行处理器执行链对象 mappedHandler 的 applyPreHandle()方法。然后执行 Handler,最后 执行处理器执行链对象的 applyPostHandle()方法。
applyPreHandle()方法用于执行处理器执行链中的所有拦截器的 preHandle()方法。 applyPreHandle()方法的返回结果取决于执行链中的每一个拦截器的 preHandle()方法。只要 有一个 preHandle()方法返回 false,则其就会返回 false。然后就执行了 return;即结束doDispatch()方法,即该请求的处理结束。
对于处理器执行链的 applyPostHandle()方法,其是循环倒序执行所有拦截器的 postHandle()方法的,所有才会出现上面流程图所画倒序的样子。
afterCompletion()方法执行时期
在刚才的处理器执行链的 applyPreHandle()方法中看到,若存在任一个拦截器的 preHandle()方法返回 false,则会调用执行处理器执行链的 triggerAfterCompletion()方法,会触发所有 afterCompletion()方法的执行。
在 doDispatch()方法中也存在一个 catch(){}语句,表示若发生异常,则会调用执行 triggerAfterCompletion()方法。
但在正常情况下,即所有的 preHandle()方法返回均为 true,且 doDispatch()方法没有异 常发生的情况下,afterCompletion()方法是在视图解析器后执行的。
查看中央调度器 DispatcherServlet 的 processDispatchResult()方法源码可知,在对视图渲 染过后,会调用执行处理器执行 链的 triggerAfterCompletion() 方法,即执行所有的 afterCompletion()方法。
打开处理器执行链的 triggerAfterCompletion()方 法,可以看到, 其对拦截器的 afterCompletion()方法的执行,也是循环倒序执行的。
还没有评论,来说两句吧...