Spring MVC起步
1. Spring MVC 的請求過程
2. 搭建Spring MVC
2.1 用Java來搭建Spring MVC(需要Servlet 3.0環境)
配置DispatcherServlet和ContextLoaderListener
package spittr.config;import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{ //對應ContextLoaderListener @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } //對於DispatcherServlet @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class };//指定配置類 } @Override protected String[] getServletMappings() { return new String[] { "/" };//將DispatcherServlet映射到"/" }}
AbstractAnnotationConfigDispatcherServletInitializer剖析
在Servlet3.0環境中,容器會在類路徑中尋找實現javax.servlet.ServletContainerInitializer介面的類,如果發現的話,就會用它來配置Servlet容器。Spring提供了這個介面的實現,名為SpringServletContainerInitializer,這個兩類反過來又會尋找實現WebApplicationInitializer的類並將配置的任務交給它們來完成。Spring3.2引入了一個便利的WebApplicationInitializer基礎實現,也就是AbstractAnnotationConfigDispatcherServletInitializer。因為我們的SpittrWebAppInitializer擴充了AbstractAnnotationConfigDispatcherServletInitializer(同時也實現了WebApplicationInitializer),因此當部署到Servlet3.0容器的時候,容器會自動探索它,並用它來配置Servlet上下文。
AbstractAnnotationConfigDispatcherServletInitializer會同時建立DispatcherServlet和ContextLoaderListener。getRootConfigClasses方法返回的帶有@Configuration註解的類將用來定義ContextLoaderListener應用內容相關的Bean。getServletConfigClasses方法返回的帶有@Configuration註解的類將用來定義DispatcherServlet應用內容相關的Bean。我們希望DispatcherServlet載入包含Web組件的Bean,如控制器、視圖解析器以及處理器映射,而ContextLoaderListener要載入應用中的其他Bean。這些Bean通常是驅動應用後端的中介層和資料層組件。
配置Spring MVC
package spittr.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.ViewResolver;import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.servlet.view.InternalResourceViewResolver;@Configuration @EnableWebMvc //啟用Spring MVC@ComponentScan("spittr.web") //啟用組件掃描public class WebConfig extends WebMvcConfigurerAdapter{ /** * 配置JSP視圖解析器 * @return */ @Bean public ViewResolver viewResolver(){ InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/view/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true); return resolver; } /** * 配置靜態資源的處理,要求DispatcherServlet將對靜態資源的請求轉寄到Servlet * 容器預設的Servlet上,而不是使用DispatcherServlet本身來處理此類請求 */ @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); }}
自訂DispatcherServlet配置
在SpittrWebAppInitializer中我們重寫了三個方法來配置DispatcherServlet,實際上有更多的方法可以進行重載從而實現對DispatcherServlet額外的配置。
方法之一就是customizeRegistration(),在AbstractAnnotationConfigDispatcherServletInitializer將DispatcherServlet註冊到Servlet容器之後,就會調用customizeRegistration(),並將Servlet註冊後得到的Registration.Dynamic傳遞進來。通過重載customizeRegistration()方法,我們可以對DispatcherServlet進行額外的配置。例如設定MultipartConfigElement:
@Override protected void customizeRegistration(Dynamic registration) { registration.setMultipartConfig( new MultipartConfigElement("/tmp/spittr/uploads")); }
至於這個設定到底是很忙我們將會在Spring上傳中介紹。
添加其他的Servlet和Filter
如果我們想在web容器中註冊其他的Servlet、Filter或者Listener,我們可以通過實現Spring的WebApplicationInitializer(我們前面說過Servlet3.0容器會自動尋找它的實作類別並用來配置servlet容器)。例如注入一個servlet和一個Filter:
import javax.servlet.ServletContext;import javax.servlet.ServletException;import org.springframework.web.WebApplicationInitializer;public class MyServletInitializer implements WebApplicationInitializer{ public void onStartup(ServletContext servletContext) throws ServletException { //註冊Servlet javax.servlet.ServletRegistration.Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class); myServlet.addMapping("/custom/**");//映射Servlet javax.servlet.FilterRegistration.Dynamic myFilter = servletContext.addFilter("myFilter", MyFilter.class); myFilter.addMappingForUrlPatterns(null, false, "/custom/**");//添加Filter的映射路徑 }}
如果你只是註冊Filter並將其映射到DispatcherServlet上的話,在AbstractAnnotationConfigDispatcherServletInitializer中還有一種便捷的方法——重載getServletFilters()方法:
@Override protected Filter[] getServletFilters() { return new Filter[] { new MyFilter() }; }
2.2 在web.xml中配置Spring MVC:
web.xml:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance http://www.springmodules.org/schema/cache/springmodules-cache.xsd http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- 指定Spring架構上下設定檔的位置,被ContextLoaderListener使用 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- 指定Spring MVC應用上下文設定檔的位置,如果不在這裡指定,會根據Servlet名字-context尋找設定檔 --> <param-name>contextConfigLocation</param-name> <param-value>classpath:dispatcherServlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
applicationContext.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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="test" class="spittr.web.Test" /> <context:component-scan base-package="spittr" /></beans>
dispatcherServlet-context.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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix">/WEB-INF/</property> <property name="suffix">.jsp</property> </bean></beans>
2.3 讓web.xml使用Java配置而不是XML配置
也即是說讓Spring MVC在啟動的時候,從帶有@Configuration註解的類上載入配置,我們需要告訴DispatcherServlet和ContextLoaderListener使用AnnotationConfigWebApplicationContext,這是一個WebApplicationContext的實作類別,它會載入Java配置類,而不是使用XML。要實現這種配置,我們只需要設定contextClass上下文參數以及DispatcherServlet的初始化參數。
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance http://www.springmodules.org/schema/cache/springmodules-cache.xsd http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- 使用Java配置 --> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <!-- 指定配置類 --> <param-value>spittr.config.RootConfig</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 使用Java配置 --> <init-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <!-- 指定配置類 --> <param-value>spittr.config.WebConfig</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
控制器
預留位置
Spring MVC允許我們在@RequestMapping路徑中添加預留位置。預留位置的名稱要用大括弧(“{”和”}”)括起來。路徑中的其他部分要與所處理的請求完全符合,但是預留位置部分是任意的值。
package spittr.web;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;public class Spittler { @RequestMapping("/{spittleId}") public String spittle( @PathVariable("spittleId") long spittleId){ System.out.println(spittleId); return "page"; }}
這樣,在請求路徑中,不管預留位置部分的值是什麼都會傳遞到處理器方法的spittleId參數中。
如果@PathVariable中沒有value屬性的話,它會假設預留位置的名稱與方法的參數名相同。
重新導向和前往指定的URL路徑
return "redirect:/spitter/";//重新導向return "forward:/spitter";//前往指定的URL路徑
表單檢驗
從Spring3.0開始,Spring MVC中提供了對Java 校正API的支援。
public class User { @NotNull @Size(min=5,max=16) private String userName; //非空,5-16個字元 @NotNull @Size(min=6, max=10) private String password;//非空,6-10個字元}
package spittr.web;import org.springframework.validation.Errors;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.RequestMapping;import spittr.entity.User;public class Spittler { @RequestMapping("/login") public String spittle( @Validated User user,//校正輸入 Errors errors){ if(errors.hasErrors()){ return "registerForm";//如果校正出現錯誤,則重新返回表單。 } return "redirect:/spitter/"; }}
Errors參數要緊跟在帶有校正註解的參數後面。