一、
概述
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
!!!