转自:http://blog.csdn.net/u011768896/article/details/45000397
参考资料:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-features
1 Spring Web MVC框架介绍
DispatcherServlet是框架的设计核心,他负责分发请求给处理器、包括可配置的处理映射,视图表现,本地化,时区以及可上传文件式的主题。
默认的处理器基于注解@Controller和@RequestMapping,提供一个相当大幅度的灵活度处理的方法。
通过Spring 3.0,@Controller能允许你通过@PathVariable注解创建一个RESTful的web站点和应用程序。
通过Spring MVC我们可以用任何对象作为控制对象或基于表单的对象,我们不需要实现某框架的特殊接口或继承类。Spring的数据绑定具有很高的灵活性,比如它可以处理类型错误如验证错误一样,使得通过应用程序验证,而不是系统错误。
Spring MVC的页面处理也非常灵活。Controller不仅是准备一个model的含数据的map的响应,也可以选择一个视图名,并且它可以直接写出response流并且完成请求。
视图(View)名可通过文件扩展名配置或者接受头内容的类型转让,包括bean的名字,properties问卷或者通过自定义的ViewResolver接口。
模型(Model)是一个Map接口,它考虑到完全抽象的表现层技术。你可以直接将其表示成JSP,Velocity,Freemarker,或者生成XML,JSON,Atom,和许多其他内容类型。Model的Map可以简单的转换为一个合适的格式,如JSP请求或者Velocity模板。
1.1 Spring Web MVC功能
- 清晰的分工
- 强力直观的配置
- 灵活的,无强制配置,适应性强
- 可重用的业务代码
- 自定义的数据绑定和验证
- 自定义的处理器匹配和页面响应
- 灵活的模型转换
- 自定义的时区,本地化以及主题策略
- 一个简单并强大的JSP标签库,提供了支持功能
- 对象的生命周期是包括在当前的HTTP request或者 HTTP Session中的
2 The DispatcherServlet
Spring Web MVC框架像许多其他的web MVC框架一样,基于request,并设计成围绕着核心Servlet来分发请求到控制端,控制端提供功能性。
如下图,DispatcherServlet承担着Front controller的任务。
DispatcherServlet是一个Servlet,他继承自HttpServlet的基类,并且声明自web.xml。我们需要匹配请求来让DispatcherServlet处理,即<url-pattern>,
web.xml :
<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>
在Servlet3.0+的环境下,也可以通过编码的手段来配置:
编码配置(等价于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是Spring MVC提供的基类来初始化Servlet 3的容器。
每一个DispatcherServlet都有一个自己的WebApplicationContext,而这个WebApplicationContext 继承的所有实例都定义在根WebApplicationContext中。
在初始化DispatcherServlet时,Spring MVC寻找一个[servlet-name]-servlet.xml的文件在WEB-INF下,比如example-servlet.xml。
他也可以通过配置属性contextConfigLocation来自定义配置文件目录:
<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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
WebApplicationContext是ApplcationContext基于web应用功能的扩展,它不同于ApplicationContext在可以使用主题,并且可以知道哪个Servlt是被联系的。WebApplicationContext被绑定在ServletContext中,通过RequestContextUtils类中的静态方法,你可以查看WebApplicationContext。
2.1 WebApplicationContext中特殊的Bean类型
Spring的DispatcherServlet使用特殊的Bean去处理请求以及返回页面。我们可以选择单个或多个使用这些bean在WebApplicationContext中。Spring MVC准备了一个默认bean列表,所以当你没有配置时,你并不需要初始化他们。
Bean Type | 说明 | HandlerMappingMaps来自请求到处理器和一个预先和延后处理器(即拦截器),基于一些可改变的规则来自HandlerMapping实现。最常用的实现支持注解的控制器HandlerAdapter帮助DispatcherServlet执行一个处理器去匹配不被理会的请求。例如一个注解的controller需要处理大量的注解,因此HandlerAdpater的主要用途是隐藏DisptatcherServlet的细节HandlerExceptionResolver匹配错误至页面ViewResolver处理基于String的View名字至存在的View页面LocaleResolver&LocaleContextResolver用于国际化ThemeResolver提供能用的Web应用主题MulitipartResolver解析上传文件请求FlashMapManager保存并搜索“input”和“output”,能用于传递属性至不同的request,经常用于重定向2.2 默认的DispatcherServlet配置
所有的特殊Bean都有配置,在org.springframework.web.servlet中的DispatcherServlet.properties。
2.3 DispatcherServlet的处理顺序
- 获取WebApplicationContext并被绑定为一个控制器以及其他元素可使用的属性。
- 本地的解析器被绑定到请求。
- 主题解析器被绑定。
- 如果指定了复合文件解析器,则请求将检查符合文件
- 搜索适当的处理器
- 如果返回了一个model,则提供一个view。
DispatcherServlet可初始化这几个属性:contextClass(自定义的类),contextConfigLocation(配置文件),namespace(默认为[servlet-name]-servlet)。
3 实现Controllers
@Controllerpublic class HelloWorldController { @RequestMapping("/helloWorld") public String helloWorld(Model model) { model.addAttribute("message", "Hello World!"); return "helloWorld"; }}
@Controller和@RequestMapping这2个注解提供了灵活的方法名即签名配置。
在以上例子中方法需要接受一个Model类型的形参,并且返回一个String类型的视图名。
3.1 通过@Controller来定义controller
只需要启动扫描注解包就可以使用@Controller来定义包
<?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>
3.2 通过@RequestMapping来匹配请求
@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"; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
以上例子使用了很多@RequestMapping注解,第一个在类级别下的@RequestMapping(“/appointments”)说明所有的处理方法都是与appointments相关的。
get表明只接受get请求。
用@RequestMapping注解来修饰类级别并不是必须的:
@Controllerpublic 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()); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
URL Template Patterns
URL模板可以用在@RequestMapping下的方法。
例如http://www.example.com/users/{userId} 可将fred注入到域中http://www.example.com/users/fred。
在Spring MVC中使用@PathVariable注解在形参中,可将其绑定到url模板中的属性。
详细例子:
@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";}
或者:
@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { }
在类级别使用url模板
@Controller@RequestMapping("/owners/{ownerId}")public class RelativePathUriTemplateController { @RequestMapping("/pets/{petId}") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { }}
当使用findPet( )方法后URL:/owners/42/pets/21.
@PathVariable的类型为基本类型。
URL模板也可以匹配正则表达式
3.3 定义@RequestMapping 处理器方法
@RequestMapping处理方法有很高的灵活性。
支持方法参数类型
- Request和Reponse对象(Servlet API),比如ServletRequest和HttpServletRequest
- Session object(Servlet API):HttpSession
- WebRequest或者NativeWebRequest
- Local,LocaleResolver/LocaleContextResolver
- TimeZone/ZoneId
- InputStream/Reader,request内容
- OutputStream/Writer,response内容
- HttpMethod,Http请求
- Principal:包含当前的认证用户
- @PathVariable:用于URL模板的变量
- @MatrixVariable:用于URL的键值配对的片段
- @RequsetParam:用于指定的Servlet request参数
- @RequestHeader:用于指定的Servlet request的HTTP头
- @RequestBody:用于指定的HTTP request的body
- @RequestPart:用于复合数据请求
- HttpEntity<?>参数用来访问Servlet request HTTP headers和contensts
- Map/Model,保存了视图所需要的模型数据
- RedirectAttributes:特定的重定向的属性
- Errors/BindingResult: 验证结果
- SessionStatus:
- UrlComponentsBuilder
如果使用Errors或者BindingResult,需要如下顺序:
@RequestMapping(method = RequestMethod.POST)public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, Model model) { ... }
支持的返回类型
- ModelAndView类型,@ModelAttrivute描述的方法
- Model类型,@ModelAttribute描述的数据方法
- Map类型
- View类型
- String类型,对应view的名字
- void,相应自己
通过@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"; } }
3.4 异步请求处理
4 处理匹配
我们通过@RequestMapping注解来处理匹配,然而,知道所有的HandlerMapping类都是继承自AbstractHandlerMapping可以自定义他们的行为:
- interceptors:拦截器
- defaultHandler:默认的处理器
- order:基于order属性
- alwaysUseFullPath。
- urlDecode,默认为true
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <property name="interceptors"> <bean class="example.MyInterceptor"/> </property> </bean><beans>
4.1 通过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; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
5 处理页面
对于Spring处理视图,ViewResolver和View很重要,ViewResolver提供一个匹配在视图名和视图文件,view接口记录了request的准备已经处理request到视图。
5.1 通过ViewResolver接口处理views
ViewResolver:
ViewResolver | Description | AbstractCachingViewResolver处理器缓存了一些视图,通常这些视图需要在使用前预处理XMLViewResolver实现ViewResolver能接受配置文件写在XML,默认的配置文件在/WEB-INF/views.xmlResourceBundleViewResolver使用属性文件实现ViewResolver,默认为views.propertiesUrlBasedViewResolver直接将结果映射到URLInternalResourceViewResolverUrlBasedViewResolver的次级类VelocityViewResolver支持VelocityViewContentNegotiationgViewResolver实现ViewResolver接口,通过request的文件名或者accept头来生成视图URLBasedViewResolver
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/></bean>
ResourceBundleViewResolver
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> <property name="basename" value="views"/> <property name="defaultParentView" value="parentView"/></bean>
5.2 ViewResolver链
<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><beans> <bean name="report" class="org.springframework.example.ReportExcelView"/></beans>
5.3 重定向views
RedirectView
@RequestMapping(value = "/files/{path}", method = RequestMethod.POST)public String upload(...) { return "redirect:files/{path}";}
The redirect: prefix
redirect:http://myhost.com/some/arbitrary/path
The forward: prefix
5.4 ContentNegotiatingViewResolver
ContentNegotiatingViewResolver 并不自己处理视图,而是委托给其他的视图处理器。
example:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> <property name="mediaTypes"> <map> <entry key="atom" value="application/atom+xml"/> <entry key="html" value="text/html"/> <entry key="json" value="application/json"/> </map> </property> <property name="viewResolvers"> <list> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </list> </property> <property name="defaultViews"> <list> <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" /> </list> </property></bean><bean id="content" class="com.foo.samples.rest.SampleContentAtomView"/>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
6 使用闪存属性
闪存属性可以提供一个方法给请求去保存。
Spring MVC有两种主要的方式来支持闪存属性,FlashMap用来保存闪存属性当FlashMapManager用来保存,检索以及管理FlashMap的属性。
7 搭建URL
Spring MVC提供了一个机制去搭建编写URL - UrlComponentsBuilder和UriComponents
扩展并加密URL的例子:
UriComponents uriComponents = UriComponentsBuilder.fromUriString( "http://example.com/hotels/{hotel}/bookings/{booking}").build();URI uri = uriComponents.expand("42", "21").encode().toUri();
UriComponents是不可变的,并且expand( )和encode( )执行将返回一个新的实例。
UriComponents uriComponents = UriComponentsBuilder.newInstance() .scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build() .expand("42", "21") .encode();
8 本地化
9 使用主题
10 Spring的文件上传
11 处理错误
12 web安全
13 约定优先配置原则
13.1 控制器的匹配
13.2 模型的ModelMap
14 ETag支持
15 基于代码的servlet容器配置
16 SpringMVC的配置
16.1 启动MVC的JAVA配置或XML命名空间
基于@Configuration的配置
@Configuration@EnableWebMvcpublic class WebConfig {}
<?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 /></beans>
16.2 自定义提供的配置
@Configuration@EnableWebMvcpublic class WebConfig extends WebMvcConfigurerAdapter { @Override protected void addFormatters(FormatterRegistry registry) { } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { }}
16.3 拦截器配置
通过java配置:
@Configuration@EnableWebMvcpublic class WebConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LocaleInterceptor()); registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**"); registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*"); }}
<mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" /> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/admin/**"/> <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor" /> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/secure/*"/> <bean class="org.example.SecurityInterceptor" /> </mvc:interceptor></mvc:interceptors>
16.4 内容导航
@Configuration@EnableWebMvcpublic class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.favorPathExtension(false).favorParameter(true); }}
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" /><bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <property name="favorPathExtension" value="false" /> <property name="favorParameter" value="true" /> <property name="mediaTypes" > <value> json=application/json xml=application/xml </value> </property></bean>
16.5 View Controllers
@Configuration@EnableWebMvcpublic class WebConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("home"); }}
<mvc:view-controller path="/" view-name="home"/>
16.6 View Resolvers
@Configuration@EnableWebMvcpublic class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.enableContentNegotiation(new MappingJackson2JsonView()); registry.jsp(); }}
<mvc:view-resolvers> <mvc:content-negotiation> <mvc:default-views> <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" /> </mvc:default-views> </mvc:content-negotiation> <mvc:jsp /></mvc:view-resolvers>
<mvc:view-resolvers> <mvc:content-negotiation> <mvc:default-views> <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" /> </mvc:default-views> </mvc:content-negotiation> <mvc:freemarker cache="false" /></mvc:view-resolvers><mvc:freemarker-configurer> <mvc:template-loader-path location="/freemarker" /></mvc:freemarker-configurer>
16.7 静态资源服务
<mvc:resources mapping="/resources/**" location="/public-resources/"/>
<mvc:resources mapping="/resources/**" location="/public-resources/"> <mvc:resource-chain> <mvc:resource-cache /> <mvc:resolvers> <mvc:version-resolver> <mvc:content-version-strategy patterns="/**"/> </mvc:version-resolver> </mvc:resolvers> </mvc:resource-chain></mvc:resources>
16.8 默认的servlet
<mvc:default-servlet-handler/>
<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/>
16.9 路径匹配
<mvc:annotation-driven> <mvc:path-matching suffix-pattern="true" trailing-slash="false" registered-suffixes-only="true" path-helper="pathHelper" path-matcher="pathMatcher" /></mvc:annotation-driven><bean id="pathHelper" class="org.example.app.MyPathHelper" /><bean id="pathMatcher" class="org.example.app.MyPathMatcher" />
16.10 高级自定义配置
16.11 自定义MVC命名控制