從Spring到SpringBoot構建WEB MVC核心配置詳解
理解Spring WEB MVC架構的演變
基礎Servlet架構
核心架構:前端控制器
Spring WEB MVC架構
認識Spring WEB MVC
傳統時代的Spring WEB MVC
怎麼講呢?就是很傳統的使用Spring Framework WEB MVC的方式,例如Bean配置在xml中,前端控制器配置在web.xml中等。
Maven 依賴
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.3.RELEASE</version> </dependency> </dependencies>
web.xml
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.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>
Spring.xml
<context:component-scan base-package="com.jimisun.web"/> <mvc:annotation-driven/> <bean id="viewResolver" 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>
Controller
@Controller
public class HelloSpringController {
@RequestMapping("")
public String index(){
return "index";
}
}
新時代Spring WEB MVC
所謂的新時代相比肯定是在傳統時代上的升級。提到升級我們首先會想到會提升使用的便捷程度以及效能。
- 基於註解驅動配置WEB MVC
- 自動裝配前端控制器
基於註解驅動配置WEB MVC
@Configuration
@EnableWebMvc
public class WebDispatcherServletConfigure implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resourceViewResolver = new InternalResourceViewResolver();
resourceViewResolver.setViewClass(JstlView.class);
resourceViewResolver.setPrefix("/WEB-INF/jsp/");
resourceViewResolver.setSuffix(".jsp");
return resourceViewResolver;
}
}
在Spring.xml檔案中則不用再配置處理器對映器,處理器介面卡和內部資源檢視解析器(JSP)。只需要保留<context:component-scan base-package="com.jimisun.web"/>
以便發現我們自定義的前端控制器配置類WebDispatcherServletConfigure
。因為@EnableWebMvc
註解為我們自動配置內部資源檢視解析器不符合我們自定義的要求,所以上述程式碼中我們使用@Bean
註解重新裝配了InternalResourceViewResolver
類。
基於註解驅動配置的重點是@EnableWebMvc
註解,此模組註解將匯入DelegatingWebMvcConfiguration
配置類,該類又集成了WebMvcConfigurationSupport
類。WebMvcConfigurationSupport
中則配置了一些關於 WEB MVC的相關Bean;例如:RequestMappingHandlerMapping
,ContentNegotiationManager
,HandlerMapping
,BeanNameUrlHandlerMapping
,RequestMappingHandlerAdapter
,ViewResolver
等等......
自動裝配前端控制器
原理 : 在web容器啟動時為提供給第三方元件機會做一些初始化的工作,例如註冊servlet或者filtes等,servlet規範中通過ServletContainerInitializer實現此功能。每個框架要使用ServletContainerInitializer就必須在對應的jar包的META-INF/services 目錄建立一個名為javax.servlet.ServletContainerInitializer的檔案,檔案內容指定具體的ServletContainerInitializer實現類,那麼,當web容器啟動時就會執行這個初始化器做一些元件內的初始化工作。一般伴隨著ServletContainerInitializer一起使用的還有HandlesTypes註解,通過HandlesTypes可以將感興趣的一些類注入到ServletContainerInitializerde的onStartup方法作為引數傳入。
我們從spring-web的依賴包中找到這個檔案,所以我們可以將前端控制器在Servlet容器啟動的時候將Spring WEB MVC的前端控制器新增入Servlet容器中,而不用再配置在web.xml檔案中。
我們可以看一下Spring的這個SpringServletContainerInitializer
類
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
......
}
我們可以看到Spring定義的這個類實現了Servlet3.0的ServletContainerInitializer
介面,並且標註了@HandlesTypes
;表示將實現WebApplicationInitializer.class
介面的類注入到ServletContainerInitializer
的回撥方法onStartup()
進行初始化。
我們從上面圖可以看到WebApplicationInitializer介面的實現類共有四個,所以就意味著我們自定義一個類實現這三個抽象方法其中的一個就行。下面是我們自定義的Servlet容器初始化器類。
public class DefaultAnnotationConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{DispathcherConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
getServletMappings()
方法用來返回前端控制器要攔截的路徑形式。其中getRootConfigClasses()
方法用於配置Spring Framework與Service,response有關的Bean;而getServletConfigClasses()
方法則用來配置Spring Framework中的WEB層相關的Bean。資料參考 :https://stackoverflow.com/questions/35258758/getservletconfigclasses-vs-getrootconfigclasses-when-extending-abstractannot
總的來說只要自定義一個類,實現WebApplicationInitializer介面即可;我們就可以不用在web.xml中配置前端控制器了。
SpringBoot簡化WEB MVC開發
Spring Boot從一下三個方面來簡化WEB開發;分別是“自動裝配”,“條件裝配”,“外部化配置” 三個方面來簡化WEB MVC開發。
自動裝配
SpringBooot對於WEB MVC的完全自動裝配主要體現在一下三個方面。
- 前端控制器自動裝配
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
- WEB MVC相關元件自動裝配
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
- WEB容器自動裝配
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
條件裝配
從完全自動裝配可以看到,SpringBoot替我們裝配了很多東西;但是在大部分情況下就是基於條件進行裝配的,既滿足某某條件才會進行裝配。還是來看一下前端控制器自定裝配類org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
}
從該類的註解上我們可以知道@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
該類的裝配順序,@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
在WEB容器裝配之後再進行裝配,@Configuration
可被管理的Bean,@ConditionalOnWebApplication(type = Type.SERVLET)
滿足條件為WEB型別應用,@ConditionalOnClass(DispatcherServlet.class)
滿足條件存在DispatcherServlet
類。
可以看到我們需要滿足兩個條件SpringBoot才會為我們裝配這個前端控制器DispatcherServlet
類。
外部化配置
關於WEB MVC的外部化配置我們需要了解一下WebMvcProperties
類和ResourceProperties類;分別對WebMvc和資原始檔進行配置。
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
.......
}
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
......
}
從註解上我們可以瞭解到,我們可以在application.properties
檔案或者application.yml
檔案中進行相關配置。
例如:SpringBoot在自動裝配WEB MVC元件的時候裝配了一個內部資源檢視解析器(JSP)
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
其中this.mvcProperties.getView().getPrefix()
就是從application.properties
檔案或者application.yml
檔案中獲取的值進行的操作而非硬編碼。
本章原始碼下載
我有一個微信公眾號,經常會分享一些Java技術相關的乾貨;如果你喜歡我的分享,可以用微信搜尋“Java團長”或者“javatuanzhang”關注。