Mvc框架围绕着一个DispatcherServlet设计,它用来分发处理请求,配合着处理映射,视图解析,语言,时间区,主题解析,支持文件上传。默认的处理由@Controller and @RequestMapping来支持。Spring3.0控制器机制也支持创建Restful类型的网站,通过@PathVariable等一系列注解来声明。
Springmvc视图解析很灵活,一个控制器负责准备数据和选择的视图名组成的模板映射,或者直接将数据写到响应流,完成请求。
DispatcherServlet是一个Servlet继承了HttpServlet基类。你需要映射请求用DispatcherServlet来处理
<web-app>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/example/*</url-pattern>
</servlet-mapping>
</web-app>
上面配置中所有以/example开头的请求都会由DispatcherServlet的实例example处理,同样相等的在a Servlet 3.0+environment环境中,可以用以下的代码代替上面的xml中的内容。
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/example/*");
}
}
WebApplicationInitializer由mvc提供的接口,保证的的类被检测并自动初始化Servlet 3容器,一个抽象类实现了AbstractDispatcherServletInitializer接口使得注册DispatcherServlet更容易?(不怎么了解)
上下文关系图
初始化dispatcherServlet,寻找应用中WEB-INF目录中的[servlet-name]-servlet.xml。
考虑一下的web.xml中的配置
<web-app>
<servlet>
<servlet-name>golfing</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>golfing</servlet-name>
<url-pattern>/golfing/*</url-pattern>
</servlet-mapping>
</web-app>
以上的配置对应的你的应用中得有一个/WEB-INF/golfing-servlet.xml对用的配置文件。
也可以只有一个根目录下的上下文文件包含了初始化的参数,指定具体的servlet配置路径
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
WebApplicationContext是ApplicationContext的扩展,有一些其他的特性。可以来解析主题,并且知道了哪个servlet相关(有一个和ServletContext的连接)。
Dispatcherservlet用特殊的bean来处理请求返回视图,你可以通过配置选择一些ben在web上下文中,也可以使用默认的一系列bean。
HandlerMapping 匹配请求和一系列的前后处理(处理拦截器)。
HandlerAdapter 帮助DispatcherServlet调用映射的处理器
HandlerExceptionResolver 将异常对应到视图
ViewResolver 解析逻辑的基于字符串的视图名到实际的视图类型
LocaleResolver & LocaleContextResolver 提供国际化的视图
ThemeResolver 在应用中提供个性化的布局资源(css,jpg)
MultipartResolver 处理多媒体请求,如处理文件上传来自表单
FlashMapManager 储存输入,输出 FlashMap,传递属性组从一个请求到另一个请求在重定向中
默认的DispatcherServlet配置保存在org.springframework.web.servlet的DispatcherServlet.properties文件中
DispatcherServlet处理请求的顺序
1.应用上下文查找并绑定请求中的信息作为属性,提供以后的处理器或其他使用
2.Locale resolver 解析方言在处理请求中(渲染视图,准备数据)使用
3.The theme resolver 请求中使视图决定要使用哪些主题
4.a multipart file resolver 请求中检查多媒体,找到了,请求被包装成MultipartHttpServletRequest,待后续处理
5.寻找合适的处理器,找到了处理器相关的执行链(前处理,后处理,控制器),来处理业务和数据
6.如果业务实体返回,视图被渲染;无返回不处理视图
DispatcherServlet 初始化参数组
contextClass 实现了WebApplicationContext,初始化servlet使用的上下文,默认使用XmlWebApplicationContext
contextConfigLocation 根据字符串标识的一个或多个上下文类名,或者是位置信息来配置,最后的起作用。
Namespace WebApplicationContext命名空间,默认是 [servlet-name]-servlet
解析用户输入,转变为model,通过view展示给user
使用注解@RequestMapping, @RequestParam, @ModelAttribute(Spring2.5)
@Controller
public class HelloWorldController {
@RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
return "helloWorld";
}
}
自动检测注解控制器xml配置
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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">
<context:component-scan base-package="org.springframework.samples.petclinic.web"/>
<!-- ... -->
</beans>
将URLS到对应的实体类或特定处理方法
Controller
@RequestMapping("/appointments")
public class AppointmentsController {
private final AppointmentBook appointmentBook;
@Autowired
public AppointmentsController(AppointmentBook appointmentBook) {
this.appointmentBook = appointmentBook;
}
@RequestMapping(method = RequestMethod.GET)
public Map<String, Appointment> get() {
return appointmentBook.getAppointmentsForToday();
}
@RequestMapping(value="/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
return appointmentBook.getAppointmentsForDay(day);
}
@RequestMapping(value="/new", method = RequestMethod.GET)
public AppointmentForm getNewForm() {
return new AppointmentForm();
}
@RequestMapping(method = RequestMethod.POST)
public String add(@Valid AppointmentForm appointment, BindingResult result) {
if (result.hasErrors()) {
return "appointments/new";
}
appointmentBook.addAppointment(appointment);
return "redirect:/appointments";
}
}
@RequestMapping 类上, 表示控制器类中方法和url相关
方法上,表示方法和不同URLS相关
@Controller
public class ClinicController {
private final Clinic clinic;
@Autowired
public ClinicController(Clinic clinic) {
this.clinic = clinic;
}
@RequestMapping("/")
public void welcomeHandler() {
}
@RequestMapping("/vets")
public ModelMap vetsHandler() {
return new ModelMap(this.clinic.getVets());
}
}
可以用@RequestMapping(method=GET)缩小动作范围
有时候一个控制器需要运行时用AOP代理装饰,比如在控制器上用@Transactional,这种情况下使用基于类的代理
RequestMappingHandlerMapping 是3.1中唯一一个地方决定请求处理的方法,RequestMappingHandlerAdapter 是实际调用方法。
在3.1之前,类型和方法级别的请求映射由两个阶段检查,先是DefaultAnnotationHandlerMapping,然后实际方法调用是AnnotationMethodHandlerAdapter。
@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
url模板“/owners/{ownerId}”表面变量是ownerId,当控制器处理这个请求时,URI中合适地方发现的值设置为ownerId的值,比如,当一个请求来自/owners/fred时,ownerId的值是fred。
或者你可以在参数注解中明确指定变量名,如下:@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
// implementation omitted
}
一个方法参数中,可以有多个@PathVariable;
@RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
Pet pet = owner.getPet(petId);
model.addAttribute("pet", pet);
return "displayPet";
}
还有一种路径中参数变量的请求方式URL/owners/42/pets/21.
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping("/pets/{petId}")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
路径参数可以使任何简单类型int,long,Date等,Spring自动转化类型或者失败时表现TypeMismatchException异常
@RequestMapping支持URI模板中变量表达式的使用,如{varName:regex},第一部分是变量名,第二部分是匹配表达式,比如:
/spring-web/spring-web-3.0.5.jar
@RequestMapping("/spring-web/{symbolicName:[a-z-]}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]}")
public void handle(@PathVariable String version, @PathVariable String extension) {
// ...
}
}
请求路径匹配规则
默认MVC执行文件后缀匹配,匹配路径/person也适合匹配/person.*;
模型变量:在URL中eg: "/cars;color=red;year=2012",以;或,分割,Eg: "color=red,green,blue" 或"color=red;color=green;color=blue";
// GET /pets/42;q=11;r=22
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
用注解指定这种变量之间值得映射关系:
// GET /owners/42;q=11/pets/21;q=22
@RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(
@MatrixVariable(value="q", pathVar="ownerId") int q1,
@MatrixVariable(value="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
可以指定默认值代替路径中的变量值
// GET /pets/42
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
// q == 1
}
将值传入Map类型的参数中
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(
@MatrixVariable Map<String, String> matrixVars,
@MatrixVariable(pathVar="petId"") Map<String, String> petMatrixVars) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 11, "s" : 23]
}
默认配置中<mvc:annotation-driven>有一个属性enable-matrix-variables 为false,如果要使用这种变量需要设置为true:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven enable-matrix-variables="true"/>
</beans>
@RequestMapping中参数consumes指定媒体类型,来匹配请求头中content-Type中相同的媒体类型
@Controller
@RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
// implementation omitted
}
又如Content-Type :text/plain
@RequestMapping中produces属性指定相应请求响应头中content-type类型
@Controller
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, produces="application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {
// implementation omitted
}
请求参数和请求头值
@RequestMapping params "myParam", "!myParam", or "myParam=myValue"
指定参数,非指定参数,指定参数值
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping(value = "/pets", method = RequestMethod.GET, headers="myHeader=myValue")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
请求或相应对象(servlet API)veg: ServletRequest, HttpServletRequest;
Session 对象(servlet API)httpSesion类型,展现session,这样的参数永远不会为空(session可能非线程安全,尤其在servlet环境中,如果多个请求同时操作一个会话,可以设置RequestMappingHandlerAdapter's "synchronizeOnSession" flag to "true");
org.springframework.web.context.request.WebRequest or org.springframework.web.context.request.NativeWebRequest请求参数或属性通过,与本地Servlet/Portlet API无关;
java.util.Locale 当前请求的现场,由配置环境中的LocaleResolver / LocaleContextResolver决定;
java.util.TimeZone当前请求,由LocaleContextResolver决定;
java.io.InputStream / java.io.Reader 请求内容,由Servlet API暴露原始值;
java.io.OutputStream / java.io.Writer,响应内容,由Servlet API暴露原始值;
org.springframework.http.HttpMethod 请求动作,get,post,delete,head,opitions…..;
java.security.Principal包含目前已授权的用户;
@PathVariable URL模板中变量对应参数;
@MatrixVariable URL路径片段中的name-value对对应的参数类型;
@RequestParam 访问特殊的servlet请求参数,参数值自动转化为方法参数类型;
@RequestHeader访问特殊的servlet请求头,参数值自动转化为方法参数类型;
@RequestBody 访问http请求体参数注解,参数值转换为参数定义类型通过httpMessageConverter;
@RequestPart访问"multipart/form-data"请求部分的注解;
HttpEntity<?> 访问servlet请求http的请求头和内容,请求流转化为实体通过HttpMessageConverter;
java.util.Map / org.springframework.ui.Model / org.springframework.ui.ModelMap 充实model暴露给web视图;
org.springframework.web.servlet.mvc.support.RedirectAttributes 明确重定向的属性集合,在server端缓存属性方便重定向后二次请求的需要;
RedirectAttributes 不通过model如果方法返回带有 “redirect:”前缀的视图名或RedirectView;
org.springframework.validation.Errors / org.springframework.validation.BindingResult 校验结果为前处理或form对象;
org.springframework.web.bind.support.SessionStatus处理表单过程完成状态的标志,如果成功就清理会话属性(@SessionAttributes指定);
org.springframework.web.util.UriComponentsBuilder准备当前请求的url生成器。;
无效的参数顺序(@ModelAttribute("pet") Pet pet, Model model, BindingRes
有效的参数顺序 (@ModelAttribute("pet") Pet pet, BindingResult result, Model model)
ModelAndView 对象,包含数据模型和@ModelAttribute注解访问的方法结果
Model 包含视图对象名(RequestToViewNameTranslator决定),命令对象充实的数据,结果(ModelAttribute标注方法返回相关数据)
Map 放弃数据模型的对象,包含视图名(RequestToViewNameTranslator)和充实后的数据模型;
View 命令对象返回的数据模型和@ModelAttribute涉及的数据访问方法,处理方法可以通过Model参数来充实数据模型
String 逻辑视图名的值,命令对象决定的数据模型和@ModelAttribute涉及的数据访问方法,a Model参数标记的处理方法;
Void 如果自己处理响应(直接返回数据,定义参数ServletResponse/HttpServletResponse)或者由RequestToViewNameTranslator决定,(不是定义响应参数在处理方法签名中)
如果方法是由@ResponseBody标记的,返回方式写入响应http身体,返回值转变为定义的参数类型(通过HttpMessageConverters)
HttpEntity<?>or ResponseEntity<?> 访问servlet响应http头或内容,实体将转化为响应流(HttpMessageConverters)
HttpHeaders 作为返回响应 没有身体
Callable<?>返回,Spring mvc管理线程,应用异步处理返回结果
DeferredResult<?>自己选择线程处理返回结果值
ListenableFuture<?> 同上
任何其他返回类型都是被当作数据模型的一个属性暴露给视图,@ModelAttribute在方法级上做标记
请求参数到方法参数的绑定 @RequestParam
@Controller
@RequestMapping("/pets")
@SessionAttributes("pet")
public class EditPetForm {
// ...
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
// ...
}
自动转化参数非string类型,可以设置默认参数值
请求体和@RequestBody注解的对象相映射
方法参数和http请求体的值绑定
@RequestMapping(value = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
转化通过HttpMessageConverter将请求消息转化为对象 并且将对象转化为响应体 ,RequestMappingHandlerAdapter支持@RequestBody,HttpMessageConverters支持对象的转化
如果要进行xml的转化,需要MarshallingHttpMessageConverter通过确定的org.springframework.oxm下的 Marshaller和Unmarshaller实现
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<util:list id="beanList">
<ref bean="stringHttpMessageConverter"/>
<ref bean="marshallingHttpMessageConverter"/>
</util:list>
</property
</bean>
<bean id="stringHttpMessageConverter"
class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean id="marshallingHttpMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="castorMarshaller" />
<property name="unmarshaller" ref="castorMarshaller" />
</bean>
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>
一个@RequestBody注解的方法参数可以用@Valid,表示用确定的Validator实例来验证
标记方法表示返回的对象将直接写入http响应体(不会被方法模型代替,或视图名打断)
@RequestMapping(value = "/something", method = RequestMethod.PUT)
@ResponseBody
public String helloWorld() {
return "Hello World";
}
上例中文本会写入响应流中
与@RequestBody,相同,HttpMessageConverter转换返回对象为响应体。
@RestController在映射的方法上,,将@ResponseBody和@Controller联合起来。
和@RequestBody and @ResponseBody用来访问请求体和响应体,而且可以访问请求和响应头(httpEntity and ResponseEntity)
@RequestMapping("/something")
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException {
//请求头值
String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader"));
//读取请求体作为字节数组
byte[] requestBody = requestEntity.getBody();
// do something with request header and body
//返回创建的响应头
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyResponseHeader", "MyValue");
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}
添加一个或多个model属性,不是直接与请求相关联,而是在关联之前调用。
/ Add one attribute
// The return value of the method is added to the model under the name "account"
// You can customize the name via @ModelAttribute("myAccount")
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountManager.findAccount(number);
}
// Add multiple attributes
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountManager.findAccount(number));
// add more ...
}
@ModelAttribute注解的方法用来构成哪些下拉框值得属性,或者构成一个对象用来在html表单中展示
在方法上使用@ModelAttribute有两种方式,第一种,增加一个参数并且返回。第二种,接收一个Model,加入多个属性。
一个controller可以有多个@ModelAttribute标记的方法,这些方法都在映射url之前被调用
@ModelAttribute可以用在@RequestMapping注解的方法上,RequestMapping注解方法的返回时一个model的属性而不是一个视图名。
参数由model而来,如果不在model中,则先实例化后加入model.在model中后,参数fields由所有请求匹配的名字中获得。者叫做数据绑定,一个有用的机制使你避免从field中一个个解析
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute Pet pet) { }
Pet的实例从哪来:
已在model中(使用了SessionAttributes )
已在model中由于使用了(@ModelAttribute)在同一个controller 上
基于URI template变量和类型转换
实例化使用自己默认的构造器
@ModelAttribute标记的方法是一种普遍的方法来传递数据库中的值,可以选择性的存储在请求或者使用@SessionAttributes。一些情况中使用URI template variable and a type converter.恢复属性更方便
@RequestMapping(value="/accounts/{account}", method = RequestMethod.PUT)
public String save(@ModelAttribute("account") Account account) {
}
上例中model属性account匹配uri template同名变量,如果你注册Converter<String, Account>可以将string型的account值转为account实例
数据绑定中可能有没有匹配的错误,检查这种错误加上BindingResult参数
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
// ...
}
@Valid校验参数绑定
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
特定的处理器处理SessionAttributes定义的session。列出model属性名或属性类型(显示的存储在session或一些会话的存储)当作随后请求的支持者
@Controller
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
public class EditPetForm {
// ...
}
指定重请求和短时间存储的属性
默认的一些model属性被暴露在uri template的重请求url的变量中,这些参数被保存下来在集合或队列中。
@CookieValue 来映射cookie values
可以将方法参数与http cookie绑定
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
http请求获得的cookie值
@RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) {
//...
}
由名字来映射
Host localhost:8080
Accept text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
@RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}
上例中通过映射将相同名字对应的值映射到方法参数中
@RequestHeader 用在Map<String, String>, MultiValueMap<String, String>, or HttpHeaders 中会获得所有的头中的值。
请求中的值包括请求参数,路径变量,请求头,cookie值转化为对应的方法参数类型
最近修改的响应内容的缓存
一个@RequestMapping标注的方法可能要去支持最近修改的请求去促进内容的缓存
一个应用就是请求通过计算最近修改时间查看web资源是否发生了变化,状态304没有发生变化
@RequestMapping
public String myHandleMethod(WebRequest webRequest, Model model) {
long lastModified = // 1. application-specific calculation
if (request.checkNotModified(lastModified)) {
// 2. shortcut exit - no further processing necessary
return null;
}
// 3. or otherwise further request processing, actually preparing content
model.addAttribute(...);
return "myViewName";
}
方法级别的注解使用,通知实现的类通过路径扫描自动检测相关的实现类;当使用MVC namespace 或 mvc java config
@ControllerAdvice注解的classes能包含@ExceptionHandler, @InitBinder, and @ModelAttribute 注解的方法
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class AnnotationAdvice {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class BasePackageAdvice {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class AssignableTypesAdvice {}
Jackson Serialization View Support
有时能够过滤对象上下文序列化到http响应体中。为了使用这种功能通过springmvc提供的Jackson’s Serialization Views.
在@ResponseBody controller方法上使用或者控制方法返回ResponseEntity,简单的在类参数上使用注解@JsonView,指定具体的view类或者要使用的接口
@RestController
public class UserController {
@RequestMapping(value = "/user", method = RequestMethod.GET)
@JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
@JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
@JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
控制器依赖视图解析,简单的添加序列化的视图类到model
@Controller
public class UserController extends AbstractController {
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getUser(Model model) {
model.addAttribute("user", new User("eric", "7!jd#h23"));
model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class);
return "userView";
}
}
Jackson JSONP Support
为了使jsonp支持@responseBody标记的或者返回 ResponseEntity的方法,定义一个@ControllerAdvice bean 继承AbstractJsonpResponseBodyAdvice 类。
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
JSONP自动使得请求有参数(名字为jsoup或callback),这些名字能够自定义通过jsonpParameterNames属性
Spring MVC 3.2 介绍 Servlet 3的异步请求过程,不是返回一个值,一个controller返回java.util.concurrent.Callable,并且产生一个返回结果通过另一线程,同时主要的servlet容器线程被释放,并允许处理其他请求。Spring mvc调用 the callable在另一个线程中(TaskExecutor的帮助),当callable返回时,将请求分派给Servlet容器简单处理callable返回的值,以下是一个例子
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public String call() throws Exception {
// ...
return "someView";
}
};
}
第二种选择是返回一个DeferredResult的实例。这种情况下返回值也会被一个线程处理,spring mvc并不知道这个线程,比如返回可能是对一个JMS信息事件的响应,一个定时任务等:
@RequestMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
// Save the deferredResult in in-memory queue ...
return deferredResult;
}
// In some other thread...
deferredResult.setResult(data);
}
没有servlet3异步处理特性的了解是很难明白这一点,了解一下基础的事实
ServletRequest通过request.startAsync()的调用使得进入异步的模式。做事的是servlet和一些filters,可以退出,响应依然打开,允许其他线程完成处理
调用后返回异步的上下文,可以供以后异步的处理,例如提供dispatch方法,可以调用来分发请求返回servlet 容器。异步分发类似forward跳转,只是从应用的一个线程到container的另一个线程,然而forward是同步发生在同一个线程中。
ServletRequest可以访问DispatcherType,,可以用来区分servlet和filter(在最初的请求处理线程中)
Callable异常请求处理:Controller返回Callable对象;spring mvc开始异常处理提交callable到taskExecutor(不同的线程处理);DispatcherServlet和所有Filter退出请求处理现场,响应依然打开;calalble产生结果,spring 分发请求回到servlet容器;DispatcherServlet再次被调用处理异步产生的结果(处理callable)
当返回callable时引起异常怎么处理?由@ExceptionHandler注解的方法在同一个控制器中或者一个配置的实例HandlerExceptionResolver
当使用DeferredResult时选择它的setErrorResult(Object)方法,提供一个异常或其他对象。用@ExceptionHandler标记的方法在同一个controller或者一个HandlerExceotionResolver实例处理异常
拦截异步请求(Intercept)
一个存在的HandlerInterceptor实现AsyncHandlerInterceptor,提供另外的方法afterConcurrentHandlingStarted。这个方法被调用在异常处理开始之后,初始的请求处理线程退出。可以参考AsyncHandlerInterceptor javadocs
DeferredResult提供其他异步请求生命周期的调用,有方法onTimeout(Runnable),onCompletion(Runnable),分别是在超时或者完成后调用。超时事件可以设置DeferredResult为一些其他值,完成调用是final型,它的结果不能再设置
处理映射handler mappings
下例示例如何配置一个拦截器
<beans>
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<bean class="example.MyInterceptor"/>
</property>
</bean>
<beans>
<beans>
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<list>
<ref bean="officeHoursInterceptor"/>
</list>
</property>
</bean>
<bean id="officeHoursInterceptor"
class="samples.TimeBasedAccessInterceptor">
<property name="openingTime" value="9"/>
<property name="closingTime" value="18"/>
</bean>
<beans>
package samples;
public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
private int openingTime;
private int closingTime;
public void setOpeningTime(int openingTime) {
this.openingTime = openingTime;
}
public void setClosingTime(int closingTime) {
this.closingTime = closingTime;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
Calendar cal = Calendar.getInstance();
int hour = cal.get(HOUR_OF_DAY);
if (openingTime <= hour && hour < closingTime) {
return true;
}
response.sendRedirect("http://host.com/outsideOfficeHours.html");
return false;
}
}
解析视图,
两个接口ViewResolver提供视图名和视图的映射,View将请求交给对应的视图技术
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="1"/>
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
<!-- in views.xml -->
<beans>
<bean name="report" class="org.springframework.example.ReportExcelView"/>
</beans>
重定向view
@RequestMapping(value = "/files/{path}", method = RequestMethod.POST)
public String upload(...) {
// ...
return "redirect:files/{path}";
}
创建urls
提供创建加密的URI使用UriComponentsBuilder and UriComponents.
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
"http://example.com/hotels/{hotel}/bookings/{booking}").build();
URI uri = uriComponents.expand("42", "21").encode().toUri();
Note that UriComponents is immutable and the expand() and encode() operations return new instances if necessary.
You can also expand and encode using individual URI components:
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build()
.expand("42", "21")
.encode();
在servlet环境中ServletUriComponentsBuilder提供静态工厂方法从servlet 请求中获得可用的url信息
HttpServletRequest request = ...
// Re-use host, scheme, port, path and query string
// Replace the "accountId" query param
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request)
.replaceQueryParam("accountId", "{id}").build()
.expand("123")
.encode();