文章目录
介绍
前面的文章介绍了Spring MVC的核心原理,就是抽象出请求处理流程中的公共的(或者说重复的)步骤,形成各种接口组件(分为三大类:模型层、视图层、控制器层),然后将它们串联起来;业务开发人员可以根据需求灵活配置具体的实现组件,插入到该流程中。
Spring MVC的基础是Servlet技术和Spring IoC技术。使用Servlet技术拦截请求,实现前端控制器(即调度器、分派器)DispatcherServlet;使用Spring IoC技术配置和管理请求处理流程中需要的各个具体的实现组件,实现组件之间的解耦。
前面的文章中我们也已经将Spring IoC整合到Spring MVC中了,剩下的工作就需要编写我们的控制器来接收请求,并调用模型层组件来处理请求。
Spring MVC虽然也提供了编程的方式来实现控制器,但最经常用的还是基于注解的方式,本篇文章就先粗略的介绍如何使用基于注解的方式来实现我们的控制器,至于控制器所包含的每一个方面的细节以后用到再详细讲解。
根据Spring MVC的核心原理,控制器需要解决以下几个基本问题:
- 要指定什么样的请求分派给哪一个Handler来处理;
- 将请求中携带的数据转换成业务模型对象;
- 将请求的处理结果以什么样的形式传递给视图;
- 请求处理过程中出现异常怎么办。
这几个问题都可以使用Spring MVC提供的注解实现,我们仍然以之前建立的工程spring-mvc-test来演示说明。
控制器的声明
控制器的声明很简单,使用@Controller
这个注解来修饰要作为控制器的类即可,前面的文章已经介绍过。它和@Service
、@Repository
、@Component
一样,Spring IoC会自动生成这几个注解所修饰类的Bean,@Controller
这个注解表明该Bean是一个web层(控制器层)组件。
比如,在之前的演示工程中,我声明了两个控制器,其中一个如下:
@Controller
public class HelloController {
//省略其他内容
}
当然,这需要在Spring IoC容器开启自动扫描才会有效,有两种方式,基于XML的方式是:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="test.appa"/>
</beans>
基于Java配置的方式是:
@Configuration
@ComponentScan({ "test.appa" })
public class AppAConfig {
}
实际上,我们也可以不用此注解,而直接在Spring IoC的配置元数据中定义该Bean,这就足够了。所以,控制器的声明主要目的是要能够生成作为控制器的Bean。当然,使用注解的方式更加简练且清晰。
Spring MVC还提供了@RestController
注解,它也可以声明控制器,不过该控制器有点特殊,其内部所有Handler的返回结果都将直接填充到响应体中。实际上该注解是一个复合注解,可以看看它的定义:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(annotation = Controller.class)
String value() default "";
}
可见它是由@Controller
和@ResponseBody
组合而成的,而其特殊性就是由@ResponseBody
这个注解所带来的,后面会继续介绍。
需要注意的是,作为控制器的类也可以继承其他类,或实现其他接口,还有一些场景中我们需要在运行时使用AOP代理(属于Spring AOP的内容,以后会介绍)来装饰该控制器类,比如使用@Transactional
注解修饰该控制器类,这时候还需要一些特殊配置。
通常情况下,我们尽量要让控制器类干净一点,就是避免继承其他类,或实现其他接口,或使用基于AOP的注解来修饰。
Handler的定义和请求映射
仅仅是声明控制器其实并没有什么作用,只是要告诉Spring IoC容器生成一个Bean而已,甚至还没有涉及到Spring MVC的内容。
Spring MVC的DispatcherServlet
拦截到某个请求(根据servlet-mapping
的配置)之后,应该交给Spring MVC的Handler来处理,所以我们需要定义Handler,并指定什么样的请求才由该Handler来处理,这个问题我们统称为请求映射问题。
一个Handler就对应控制器类中的一个方法,当然我们需要以某种方式告诉Spring MVC该方法是一个Handler,该Handler能够处理什么样的请求。Spring MVC提供了以下注解来解决这个问题:
@RequestMapping
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
这些注解的功能都是解决请求映射问题的,其中,除@RequestMapping
之外,剩下的几个注解也是复合注解,它们都使用了@RequestMapping
来定义的,比如@GetMapping
的定义如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
//省略
}
它指定了HTTP请求的方法必须是GET方法,其他注解也是类似。
那么它们是如何使用的呢?很简单,只要用它们修饰控制器的方法上即可,同时,@RequestMapping
注解还可以修饰类(从定义中的@Target
的属性值是METHOD
和TYPE
可以看出),我们可以看一下它的定义:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
可以看出,它有多个属性:
name
:给映射赋予一个名字,不常用;value
(等效于path):配置HTTP请求的URL模式,如path="/xxx/yyy/{id}"
,可以使用通配符,路径参数、正则表达式、占位符形式的外部配置参数;method
:配置HTTP请求的方法,如method=RequestMethod.GET
;params
:配置HTTP请求所携带的参数必须满足的条件,如params="myParam=myValue"
;headers
:配置HTTP请求的头部必须满足的条件,如headers = "content-type=text/*"
;consumes
:配置HTTP请求的Content-Type
头部,如consumes = "application/json"
;produces
:配置HTTP请求的Accept
头部,如produces = {"text/plain", "application/*"}
;
看看我们的示例:
@RequestMapping("/hello")
@Controller
public class HelloController {
@Autowired
private HelloService service;
@GetMapping("/doSomething")
@ResponseBody
public String doSomething() {
service.doSomething();
return "OK";
}
}
首先,HelloController
被@RequestMapping
所修饰,其value
属性值(即path
属性值)是/hello
;
然后,HelloController
内定义了一个方法doSomething()
,该方法由@GetMapping
所修饰,其value
属性值(即path
属性值)是/doSomething
;
所以,我们定义了一个Handler,它的执行方法是HelloController
的doSomething()
,它处理的HTTP请求必须采用HTTP GET方法,URL必须是http://localhost:8080/spring-mvc-test/appA/hello/doSomething
:
http://localhost:8080
:此部分取决于浏览器和web容器的部署;/spring-mvc-test
:此部分是该web应用在web容器中的上下文路径(因为web容器中可以部署多个web应用);/appA
:此部分来自于DispatcherServlet
的servlet-mapping
配置;/hello
:此部分来自于HelloController
的@RequestMapping
的path
属性值;/doSomething
:此部分来自于doSomething()
方法的@GetMapping
的path
属性值
前面提到@RequestMapping
等注解有多个属性,除了可以配置path
之外,还可以配置其他属性来匹配HTTP请求,暂不细说,大家可以查看JavaDoc文档或源码。
请求数据转换为业务模型对象
如前所述,方法加上@RequestMapping
注解修饰之后就成为了Handler的执行方法,那我们如何在执行该方法时访问HTTP请求所携带的数据呢?
Spring MVC为我们提供了一种很方便的方式,就是通过给Handler执行方法提供特殊的参数即可。这种特殊性体现在两个方面:
- 特定类型的参数,比如
HttpServletRequest
、org.springframework.ui.Model
等; - 用特殊的注解修饰参数,比如
@RequestParam
、@RequestBody
等。
比如,将示例中的HelloController的doSomething()方法添加如下参数即可访问HTTP请求的各种数据,以及携带的参数:
package test.appa;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@RequestMapping(path="/hello")
@Controller
public class HelloController {
@Autowired
private HelloService service;
@GetMapping("/doSomething")
@ResponseBody
public String doSomething(HttpServletRequest request, @RequestParam int id) {
System.out.println(request.getRemoteAddr());
System.out.println(id);
service.doSomething();
return "OK";
}
}
不过,你的请求URL也应该添加id这个参数:
http://localhost:8080/spring-mvc-test/appA/hello/doSomething?id=100
很显然,是由Spring MVC的DispatcherServlet
在串联请求处理的各个步骤时,为Handler方法传入特定类型的参数值,或将HTTP请求的数据解析出来并进行转换再赋给为由特殊注解修饰的参数。
下表翻译自Spring框架官网,列出了Handler方法支持的参数类型和注解:
参数类型或注解 | 描述 |
---|---|
WebRequest, NativeWebRequest | 提供对请求参数、请求属性、会话属性的通用访问,从而无需直接使用Servlet API |
javax.servlet.ServletRequest, javax.servlet.ServletResponse | 它们或它们的任何一个具体类都可以,比如ServletRequest 、HttpServletRequest 、Spring的MultipartRequest 、MultipartHttpServletRequest |
javax.servlet.http.HttpSession | 会强制建立会话,因此该参数永远不会为null ,不过该会话非线程安全。如果希望多个线程能够并发访问会话,应该设置RequestMappingHandlerAdapter 的synchronizeOnSession 标记为true |
javax.servlet.http.PushBuilder | 为HTTP/2资源推送准备的Servlet 4.0的API,注意,根据Servlet规范,如果客户端不支持HTTP/2功能,则注入的PushBuilder 实例可以为空。 |
java.security.Principal | 当前已认证的用户,可能是Principal 的一个具体实现类 |
HttpMethod | HTTP请求的方法,GET、POST之类的 |
java.util.Locale | 当前请求的区域设置(用于处理国际化问题,每一个Locale 对象都代表了一个特定的地理、政治和文化地区。在操作Date ,Calendar 等表示日期/时间的对象时,经常会用到;因为不同的区域,时间表示方式都不同。)取决于可用的LocaleResolver 的最具体的子类(即配置的LocaleResolver 或LocaleContextResolver ) |
java.util.TimeZone + java.time.ZoneId | 当前请求的时区,取决于LocaleContextResolver |
java.io.InputStream, java.io.Reader | 用于访问原始的请求体 |
java.io.OutputStream, java.io.Writer | 用于访问原始的响应体 |
@PathVariable | 用于访问URI的路径变量,如“/xxx/{v1}/{v2}” ,Spring MVC会自动解析并进行类型转换后赋值给该注解所修饰的参数,下同 |
@MatrixVariable | 用于访问URI的路径段中的名字-值形式的变量,如映射模式是/pets/{petId} ,实际URI是/pets/42;q=11;r=22 ,方法参数就可以设计为(@PathVariable String petId, @MatrixVariable int q) |
@RequestParam | 访问HTTP请求所携带的参数(包括multipart文件),有两种方式,一种是上面示例中URI所携带的查询参数的键值对,一种是请求表单所携带的媒体类型是application/x-www-from-urlencoded 的键值对。注意,如果参数类型是简单类型(取决于BeanUtils#isSimpleProperty ),此注解可以省略 |
@RequestHeader | 用来访问HTTP请求的头部,比如Host localhost:8080 、Accept-Encoding gzip,deflate 、Keep-Alive 300 |
@CookieValue | 用来访问HTTP请求所携带的cookies,比如请求所携带的cookie是JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 ,则方法参数可以设计为(@CookieValue("JSESSIONID") String cookie) |
@RequestBody | 用来访问HTTP请求的请求体,Spring MVC会使用HttpMessageConverter 的实现类来解析请求体并转换为参数类型的对象,比如可以将方法参数设计为(@RequestBody Account account) ,Account 即为你所设计的业务模型类 |
HttpEntity | 用来访问HTTP请求的头部和请求体,请求体已经被HttpMessageConverter 转换为你的业务模型对象,比如方法参数设计为(HttpEntity<Account> entity) ,请求体会转换为Account 对象 |
@RequestPart | 用来访问媒体类型为multipart/form-data 的请求中的其中一个part,使用HttpMessageConverter 来转换该part的体部 |
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap | 用来访问HTML控制器中需要用到的或传给视图模板的Model ,这里的Model 是Spring MVC提供的一个类 |
RedirectAttributes | 如果需要临时保存重定向属性和flash属性,则用来指定这些属性,直到该请求被重定向之后 |
@ModelAttribute | 用来访问Model(不存在则先实例化一个)中已经存在的属性,并进行数据绑定和校验。注意,如果参数类型为非简单类型,则此注解可以省略。它也可以从请求参数、路径变量获取相应的字段数据,比如可以将方法参数设计为(@ModelAttribute Account account) ,Account类中有个id 字段,映射模式为/accounts/{id} ,则Spring MVC也会为该参数绑定id 字段的数据 |
Errors, BindingResult | 用于访问数据绑定和校验可能导致的错误,注意,此类型参数必须声明在要检测错误的参数之后 |
SessionStatus + 用在类级别上的@SessionAttributes | 用于标记表单处理已经完成,由此触发清理掉通过类级别@SessionAttributes 声明的会话属性 |
UriComponentsBuilder | 用来生成一个URL,该URL相对于当前请求的host、port、scheme、context path、servlet mapping的字面值部分 |
@SessionAttribute | 用于访问预先创建好的会话属性,不同于类级别@SessionAttributes 声明的用于临时存储的会话属性 |
@RequestAttribute | 用于访问预先创建好的请求属性 |
其他任何参数 | 表示没有匹配到此表中上面列举的任何形式,如果是简单类型,则自动解析为@RequestParam ;否则解析为@ModelAttribute |
比较常用的应该是@RequestParam
、@ModelAttribute
、@RequestBody
、HttpEntity
、java.util.Map
和org.springframework.ui.Model
了,而@RequestParam
、@ModelAttribute
这两个是可以省略的。
@PathVariable
也算比较常用,通常用于Restful风格API中。
大家可以像上面的示例那样,按照上表一个一个尝试着设计并测试一下。
处理结果传递给视图或HTTP响应
现在Handler方法已经接收到HTTP请求,并将请求中的数据转换成了我们的业务模型对象,接下来就是调用模型层中的服务以执行业务逻辑了,执行完后必然会产生某种处理结果(错误和异常也算是一种结果),那么该如何把处理结果传递给视图层(视图解析器、视图模板等)或直接写到HTTP响应呢?
与转换请求数据到Handler方法的参数类似,处理结果的传递则利用了Handler方法的返回值,我们只要提供某种特殊类型的返回值即可。这种特殊性也是体现在两个方面:
- 特定类型的返回值,比如
HttpEntity<B>
、String
、View
等; - 用特殊的注解修饰的返回值,比如
@ModelAttribute
、@ResponseBody
等。
至于将这些返回值是直接写HTTP响应,还是交给视图层渲染之后写到HTTP响应,都无需我们操心,Spring MVC已经帮我搞定了,因为这些行为都是一样的。
比如,我们的HelloController
示例中:
@RequestMapping("/hello")
@Controller
public class HelloController {
@Autowired
private HelloService service;
@GetMapping("/doSomething")
@ResponseBody
public String doSomething() {
service.doSomething();
return "OK";
}
}
doSomething()
这个Handler方法的返回值使用了@ResponseBody
来修饰,因此,Spring MVC会使用HttpMessageConverter
转换后直接写到HTTP响应报文中。而这个动作肯定也是DispatcherServlet
在串联请求处理的各个步骤时完成的。我们只需要实现具体的HttpMessageConverter
用来执行如何转换即可,不过Spring MVC已经为我们提供了默认的HttpMessageConverter
具体实现,它可以胜任大多数场景,因此我们什么都不用做就可以将"OK
"这个字符串返回给浏览器展现出来。
下表翻译自Spring框架官网,列出了Handler方法支持的返回值类型和注解:
返回值类型或注解 | 描述 |
---|---|
@ResponseBody | 被此注解修饰的返回值将会由HttpMessageConverter 转换之后直接写入到HTTP响应中 |
HttpEntity<B> , ResponseEntity<B> | 此类型的返回值也是由HttpMessageConverter 转换之后直接写入到HTTP响应中,不过它还可以指定HTTP响应头部,响应体就是由泛型中的参数类型转换而来 |
HttpHeaders | 只返回HTTP响应头部,没有响应体 |
String | 字符串类型的返回值(没有其他注解修饰)就表示视图的名字,它将由ViewResolver 来解析,并将使用隐含的Model (取决于@ModelAttribute 方法)来获取处理结果,或使用Model 参数显示添加处理结果 |
View | 返回视图实例,用来渲染视图,也将使用隐含的Model (取决于@ModelAttribute 方法)来获取处理结果,或使用Model 参数显示添加处理结果 |
java.util.Map, org.springframework.ui.Model | 返回的是要添加到Model的属性,视图名将由RequestToViewNameTranslator 隐式计算出 |
@ModelAttribute | 此注解修饰的返回值是要添加到Model的属性,视图名将由RequestToViewNameTranslator 隐式计算出,注意,此注解可以省略 |
ModelAndView | 返回的是视图和模型的属性,还有可选的响应状态 |
void | 如果Handler方法返回类型为void 或返回null 值,且拥有ServletResponse 、OutputStream 类型的参数,或被@ResponseStatus 修饰,就表示由该方法来全权处理响应,控制器检测到ETag 或lastModified 为真是也是如此。其他情况下,对于REST控制器来说表示没有响应体;对于HTML控制器来说将选择默认的视图名。 |
DeferredResult<V> | 表示由其他线程来异步的生成前述的返回值 |
Callable<V> | 表示由Spring MVC管理的线程来异步的生成前述的返回值 |
ListenableFuture<V> , java.util.concurrent.CompletionStage<V> , java.util.concurrent.CompletableFuture<V> | DeferredResult<V> 的替代品,更加方便 |
ResponseBodyEmitter, SseEmitter | 异步生成对象流,并使用HttpMessageConverter 转换后写入到HTTP响应体 |
StreamingResponseBody | 异步写入到OutputStream |
Reactive types — Reactor, RxJava, or others through ReactiveAdapterRegistry | 若DeferredResult 拥有List这种多值的流,可用这些类型代替 |
其他任何返回值 | 若是非简单类型,且不匹配上述形式的返回值,那么就以视图名来对待(通过RequestToViewNameTranslator 计算默认视图名);简单类型将保持未解析状态 |
比较常用的应该是@ResponseBody
、String
、@ModelAttribute
、HttpEntity<B>
、ResponseEntity<B>
、java.util.Map
、org.springframework.ui.Model
、ModelAndView
和View
了,而@ModelAttribute
这个是可以省略的。
@ResponseBody
通常用于Restful风格API中。
使用视图层渲染的一般是展现给人看的,此为人机界面;而直接写入HTTP响应体的通常是返回给另一个Web应用的,此为应用API。
大家可以像上面的示例那样,按照上表一个一个尝试着设计并测试一下。
异常处理
我们还是剩最后一个问题没有解决,就是Handler方法的执行过程中出现异常该如何处理。Spring MVC采用了设计了一种与Handler类似的机制,即ExceptionHandler
机制。
ExceptionHandler的定义和异常映射
正如Handler的定义和请求映射采用@RequestMapping等注解修饰方法,ExceptionHandler的定义和异常映射也采用一个注解,就是@ExceptionHandler
。
它的含义是:被它修饰的方法是一个ExceptionHandler方法。
Spring MVC会在初始化的时候扫描该注解生成ExceptionHandler的异常映射表,一旦某个Handler抛出了某个异常,则查找异常映射表,找到匹配的该异常的ExceptionHandler方法,然后执行该方法。
比如,为HelloController定义一个ExceptionHandler:
@RequestMapping(path="/hello")
@Controller
public class HelloController {
@Autowired
private HelloService service;
@GetMapping("/doSomething")
@ResponseBody
public String doSomething() throws Exception {
service.doSomething();
throw new Exception();
}
@ExceptionHandler
@ResponseBody
public String handle(Exception e) {
return "ERROR";
}
}
为了模拟异常处理,将doSomething()
方法的签名添加上throws Exception
声明,并在方法体内抛出异常throw new Exception();
然后定义一个handle(Exception e)
方法,并用@ExceptionHandler
和@ResponseBody
修饰它,前者表示该方法是ExceptionHandler
方法,后者表示异常处理的结果直接写到HTTP响应体中。
特别要注意的是handle(Exception e)
方法有一个Exception
类型的参数,只有这样才能匹配到doSomething()
方法的throws Exception
声明,如果是Exception
的子类则匹配不到,但可以是Exception
或其父类。
还有另外一种方式指定异常映射,就是采用@ExceptionHandler
注解的属性,比如,可以这样:
@ExceptionHandler(Exception.class)
@ResponseBody
public String handle() {
return "ERROR";
}
通常建议以这种方式来指定异常匹配模式,并且尽量指定具体的异常,而不是像Exception
这样比较抽象的异常。
当然,@ExceptionHandler
注解的属性值支持多个异常,如:
@ExceptionHandler({IOException.class, NullPointerException.class})
如果Handler方法抛出的某个异常匹配到了多个ExceptionHandler方法,Spring MVC将会使用ExceptionDepthComparator
来根据被抛异常在匹配异常中的深度来排序。
需要注意的是:
- 匹配异常的时候是拿被抛异常的root异常或者cause异常来匹配的;
- ExceptionHandler方法也可以抛出异常。
ExceptionHandler方法的参数
ExceptionHandler方法的参数跟Handler方法类似,我们只需给方法提供特殊的参数即可获取到有关数据,比如请求相关的信息、异常相关的信息等。
下表列出了ExceptionHandler方法的参数形式,大多数跟Handler方法的参数形式相同(这也很容易理解,本质上异常处理也属于该请求处理流程的一部分)。
参数类型或注解 | 描述 |
---|---|
Exception及其子类 | 用于访问异常相关的信息 |
HandlerMethod | 访问抛出异常的Handler方法的信息 |
WebRequest, NativeWebRequest | |
javax.servlet.ServletRequest, javax.servlet.ServletResponse | |
javax.servlet.http.HttpSession | |
java.security.Principal | |
HttpMethod | |
java.util.Locale | |
java.util.TimeZone, java.time.ZoneId | |
java.io.OutputStream, java.io.Writer | |
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap | 为生成错误响应而访问模型数据,一般为空 |
RedirectAttributes | |
@SessionAttribute | 用于访问会话属性 |
@RequestAttribute | 用于访问请求属性 |
ExceptionHandler方法的返回值
ExceptionHandler方法的返回值也是跟Handler方法类似,表示返回视图或直接写入HTTP响应。其支持的形式也大部分相同:
返回值类型或注解 | 描述 |
---|---|
@ResponseBody | |
HttpEntity<B>, ResponseEntity<B> | |
String | |
View | |
java.util.Map, org.springframework.ui.Model | |
@ModelAttribute | |
ModelAndView | |
void | |
其他任何返回值 |
控制器的类型
- REST控制器:实际上就是被@ResponseBody注解修饰的方法,或者直接在控制器类上用@RestController注解修饰,它主要用于应用(程序)之间的数据交换和交互,最典型的就是使用JSON和XML。
- HTML控制器:就是普通的控制器,一般处理结果需要交给视图层组件,进行视图渲染后返回给浏览器用于与人的交互,这就是人机界面。
总结
到目前为止,实际上我们已经把Spring MVC最主要的内容已经梳理了一遍,大家可以在之前的演示工程spring-mvc-test中修改代码,可以尝试着修改不同的请求映射模式和异常映射模式,传入表中所列各种形式的参数和返回值,尽情的实践吧。
记住,软件科学是一门实践科学,事实上任何学问都是,正如名句:纸上得来终觉浅,绝知此事要躬行!
- Handler的定义使用
@RequestMapping
修饰方法,指定了该方法能够处理的HTTP请求满足何种条件; - Handler通过各种形式的参数,很方便的从HTTP请求中提取我们需要的数据并转换;
- Handler通过各种形式的返回值,很方便的把我们的处理结果传递给视图或直接写到HTTP响应;
- Handler执行过程中出现的异常可以定义ExceptionHandler来集中处理;
- ExceptionHandler的定义使用
@ExceptionHandler
修饰方法,指定了该方法能够处理何种异常; - ExceptionHandler的数据获取和处理结果返回,与Handler相同,都采用各种形式的参数和返回值;
- 本质上是Spring MVC在
DispatcherServlet
中串联了请求处理的各种步骤,包括分派HTTP请求、解析HTTP请求、将数据绑定到我们的模型组件并进行校验、执行Handler方法、将Handler方法的返回值传递给视图或直接写到HTTP响应、视图层组件使用附带的模型属性进行视图渲染后写到HTTP响应、Handler如有异常则转到ExceptionHandler; - 串联步骤时采用了面向接口的原则,而实际运行过程中需要我们配置各个步骤需要用到的具体类Bean,甚至开发特殊需求的具体类,这里借助了Spring IoC容器;
- 交互主要分为人机交互(人机界面)和应用间交互(应用API)。