关于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.