關於Spring配置文件的動態載入的修改 html
一 概述 java
Spring MVC 的開發是基於 action-servlet.xml 進行配置,但不支持開發模式下進行動態的配置文件載入。本文主要是介紹如何修改 Spring 的源代碼,使 Spring 支持動態的配置文件更新,讓開發變得更加簡單。 ios
2、 實現 action-servlet.xml 動態載入 web
Spring 提取配置文件的思路 :每次 Spring MVC 會在使用前將 XML 文件載入內存中,並生成映射類的實例,放在 Mapping Map 裏。而後判斷每一個請求,若是有其 URL 所對應的映射,則返回其對應的 Action 實例。 spring
修改思路 :將每次獲得請求時,讓程序從新載入 xml 文件,並實例化其映射,而後放入 Mapping Map 中。 app
一、 首先是 FrameworkServlet ,他是 DispatcherServlet 的基類。 XML 在載入內存後,放在一個叫 WebApplicationContext 的類中。找到 getWebApplicationContext() 方法,加入如下代碼: dom
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils ide
.instantiateClass(getContextClass()); flex
wac.setParent(WebApplicationContextUtils ui
.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 文件一次。
二、 修改 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");
}
3、實現 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" 是自定義的名稱,能夠任意修改。
三、 找到 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 !!!
另一篇
http://techdive.in/spring/spring-refresh-application-context
In this section, we will discuss about how to reload an application context in Spring.
In many scenarios, you may require to reload the entire application context in Spring. But how to do it? There is actually an easy way to do. Have a look at the following code.
Take a closer look at the last line, which type casts Application Context in to Configurable Application Context class part and then calls its refresh method. At this point in your application the entire application context (all the beans in Application-context.xml) will be reloaded or recreated.
This is the simple way to reload the context without restarting the web server.
再一篇
public interface MonitorMBean extends Serializable {
String reload();
}
public class MonitorMBeanImpl implements MonitorMBean {
/**
* The MBean implementation for reloading method
*
* */
public String reload() {
//StandaloneSever is the class whic has the spring application context
StandaloneServer.reload();
return "Successfully reloaded the etl context";
}
}
We have a POJO based bean called monitorBeanLocal.<!-- Starting mbean server -->
<bean id="mbeanServer" class="java.lang.management.ManagementFactory" factory-method="getPlatformMBeanServer"/>
Now we expose our POJO to be a MBean<!-- monitor jmx mbean for the standalone server -->
<bean id="monitorBeanLocal" class="hsenidmobile.control.impl.MonitorMBeanImpl" depends-on="mbeanServer"/>
Now lets have a RMI server connector<!--Expose out monitor bean as jmx managed bean-->
<bean id="monitorMBean" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=monitorBean" value-ref="monitorBeanLocal"/>
</map>
</property>
<property name="server" ref="mbeanServer"/>
</bean>
Of course we need the RMI registry also.<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
<property name="port" value="1098"/>
</bean>
Thats all about the JMX part. But for our testing purpose I have a bean called WhoAmI<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry">
<property name="objectName" value="connector:name=rmi"/>
<property name="serviceUrl"
value="service:jmx:rmi://127.0.0.1/jndi/rmi://127.0.01:1098/server"/>
<property name="environment">
<props>
<prop key="jmx.remote.jndi.rebind">true</prop>
</props>
</property>
</bean>
<!-- Sample bean to see how this is reloaded -->
<bean id="whoAmI" class="hsenidmobile.control.domain.WhoAmI">
<property name="myName" value="JK"/>
</bean>
public class WhoAmI {Cool, now lets go through our main server class.
private String myName;
public void setMyName(String myName) {
this.myName = myName;
}
public void printMyName(){
System.out.println("My Name is now " + myName);
}
}
public class StandaloneServer {So we simply start the spring application there. Of course you can also see the simple method reload which is called by our monitor bean. The only different you would have noticed it, I use AbstractApplicationContext instead of ApplicationContext since it has the additional methods for our requirements.
private static AbstractApplicationContext context;
public static void main(String[] args) {
if (args.length <>");
return;
}
String contextXml = args[0];
context = new FileSystemXmlApplicationContext(new String[]{contextXml}, true);
context.registerShutdownHook();//This will be useful incase if you want control the grace shutdown.
printMyName();
}
/**
* Method for reloading the context
* */
public static void reload() {
if (context == null) {
throw new RuntimeException("Context is not available");
}
System.out.println("Reloading the context");
context.refresh();
}
/**
*Test method for context reloading
* */
private static void printMyName() {
new Thread() {
public void run() {
while(true){
((WhoAmI) context.getBean("whoAmI")).printMyName();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//do nothing
}
}
}
}.start();
}
}
public class AdminClient {
public static void main(String[] args) {
String mbeanName = "bean:name=monitorBean";
MonitorMBean monitorMBean;
String serviceUrl = "service:jmx:rmi://localhost/jndi/rmi://localhost:1098/server";
try {
monitorMBean = createMbeanStub(mbeanName, serviceUrl);
monitorMBean.reload();
} catch (IOException e) {
System.out.println("IO Error occured while relading " + e); // Should use logger instead
} catch (MalformedObjectNameException e) {
System.out.println("Malformed error " + e); // Should use logger instead
}
System.out.println("The application context is reloaded successfully.");
}
}
private static MonitorMBean createMbeanStub(String mbeanName, String serviceUrl) throws MalformedObjectNameException,
IOException {
ObjectName mbeanObjectName = new ObjectName(mbeanName);
MBeanServerConnection serverConnection = connect(serviceUrl);
MonitorMBean monitorMBean;
monitorMBean = (MonitorMBean)MBeanServerInvocationHandler.newProxyInstance(serverConnection, mbeanObjectName,
MonitorMBean.class, false);
return monitorMBean;
}
private static MBeanServerConnection connect(String serviceUrl) throws IOException {
JMXServiceURL url = new JMXServiceURL(serviceUrl);
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
return jmxc.getMBeanServerConnection();
}
}
java hsenidmobile.control.StandaloneServerYou can see the output
My Name is now JKNow you go and change the server.xml. Edit the whoAmI bean's name parameter from JK to CK. Then run our JMX client
My Name is now JK
My Name is now JK
java hsenidmobile.control.AdminClientNow you can find the messages in the server console regarding to the reloading of the context. And also not the output message is changed to this
My Name is now CKCooool. Its simple as this.
My Name is now CK
My Name is now CK
http://hsenidians.blogspot.in/2007/07/reloading-spring-context-dynamically.html
Refresh/Reload Spring context dynamically
Spring is providing a method by which we can dynamically REFRESH/RELOAD entire context .
Create an ApplicationContextAware bean with following functions
/**
* setApplicationContext
*/
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
/**
* refreshContext()
*/
public void refreshContext(){
((ConfigurableApplicationContext)applicationContex t).refresh();
}
And call refreshContext() for the purpose.