javascript
Spring Boot(09)——使用SpringMVC
使用SpringMVC
使用SpringMVC最簡單的方法是在pom.xml中加入spring-boot-starter-web依賴,這樣Spring Boot的AutoConfiguration模塊將為我們自動進行SpringMVC的配置,創建好RequestMappingHandlerAdapter、RequestMappingHandlerMapping等,詳情可以參考org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration和DelegatingWebMvcConfiguration的源碼。
這個時候就可以定義如下這樣一個控制器,當請求/hello/json時將返回{"key1": "value1", "key2": "value2"}這樣一段JSON。當Classpath下存在jackson相關的Class時就會自動添加MappingJackson2HttpMessageConverter這樣一個HttpMessageConverter。關于默認添加的HttpMessageConverter可以參考WebMvcConfigurationSupport的addDefaultHttpMessageConverters()的源碼說明。
@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的自動注冊將由org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration配置,其默認啟用了Servlet的異步支持。
添加自定義的HttpMessageConverter
添加自定義的HttpMessageConverter比較方便的方法是通過org.springframework.boot.autoconfigure.http.HttpMessageConverters定義,它可以在指定使用默認的HttpMessageConverter的同時添加額外的HttpMessageConverter。下面的代碼中就指定了在使用默認的HttpMessageConverter的同時添加了一個自定義的CustomHttpMessageConverter。
@Configuration
public class MvcConfiguration {
}
HttpMessageConverters有幾個重載的構造方法,使用時可以參考對應的API文檔選擇合適的進行使用。
Spring Boot中擁有一個HttpMessageConvertersAutoConfiguration類,其會在未定義HttpMessageConverters類型的bean時,自動注冊一個HttpMessageConverters類型的bean,即會通過它來使用默認的HttpMessageConverter。此外,其會在自動注冊bean容器中定義的HttpMessageConverter,所以使用默認配置時也可以把需要注冊的HttpMessageConverter定義為bean容器中的一個bean。HttpMessageConvertersAutoConfiguration中擁有一個StringHttpMessageConverterConfiguration類,會在bean容器中未定義StringHttpMessageConverter類型的bean時自動定義一個,使用的字符集默認是UTF-8,可以通過在application.properties中通過spring.http.encoding.charset指定。
Converter和Formatter注冊
下面的代碼來自于WebMvcAutoConfigurationAdapter,從代碼中可以看出Spring Boot會自動注冊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);} }靜態資源的處理
Spring Boot默認會把Classpath下的/META-INF/resources/、/resources/、/static/和/public/映射為靜態資源路徑。靜態資源的相關配置由ResourceProperties類定義,對應的配置屬性前綴是spring.resources。如果不想使用默認的靜態資源位置,可以通過spring.resources.static-locations屬性進行自定義。如果不需要把那些路徑映射為靜態資源路徑,則可以設置spring.resources.addMappings的值為true。靜態資源默認會映射為/**,即如果在/resources下擁有一個index.html文件,則可以通過/index.html請求到。可以通過spring.mvc.static-path-pattern指定靜態資源映射的路徑,下面代碼就指定了靜態資源的映射路徑為/resources/**,/**對應的才是真實的靜態資源的路徑,所以此時如果需要請求/resources路徑下的index.html文件,需要通過/resources/index.html才能請求到。
spring.mvc.static-path-pattern=/resources/**更多配置信息可以參考ResourceProperties的源代碼。
瀏覽器圖標文件
Spring Boot默認會在配置的靜態資源根路徑下或者是Classpath根路徑下尋找favicon.ico文件。
Content Negotiation
Spring Boot默認是不支持后綴名匹配的,即請求/report.json時是不會被映射到@GetMapping("/report")的。可以通過如下方式指定支持后綴名匹配。
spring.mvc.contentnegotiation.favor-path-extension=true關于Content Negotiation的可選配置都定義在WebMvcProperties$Contentnegotiation.class中。可以通過spring.mvc.contentnegotiation.favor-parameter=true指定支持通過查詢參數指定請求類型,默認的查詢請求類型查詢參數是format,需要自定義時可以通過spring.mvc.contentnegotiation.parameter-name屬性來指定。
更換內置Web容器
當引入了spring-boot-starter-web后默認使用的Web容器是tomcat,Spring Boot也提供了一些其它的實現,比如jetty,當需要使用jetty實現時需要先把tomcat實現從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>不啟動Web容器
默認情況下你添加了spring-boot-starter-web后Spring Boot會自動幫你啟動Web容器,如果不期望啟動Web容器可以配置spring.main.web-application-type=none。
內置容器可配置的屬性
當使用內置的Web容器時可以針對內置容器進行一些自定義的配置,這些配置將由org.springframework.boot.autoconfigure.web.ServerProperties進行接收。常用的自定義配置包括server.port指定監聽端口,server.servlet.contextPath指定contextPath,還可以通過server.servlet.session.*指定session相關的定義信息。下面的配置中定義了監聽端口是8081,contextPath是/app,DispatcherServlet匹配的映射路徑是/web/*,session的超時時間三30分鐘,當指定session的超時時間時,如果不指定時間單位,默認單位是秒。
server.port=8081 server.servlet.context-path=/app server.servlet.path=/web server.servlet.session.timeout=30m更多關于內置Web容器可配置的信息可以參考org.springframework.boot.autoconfigure.web.ServerProperties的API或源碼。
注冊Servlet/Filter等
根據Servlet3的規范,Classpath下的@WebServlet、@WebFilter和@WebListener標注的Class會被Web容器自動檢測到,并進行注冊。但是使用Spring Boot內置的Web容器時,它們是不會自動被檢測到并被注冊的。有兩種方式可以在使用內置的Web容器時能夠讓它們進行自動注冊。
定義為Spring bean
Spring Bean容器中的定義的Filter/Servlet和Listener會被自動注冊,Filter會攔截所有請求,而Servlet的請求路徑將是bean名稱,然后需要以/結尾,比如下面代碼中定義的Servlet的請求路徑是/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對應的bean名稱是servlet/hello,則其對應的請求路徑為/servlet/hello/。
需要注意的是當bean容器中只定義了一個Servlet時,該Servlet的映射路徑不是bean名稱,而是/。
通過@ServletComponentScan掃描
第二種方式是還是在Class上標注@WebServlet、@WebFilter或@WebListener注解,然后在某個@Configuration Class上標注@ServletComponentScan,可以通過它的value、basePackages或basePackageClasses三者之一來指定需要掃描的包,如果都不指定,默認會以標注的Class所在的包作為根包進行掃描。下面的代碼中先是定義了一個Servlet,使用了@WebServlet標注,并指定了映射的路徑為/servlet/test。然后在Application類上使用了@ServletComponentScan標注。Spring Boot將會掃描到TestServlet類,并把它注冊為一個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,同時又希望可以訪問到Spring bean,則可以同時把Servlet定義為Spring的一個bean。下面的代碼中定義的Servlet可以通過/servlet/hello訪問,同時它是一個Spring bean,被注入了ApplicationContext,在訪問它時會輸出所有定義的bean的名稱。該Servlet同時也可以通過/hello/訪問到,因為作為一個HttpServlet類型的bean,它也會被注冊為一個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所寫)
總結
以上是生活随笔為你收集整理的Spring Boot(09)——使用SpringMVC的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BZOJ1941:[SDOI2010]H
- 下一篇: Spring Cloud综合实战 - 基