引言
在java Web開發(fā)過(guò)程我們?cè)缫蚜?xí)慣了Web.xml的繁雜的配置,我們從生產(chǎn)低下的”jsp+Servlet”時(shí)期進(jìn)入到SSH在步入到較為先進(jìn)的SSM時(shí)期,仍然沒有逃脫Web.xml.令人驚喜的是Servlet3.0 帶來(lái)了全新的配置方式允許我們可以使用編程的方式實(shí)現(xiàn)接口去進(jìn)行ServletContext配置.正如Spring官方文檔里說(shuō)的,這和基于(或者可能是結(jié)合)傳統(tǒng)的web.xml方法恰恰相反。我覺得應(yīng)該是一個(gè)令人驚喜的改變.而后在SpringBoot給我們帶來(lái)了AutoConfiguration,極大的解放了生產(chǎn)力.
今天我要說(shuō)的不是使用SpringBoo干掉Web.xml,而是在SSM框架中干掉Web.xml,請(qǐng)聽我細(xì)細(xì)道來(lái).
可恨的Web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-config.xml</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>
上面就是我們 在使用SSM框架開發(fā)所要配置的Web.xml文件,是不是挺繁瑣的.
現(xiàn)如今,YAML,Properties,JSON等文件以簡(jiǎn)潔和可讀性更高的優(yōu)點(diǎn)不禁讓我們趨之若鶩,甘愿拜倒在他們的石榴裙下.當(dāng)然,我并不是說(shuō)XML文件應(yīng)該被淘汰,它在有些地方有著不可替代的作用;而是我覺得配置文件就應(yīng)該簡(jiǎn)潔,讓人一眼就知道一段代碼是干啥的,不需要解讀標(biāo)簽的含義就能夠全部理解.說(shuō)了這么多那我們?cè)撊绾翁娲?請(qǐng)往下看.
WebApplicationInitializer簡(jiǎn)介
查看源碼得知,WebApplicationInitializer就是一個(gè)接口,其結(jié)構(gòu)很簡(jiǎn)單,就包含一個(gè)方法onStartup(),結(jié)構(gòu)如下:
public interface WebApplicationInitializer {
/**
* Configure the given {@link ServletContext} with any servlets, filters, listeners
* context-params and attributes necessary for initializing this web application. See
* examples {@linkplain WebApplicationInitializer above}.
* @param servletContext the {@code ServletContext} to initialize
* @throws ServletException if any call against the given {@code ServletContext}
* throws a {@code ServletException}
*/
void onStartup(ServletContext servletContext) throws ServletException;
}
所有的初始化類都實(shí)現(xiàn)了此接口,直接或間接實(shí)現(xiàn)此接口的類有:AbstractContextLoaderInitializer和AbstractReactiveWebInitializer這兩個(gè)抽象類,AbstractReactiveWebInitializer作用是為了方便WebApplicationInitializer 將一個(gè)ClassLoaderListener 注冊(cè)到Servlet 上下文中.而AbstractReactiveWebInitializer的作用是為響應(yīng)式Web 程序提供一個(gè)初始化基類.由此可見,我們使用JavaConfig模式不僅可以配置傳統(tǒng)的Web應(yīng)用,也可以很好的配置響應(yīng)式Web應(yīng)用.下面讓我們先來(lái)看看該如何利用javaConfig和Web.xml進(jìn)行.下面是對(duì)應(yīng)DispatcherServlet注冊(cè)邏輯, WebApplicationInitializer實(shí)現(xiàn)如下:
public class MyWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(appContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
如果對(duì)于上述的配置有其他的考慮,也可以繼承org.springframework.web.servlet.support.AbstractDispatcherServletInitializer,此類的父類就是我們上面提到的AbstractContextLoaderInitializer并在其基礎(chǔ)上進(jìn)行了擴(kuò)展.繼承此類Spring將會(huì)自動(dòng)為你完成配置.
我們看到這種方式更簡(jiǎn)單,也更簡(jiǎn)潔.不用擔(dān)心處理初始化參數(shù),只是普通的JavaBean風(fēng)格的屬性和構(gòu)造函數(shù)參數(shù)。
在官方文檔中提到,Spring的大多數(shù)組件已經(jīng)進(jìn)行了更新優(yōu)化,用來(lái)支持上述風(fēng)格的注冊(cè)工作.當(dāng)你在使用Spring或者源碼的時(shí)候,你會(huì)驚奇的發(fā)現(xiàn)DispatcherServlet , FrameworkServlet , ContextLoaderListener和DelegatingFilterProxy現(xiàn)在都支持使用構(gòu)造函數(shù)進(jìn)行參數(shù)注冊(cè).現(xiàn)在如果我們,們使用Servlet3.0,我們完全的干掉Web.xml,用代碼將ServletContext API的進(jìn)行實(shí)現(xiàn)和擴(kuò)展,以配置init-params, context-params 等必要的參數(shù).
既然有了這么多的支持,我們又該如何完美且不失優(yōu)雅的干掉Web.xml呢?請(qǐng)看代碼:
public class MyWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(DispatcherConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
同樣你也可以不直接實(shí)現(xiàn)WebApplicationInitializer 而使用繼承org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer的 方式去配置你的應(yīng)用程序 .這所有的操做都是由spring自動(dòng)發(fā)現(xiàn)并配置----所以你可以用你自己獨(dú)特的方式去開發(fā)Spring應(yīng)用,不需要過(guò)多的考慮其他的事情.
更為神奇的是,Spring為我們提供了一種機(jī)制以確保確保Servlet容器的初始化順序.因?yàn)槲覀兛梢赃x擇實(shí)現(xiàn)此接口的同時(shí)選擇使用@Order或者是實(shí)現(xiàn)Order接口,但是會(huì)造成這樣初始化在調(diào)用前進(jìn)行.雖然這種方式在典型的Web應(yīng)用中很少使用.但是Spring也為我們考慮到了.
討論
Web.xml并不是和WebApplicationInitializer相互排斥,恰恰相反他們可以協(xié)同工作,例如,在web.xml中可以注冊(cè)一個(gè)servlet和在WebApplicationInitializer可以注冊(cè)另一個(gè)。 一個(gè)初始化甚至可以在web.xml通過(guò)方法修改改變注冊(cè)行為,如ServletContext.getServletRegistration(String) 。 但是如果WEB-INF/web.xml存在于應(yīng)用程序,它的version屬性必須被設(shè)置為“3.0”或更大,否則ServletContainerInitializer的引導(dǎo)將會(huì)被servlet容器忽略。
上面我說(shuō)到三種常見配置文件可以代替Web.xml,基于上面javaConfig思路,我們可以自定義配置文件,將那些未來(lái)可能會(huì)發(fā)生變動(dòng)放到配置文件(這里的文件特指:YAML,Properties,JSON中的任意一個(gè))里,那些不會(huì)發(fā)生變動(dòng)或者變動(dòng)很少,使用java硬編碼的方式以代碼的方式進(jìn)行配置.
說(shuō)明
本文章為作者讀書筆記及感悟,其中參考了《spring5核心原理與30個(gè)類手寫實(shí)戰(zhàn)》以及互聯(lián)網(wǎng)上的內(nèi)容。如有錯(cuò)誤,請(qǐng)?jiān)u論或者私聊我,歡迎探討技術(shù)問題 。即將畢業(yè),在準(zhǔn)備找工作,有朋友想給我介紹的,歡迎添加微信:sllbiao。