牙牙窝

BlogJava 联系 聚合 管理
  8 Posts :: 21 Stories :: 10 Comments :: 0 Trackbacks

 

一、        概述

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 !!!

 

posted on 2006-12-06 09:38 大牙 阅读(9655) 评论(7)  编辑  收藏 所属分类: 架构师历程

Feedback

# re: 关于Spring配置文件的动态载入的修改 2007-11-19 14:38 tedeyang
good,楼主很能钻。
不过用监视xml文件的方式更好一点,否则刷新得太频繁了。  回复  更多评论
  

# re: 关于Spring配置文件的动态载入的修改 2007-11-19 14:38 tedeyang
另外在bean依赖关系复杂的时候会比较麻烦。  回复  更多评论
  

# re: 关于Spring配置文件的动态载入的修改 2008-03-27 11:25 疑问
我修改了怎么不成功呢?   回复  更多评论
  

# re: 关于Spring配置文件的动态载入的修改 2009-07-09 11:12 hezhuo1985
@tedeyang
监视xml文件是怎样实现的?
  回复  更多评论
  

# re: 关于Spring配置文件的动态载入的修改 2009-07-09 11:50 hezhuo1985
实现 applicationContext.xml 的动态载入 ,具体怎么操作?
  回复  更多评论
  

# re: 关于Spring配置文件的动态载入的修改 2011-12-08 10:48 陈华
请问楼主。如何不修改源码实现这样的需求:

在服务器运行的状态,修改Spring中的数据库连接,让它重新加载。万分感谢!

QQ 705541917

chengongmo@126.com  回复  更多评论
  

# re: 关于Spring配置文件的动态载入的修改 2012-02-15 15:20 jakey766
每次进入DispatcherServlet 时重新加载一下不太合理呀,LZ现在有其他方法没?spring中的那几个方法都用final修饰了,想改都改不了啊  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: