?
一、???????
概述
Spring MVC
的開發是基于
action-servlet.xml
進行配置,但不支持開發模式下進行動態的配置文件載入。本文主要是介紹如何修改
Spring
的源代碼,使
Spring
支持動態的配置文件更新,讓開發變得更加簡單。
二、???????
實現
action-servlet.xml
動態載入
???
Spring
提取配置文件的思路
:每次
Spring MVC
會在使用前將
XML
文件載入內存中,并生成映射類的實例,放在
Mapping Map
里。然后判斷每個請求,如果有其
URL
所對應的映射,則返回其對應的
Action
實例。
???
修改思路
:將每次得到請求時,讓程序重新載入
xml
文件,并實例化其映射,然后放入
Mapping Map
中。
1、????????????
首先是
FrameworkServlet
,他是
DispatcherServlet
的基類。
XML
在載入內存后,放在一個叫
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
的時候,會重新載入
XML
文件一次。
?
2、????????????
修改
DispatcherServlet
,這個
Servlet
是處理所有請求的入口。找到
getHandler()
這個方法,他負責找到相應的
Action
,并返回其實例。將代碼中的
?????? 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
是將強制轉換
HandlerMapping
時,用子類代替父類,因為子類提供了一個重新初始化的方法
initApplicationContext()
,調用該方法可以重新載入
WebApplicationContext
,
并刷新
Mapping Map
。
2)??????
initHandlerMappings()
是
DispatcherServlet
初始化
Mapping
的一個方法。在生成
WebApplicationContext
時,程序還會把放在
ApplicationObjectSupport.applicationContext
保存,因此需要重新初始化一次。
?
3
、修改
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
類中的
registerHandler()
方法,它的作用是注冊
Mapping
,去掉重復性校驗,將下面幾行代碼注釋掉。
??? if (mappedHandler != null) {
??????? throw new ApplicationContextException(
?????????????? "Cannot map handler [" + handler + "] to URL path [" + urlPath +
?????????????? "]: there's already handler [" + mappedHandler + "] mapped");
??? }
?
?
三、實現
applicationContext.xml
的動態載入
???
Spring
實現思路:
applicationContext.xml
是
Spring
默認的配置文件,它利用配置
ContextLoaderListener
的方式,在應用載入時啟動,并將
applicationContext.xml
載入內存中,放在
ServletContext
的
Attribute
中,保存的方式是一個
WebApplicationContext
類。當每次調用類時,
beanFactory
會調用
WebApplicationContextUtils
中的方法
getWebApplicationContext()
,得到配置信息。
???
修改方法:
在
ContextLoaderListener
初始化
WebApplicationContext
時,會利用
ContextLoader
提供的方法
initWebApplicationContext()
進行初始化,我們只需要得到
Listener
的這個
ContextLoader
的實例,并重新調用一個初始化的方法就可以實現重新載入了。
???
修改步驟:
1
、找到
ContextLoaderListener
類的方法
contextInitialized()
,在
Context
初始化的時候將
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
時,程序會重新載入
applicationContext.xml
一次。
?
OK
!大功告成,
Enjoy your spring developing
!!!
?