javascript
Spring Boot(09)——使用SpringMVC
使用SpringMVC
使用SpringMVC最簡(jiǎn)單的方法是在pom.xml中加入spring-boot-starter-web依賴,這樣Spring Boot的AutoConfiguration模塊將為我們自動(dòng)進(jìn)行SpringMVC的配置,創(chuàng)建好RequestMappingHandlerAdapter、RequestMappingHandlerMapping等,詳情可以參考o(jì)rg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration和DelegatingWebMvcConfiguration的源碼。
這個(gè)時(shí)候就可以定義如下這樣一個(gè)控制器,當(dāng)請(qǐng)求/hello/json時(shí)將返回{"key1": "value1", "key2": "value2"}這樣一段JSON。當(dāng)Classpath下存在jackson相關(guān)的Class時(shí)就會(huì)自動(dòng)添加MappingJackson2HttpMessageConverter這樣一個(gè)HttpMessageConverter。關(guān)于默認(rèn)添加的HttpMessageConverter可以參考WebMvcConfigurationSupport的addDefaultHttpMessageConverters()的源碼說(shuō)明。
@RestController @RequestMapping("hello") public class HelloController {@GetMapping("json")public Object jsonResult() {Map<String, Object> map = new HashMap<>();map.put("key1", "value1");map.put("key2", "value2");return map;}}DispatcherServlet的自動(dòng)注冊(cè)將由org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration配置,其默認(rèn)啟用了Servlet的異步支持。
添加自定義的HttpMessageConverter
添加自定義的HttpMessageConverter比較方便的方法是通過(guò)org.springframework.boot.autoconfigure.http.HttpMessageConverters定義,它可以在指定使用默認(rèn)的HttpMessageConverter的同時(shí)添加額外的HttpMessageConverter。下面的代碼中就指定了在使用默認(rèn)的HttpMessageConverter的同時(shí)添加了一個(gè)自定義的CustomHttpMessageConverter。
@Configuration
public class MvcConfiguration {
}
HttpMessageConverters有幾個(gè)重載的構(gòu)造方法,使用時(shí)可以參考對(duì)應(yīng)的API文檔選擇合適的進(jìn)行使用。
Spring Boot中擁有一個(gè)HttpMessageConvertersAutoConfiguration類,其會(huì)在未定義HttpMessageConverters類型的bean時(shí),自動(dòng)注冊(cè)一個(gè)HttpMessageConverters類型的bean,即會(huì)通過(guò)它來(lái)使用默認(rèn)的HttpMessageConverter。此外,其會(huì)在自動(dòng)注冊(cè)bean容器中定義的HttpMessageConverter,所以使用默認(rèn)配置時(shí)也可以把需要注冊(cè)的HttpMessageConverter定義為bean容器中的一個(gè)bean。HttpMessageConvertersAutoConfiguration中擁有一個(gè)StringHttpMessageConverterConfiguration類,會(huì)在bean容器中未定義StringHttpMessageConverter類型的bean時(shí)自動(dòng)定義一個(gè),使用的字符集默認(rèn)是UTF-8,可以通過(guò)在application.properties中通過(guò)spring.http.encoding.charset指定。
Converter和Formatter注冊(cè)
下面的代碼來(lái)自于WebMvcAutoConfigurationAdapter,從代碼中可以看出Spring Boot會(huì)自動(dòng)注冊(cè)bean容器中定義的Converter和Formatter。
@Override public void addFormatters(FormatterRegistry registry) {for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {registry.addConverter(converter);}for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {registry.addConverter(converter);}for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {registry.addFormatter(formatter);} }靜態(tài)資源的處理
Spring Boot默認(rèn)會(huì)把Classpath下的/META-INF/resources/、/resources/、/static/和/public/映射為靜態(tài)資源路徑。靜態(tài)資源的相關(guān)配置由ResourceProperties類定義,對(duì)應(yīng)的配置屬性前綴是spring.resources。如果不想使用默認(rèn)的靜態(tài)資源位置,可以通過(guò)spring.resources.static-locations屬性進(jìn)行自定義。如果不需要把那些路徑映射為靜態(tài)資源路徑,則可以設(shè)置spring.resources.addMappings的值為true。靜態(tài)資源默認(rèn)會(huì)映射為/**,即如果在/resources下?lián)碛幸粋€(gè)index.html文件,則可以通過(guò)/index.html請(qǐng)求到。可以通過(guò)spring.mvc.static-path-pattern指定靜態(tài)資源映射的路徑,下面代碼就指定了靜態(tài)資源的映射路徑為/resources/**,/**對(duì)應(yīng)的才是真實(shí)的靜態(tài)資源的路徑,所以此時(shí)如果需要請(qǐng)求/resources路徑下的index.html文件,需要通過(guò)/resources/index.html才能請(qǐng)求到。
spring.mvc.static-path-pattern=/resources/**更多配置信息可以參考ResourceProperties的源代碼。
瀏覽器圖標(biāo)文件
Spring Boot默認(rèn)會(huì)在配置的靜態(tài)資源根路徑下或者是Classpath根路徑下尋找favicon.ico文件。
Content Negotiation
Spring Boot默認(rèn)是不支持后綴名匹配的,即請(qǐng)求/report.json時(shí)是不會(huì)被映射到@GetMapping("/report")的??梢酝ㄟ^(guò)如下方式指定支持后綴名匹配。
spring.mvc.contentnegotiation.favor-path-extension=true關(guān)于Content Negotiation的可選配置都定義在WebMvcProperties$Contentnegotiation.class中??梢酝ㄟ^(guò)spring.mvc.contentnegotiation.favor-parameter=true指定支持通過(guò)查詢參數(shù)指定請(qǐng)求類型,默認(rèn)的查詢請(qǐng)求類型查詢參數(shù)是format,需要自定義時(shí)可以通過(guò)spring.mvc.contentnegotiation.parameter-name屬性來(lái)指定。
更換內(nèi)置Web容器
當(dāng)引入了spring-boot-starter-web后默認(rèn)使用的Web容器是tomcat,Spring Boot也提供了一些其它的實(shí)現(xiàn),比如jetty,當(dāng)需要使用jetty實(shí)現(xiàn)時(shí)需要先把tomcat實(shí)現(xiàn)從spring-boot-starter-web中排除,再添加spring-boot-starter-jetty依賴。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><!-- Exclude the Tomcat dependency --><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions> </dependency> <!-- Use Jetty instead --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId> </dependency>不啟動(dòng)Web容器
默認(rèn)情況下你添加了spring-boot-starter-web后Spring Boot會(huì)自動(dòng)幫你啟動(dòng)Web容器,如果不期望啟動(dòng)Web容器可以配置spring.main.web-application-type=none。
內(nèi)置容器可配置的屬性
當(dāng)使用內(nèi)置的Web容器時(shí)可以針對(duì)內(nèi)置容器進(jìn)行一些自定義的配置,這些配置將由org.springframework.boot.autoconfigure.web.ServerProperties進(jìn)行接收。常用的自定義配置包括server.port指定監(jiān)聽(tīng)端口,server.servlet.contextPath指定contextPath,還可以通過(guò)server.servlet.session.*指定session相關(guān)的定義信息。下面的配置中定義了監(jiān)聽(tīng)端口是8081,contextPath是/app,DispatcherServlet匹配的映射路徑是/web/*,session的超時(shí)時(shí)間三30分鐘,當(dāng)指定session的超時(shí)時(shí)間時(shí),如果不指定時(shí)間單位,默認(rèn)單位是秒。
server.port=8081 server.servlet.context-path=/app server.servlet.path=/web server.servlet.session.timeout=30m更多關(guān)于內(nèi)置Web容器可配置的信息可以參考o(jì)rg.springframework.boot.autoconfigure.web.ServerProperties的API或源碼。
注冊(cè)Servlet/Filter等
根據(jù)Servlet3的規(guī)范,Classpath下的@WebServlet、@WebFilter和@WebListener標(biāo)注的Class會(huì)被Web容器自動(dòng)檢測(cè)到,并進(jìn)行注冊(cè)。但是使用Spring Boot內(nèi)置的Web容器時(shí),它們是不會(huì)自動(dòng)被檢測(cè)到并被注冊(cè)的。有兩種方式可以在使用內(nèi)置的Web容器時(shí)能夠讓它們進(jìn)行自動(dòng)注冊(cè)。
定義為Spring bean
Spring Bean容器中的定義的Filter/Servlet和Listener會(huì)被自動(dòng)注冊(cè),Filter會(huì)攔截所有請(qǐng)求,而Servlet的請(qǐng)求路徑將是bean名稱,然后需要以/結(jié)尾,比如下面代碼中定義的Servlet的請(qǐng)求路徑是/hello/。
@Component("hello") public class HelloServlet extends HttpServlet {/*** */private static final long serialVersionUID = 8345578389259773375L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {PrintWriter writer = resp.getWriter();writer.write("hello");writer.flush();}}如果上面的Servlet對(duì)應(yīng)的bean名稱是servlet/hello,則其對(duì)應(yīng)的請(qǐng)求路徑為/servlet/hello/。
需要注意的是當(dāng)bean容器中只定義了一個(gè)Servlet時(shí),該Servlet的映射路徑不是bean名稱,而是/。
通過(guò)@ServletComponentScan掃描
第二種方式是還是在Class上標(biāo)注@WebServlet、@WebFilter或@WebListener注解,然后在某個(gè)@Configuration Class上標(biāo)注@ServletComponentScan,可以通過(guò)它的value、basePackages或basePackageClasses三者之一來(lái)指定需要掃描的包,如果都不指定,默認(rèn)會(huì)以標(biāo)注的Class所在的包作為根包進(jìn)行掃描。下面的代碼中先是定義了一個(gè)Servlet,使用了@WebServlet標(biāo)注,并指定了映射的路徑為/servlet/test。然后在Application類上使用了@ServletComponentScan標(biāo)注。Spring Boot將會(huì)掃描到TestServlet類,并把它注冊(cè)為一個(gè)Servlet。
@WebServlet("/servlet/test") public class TestServlet extends HttpServlet {/*** */private static final long serialVersionUID = -2499437569852564816L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {PrintWriter writer = resp.getWriter();writer.write("Hello Servlet.");writer.flush();}} @SpringBootApplication @ServletComponentScan public class Application {public static void main(String[] args) {SpringApplication app = new SpringApplication(Application.class);app.setAddCommandLineProperties(false);app.run(args);}}如果需要使用@WebServlet的方式定義Servlet,同時(shí)又希望可以訪問(wèn)到Spring bean,則可以同時(shí)把Servlet定義為Spring的一個(gè)bean。下面的代碼中定義的Servlet可以通過(guò)/servlet/hello訪問(wèn),同時(shí)它是一個(gè)Spring bean,被注入了ApplicationContext,在訪問(wèn)它時(shí)會(huì)輸出所有定義的bean的名稱。該Servlet同時(shí)也可以通過(guò)/hello/訪問(wèn)到,因?yàn)樽鳛橐粋€(gè)HttpServlet類型的bean,它也會(huì)被注冊(cè)為一個(gè)Servlet,映射路徑為bean名稱。
@Component("hello") @WebServlet("/servlet/hello") public class HelloServlet extends HttpServlet {@Autowiredprivate ApplicationContext applicationContext;/*** */private static final long serialVersionUID = 8345578389259773375L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {PrintWriter writer = resp.getWriter();writer.println("bean names: ");for (String name : this.applicationContext.getBeanDefinitionNames()) {writer.println(name);}writer.flush();}}參考文檔
https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/html/boot-features-developing-web-applications.html#boot-features-embedded-container-servlets-filters-listeners
(注:本文是基于Spring Boot 2.0.3所寫(xiě))
總結(jié)
以上是生活随笔為你收集整理的Spring Boot(09)——使用SpringMVC的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: BZOJ1941:[SDOI2010]H
- 下一篇: ios UITableView顶部向