?
一、???????
概述
Spring MVC
的開發(fā)是基于
action-servlet.xml
進(jìn)行配置,但不支持開發(fā)模式下進(jìn)行動(dòng)態(tài)的配置文件載入。本文主要是介紹如何修改
Spring
的源代碼,使
Spring
支持動(dòng)態(tài)的配置文件更新,讓開發(fā)變得更加簡(jiǎn)單。
二、???????
實(shí)現(xiàn)
action-servlet.xml
動(dòng)態(tài)載入
???
Spring
提取配置文件的思路
:每次
Spring MVC
會(huì)在使用前將
XML
文件載入內(nèi)存中,并生成映射類的實(shí)例,放在
Mapping Map
里。然后判斷每個(gè)請(qǐng)求,如果有其
URL
所對(duì)應(yīng)的映射,則返回其對(duì)應(yīng)的
Action
實(shí)例。
???
修改思路
:將每次得到請(qǐng)求時(shí),讓程序重新載入
xml
文件,并實(shí)例化其映射,然后放入
Mapping Map
中。
1、????????????
首先是
FrameworkServlet
,他是
DispatcherServlet
的基類。
XML
在載入內(nèi)存后,放在一個(gè)叫
WebApplicationContext
的類中。找到
getWebApplicationContext()
方法,加入以下代碼:
??????
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils
????????????? .instantiateClass(getContextClass());
?????? wac.setParent(WebApplicationContextUtils
????????????? .getWebApplicationContext(getServletContext()));
?????? wac.setServletContext(getServletContext());
?????? wac.setNamespace(getNamespace());
??????
if
(getContextConfigLocation() !=
null
) {
?????????? wac
????????????????? .setConfigLocations(StringUtils
???????????????????????? .tokenizeToStringArray(
??????????????????????????????? getContextConfigLocation(),
???????????????????????????
??? ConfigurableWebApplicationContext.
CONFIG_LOCATION_DELIMITERS
));
?????? }
?????? wac.refresh();
??????
this
.
webApplicationContext
= wac;
這樣每次再讀取
WebApplicationContext
的時(shí)候,會(huì)重新載入
XML
文件一次。
?
2、????????????
修改
DispatcherServlet
,這個(gè)
Servlet
是處理所有請(qǐng)求的入口。找到
getHandler()
這個(gè)方法,他負(fù)責(zé)找到相應(yīng)的
Action
,并返回其實(shí)例。將代碼中的
?????? Iterator it = this.handlerMappings.iterator();
?????? while (it.hasNext()) {
?????????? HandlerMapping hm = (HandlerMapping) it.next();
?????????? if (logger.isDebugEnabled()) {
????????????? logger.debug("Testing handler map [" + hm? + "] in DispatcherServlet with name '" +
???????????????????? getServletName() + "'");
?????????? }
?????????? handler = hm.getHandler(request);
?????????? if (handler != null) {
????????????? if (cache) {
????????????????? request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
????????????? }
????????????? return handler;
?????????? }
?????? }
改為
?????? initHandlerMappings();
??????
?????? Iterator it =
this
.
handlerMappings
.iterator();
??????
while
(it.hasNext()) {
?????????? BeanNameUrlHandlerMapping hm = (BeanNameUrlHandlerMapping) it.next();
??????????
if
(
logger
.isDebugEnabled()) {
?????????????
logger
.debug(
"Testing handler map ["
+ hm? +
"] in DispatcherServlet with name '"
+
???????????????????? getServletName() +
"'"
);
?????????? }
?????????? hm.initApplicationContext();
?????????? handler = hm.getHandler(request);
??????????
if
(handler !=
null
) {
?????????????
if
(cache) {
????????????????? request.setAttribute(
HANDLER_EXECUTION_CHAIN_ATTRIBUTE
, handler);
????????????? }
?????????????
return
handler;
?????????? }
?????? }
注解:
1)??
其中
BeanNameUrlHandlerMapping
是將強(qiáng)制轉(zhuǎn)換
HandlerMapping
時(shí),用子類代替父類,因?yàn)樽宇愄峁┝艘粋€(gè)重新初始化的方法
initApplicationContext()
,調(diào)用該方法可以重新載入
WebApplicationContext
,
并刷新
Mapping Map
。
2)??????
initHandlerMappings()
是
DispatcherServlet
初始化
Mapping
的一個(gè)方法。在生成
WebApplicationContext
時(shí),程序還會(huì)把放在
ApplicationObjectSupport.applicationContext
保存,因此需要重新初始化一次。
?
3
、修改
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
類中的
registerHandler()
方法,它的作用是注冊(cè)
Mapping
,去掉重復(fù)性校驗(yàn),將下面幾行代碼注釋掉。
??? if (mappedHandler != null) {
??????? throw new ApplicationContextException(
?????????????? "Cannot map handler [" + handler + "] to URL path [" + urlPath +
?????????????? "]: there's already handler [" + mappedHandler + "] mapped");
??? }
?
?
三、實(shí)現(xiàn)
applicationContext.xml
的動(dòng)態(tài)載入
???
Spring
實(shí)現(xiàn)思路:
applicationContext.xml
是
Spring
默認(rèn)的配置文件,它利用配置
ContextLoaderListener
的方式,在應(yīng)用載入時(shí)啟動(dòng),并將
applicationContext.xml
載入內(nèi)存中,放在
ServletContext
的
Attribute
中,保存的方式是一個(gè)
WebApplicationContext
類。當(dāng)每次調(diào)用類時(shí),
beanFactory
會(huì)調(diào)用
WebApplicationContextUtils
中的方法
getWebApplicationContext()
,得到配置信息。
???
修改方法:
在
ContextLoaderListener
初始化
WebApplicationContext
時(shí),會(huì)利用
ContextLoader
提供的方法
initWebApplicationContext()
進(jìn)行初始化,我們只需要得到
Listener
的這個(gè)
ContextLoader
的實(shí)例,并重新調(diào)用一個(gè)初始化的方法就可以實(shí)現(xiàn)重新載入了。
???
修改步驟:
1
、找到
ContextLoaderListener
類的方法
contextInitialized()
,在
Context
初始化的時(shí)候?qū)?/span>
ContextLoader
的引用放在
ServletContext
的
Attribute
中:
public
void
contextInitialized(ServletContextEvent event) {
??????
this
.
contextLoader
= createContextLoader();
??????
this
.
contextLoader
.initWebApplicationContext(event.getServletContext());
?????? event.getServletContext().setAttribute(
"ListenerContextLoader"
,
this
.
contextLoader
);
}
注:
"ListenerContextLoader"
是自定義的名稱,可以任意修改。
?
3、????????????
找到
WebApplicationContextUtils
類的方法
getWebApplicationContext()
,修改第一行代碼:
Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
改為:
?????? Object attr =
null
;
?????? ContextLoader cl = (ContextLoader) sc
????????????? .getAttribute(
"ListenerContextLoader"
);
??????
if
(cl !=
null
)
?????????? attr = cl.initWebApplicationContext(sc);
這樣,在每次獲取
WebApplicationContext
時(shí),程序會(huì)重新載入
applicationContext.xml
一次。
?
OK
!大功告成,
Enjoy your spring developing
?。。?/span>
?