Spring 4.1..6文档笔记



Spring mvc

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
 
 
 
  
 
实现Controllers
解析用户输入,转变为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";
    }
}
 
 
  
 
定义控制器 @Controller
自动检测注解控制器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>
 
  
 
请求映射@RequestMapping
将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,这种情况下使用基于类的代理

 

 



在Spring MVC 3.1中支持@RequestMapping methods的类

RequestMappingHandlerMapping 是3.1中唯一一个地方决定请求处理的方法,RequestMappingHandlerAdapter 是实际调用方法。

 

在3.1之前,类型和方法级别的请求映射由两个阶段检查,先是DefaultAnnotationHandlerMapping,然后实际方法调用是AnnotationMethodHandlerAdapter。

 



@PathVariable在方法参数上绑定URI模板变量的值

 

@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.*;
 
 
  
 
Matrix Variables
模型变量:在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
    }
 
}

 



@RequestMapping匹配的处理方法参数类型



 支持的方法参数类型

请求或相应对象(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 当前请求的现场,由配置环境中的LocaleResolverLocaleContextResolver决定;

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实例来验证

 



将响应体和对应的方法相关联@ResponseBody

标记方法表示返回的对象将直接写入http响应体(不会被方法模型代替,或视图名打断)

@RequestMapping(value = "/something", method = RequestMethod.PUT)
@ResponseBody
public String helloWorld() {
    return "Hello World";
}

上例中文本会写入响应流中

与@RequestBody,相同,HttpMessageConverter转换返回对象为响应体。

 

@RestController在映射的方法上,,将@ResponseBody和@Controller联合起来。

 



使用HttpEntity

和@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);
}



@ModelAttribute 在方法上和方法参数上

添加一个或多个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的属性而不是一个视图名。
 
 
 
  
 
在方法参数中使用@ModelAttribute 注解
参数由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在请求之中存储model属性在http会话中

特定的处理器处理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) {
    //...
}
由名字来映射
 
  
 
请求头属性和@RequestHeader注解的参数绑定
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";
}

 

 



@ControllerAdvice通知controllers

方法级别的注解使用,通知实现的类通过路径扫描自动检测相关的实现类;当使用MVC或 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方法上使用或者控制方法返回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属性
 
  
 
Asynchronous Request Processing
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并不知道这个线程,比如返回可能是对一个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)

 

 



Exception Handling for Async Requests

当返回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>
 
 
  
 
HandlerInterceptor拦截请求
<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;
    }
}



任何请求处理被拦截TimeBasedAccessInterceptor,如果当前日期是工作时间之外,用户重定向到静态html页面,只有在规定时间内才能访问

 

解析视图,

两个接口ViewResolver提供视图名和视图的映射,View将请求交给对应的视图技术



 Resolving views with the ViewResolver interface

 

 

<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();