论坛首页 Java企业应用论坛

一个简单的ServletContextLoader装载器

浏览 6668 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-09-26  
在很多项目中,会碰到一种需求,就是在应用服务器启动的时候,把一些东西从数据库里面读到内存中去。例如,对于一些权限信息,或者一些数据字典等等。实现这种需求本身不是很困难,写一个类,然后实现ServletContextListener这个接口,再到web.xml里面去配置一下就可以了。(我想已经有很多应用服务器支持ServletContextListener这个接口了吧,像Websphere5.0这种垃圾除外)
现在的问题是,由于需求是不断变化的,说不定哪天增加了一个又要在系统启动时往内存里面写点啥。此时,要么在原来的类的后面,加一段代码,要么就再写一个类,再配一个Listener。一般我会采用后面一种做法,因为这样至少可以做到对这些Listener保持可配置性,当业务发生变化时,简单改变配置文件就可以完成需求。不过这带来了一个问题,就是有可能web.xml就比较凌乱,而且还要搞清楚和其他一些系统启动运行的Listener的关系。
所以最近根据这个情况设计了一个简单的ServletContextLoader的装载器。在web.xml里面只需要配置一个Listener,而这个listener的作用就是依次按顺序调用其他的Listener。此时,其他的Listener都可以通过Spring的注入进入这个总的Listener服从调度。
/**
 * @author zhou.lu
 */

public class ServletContextLoaderListener implements ServletContextListener {

    private static final Log logger = LogFactory.getLog(ServletContextLoaderListener.class);

    private ServletContextLoader servletContextLoader;

    public void contextInitialized(ServletContextEvent event) {
       this.servletContextLoader = createServletContextLoader(event);
       this.servletContextLoader.initServletContext(event.getServletContext());
    }

    private ServletContextLoader createServletContextLoader(ServletContextEvent event) {
       ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
       return (ServletContextLoader) applicationContext.getBean("servletContextLoader");
    }

    public void contextDestroyed(ServletContextEvent event) {
       this.servletContextLoader.closeServletContext(event.getServletContext());
    }
}

这个就是一个总的Listener,这里模仿了Spring的源码,将具体的调度指派给ServletContextLoader这个接口来完成,而具体的实现则通过Spring拿到。接下来看一下它的具体实现:
/**
 * @author zhou.lu
 */
public class ServletContextLoaderImpl implements ServletContextLoader, InitializingBean {

    private static final Log logger = LogFactory.getLog(ServletContextLoaderImpl.class);

    private List servletContextLoaders;

    public void setServletContextLoaders(List servletContextLoaders) {
       this.servletContextLoaders = servletContextLoaders;
    }

    public void afterPropertiesSet() throws Exception {
       for (int i = 0; i < servletContextLoaders.size(); i++) {
           // check every servletContextLoader and set to loaders
           Object loader = servletContextLoaders.get(i);
           if (!(loader instanceof ServletContextLoader)) {
              throw new IllegalArgumentException("Unsupported ServletContextLoader:" + loader.getClass());
           }
       }
    }

    public void initServletContext(ServletContext servletContext) {
       for (int i = 0; i < servletContextLoaders.size(); i++) { 
           ServletContextLoader loader = (ServletContextLoader) servletContextLoaders.get(i);
           loader.initServletContext(servletContext);
       }
    }

    public void closeServletContext(ServletContext servletContext) {
       for (int i = 0; i < servletContextLoaders.size(); i++) { 
           ServletContextLoader loader = (ServletContextLoader) servletContextLoaders.get(i);
           loader.closeServletContext(servletContext);
       }
    }
}

其实这个具体的实现就是做了所有的调度。而所有实现ServletContextLoader这个接口,并且被注入到这个类中去的ServletContextLoader会被依次执行其中的方法。因而,所以如果以后要做系统启动时的数据加载,只要简单实现ServletContextLoader这个接口,并且配置到Spring的配置文件中即可。例如:
public class MasterDataLoader implements ServletContextLoader {
    private SystemService systemService;
    public void initServletContext(ServletContext servletContext) {
       // Master Data Loader
       Map masterData = systemService.getMasterData();
       // TODO Add to servletContext
    }

    public void closeServletContext(ServletContext servletContext) {
       // TODO Clear servletContext
    }

    public void setSystemService(SystemService systemService) {
       this.systemService = systemService;
    }
}

然后是Spring的配置文件片断:
<bean id="systemService" parent="baseTxProxy">
  <property name="target">
   <bean class="com.adt.surecenter.service.impl.SystemServiceImpl"
    autowire="byName"/>
  </property>
 </bean>  

 <bean id="commandMappingLoader" class="com.adt.surecenter.loader.CommandMappingLoader" autowire="byName" />
 
 <bean id="masterDataLoader" class="com.adt.surecenter.loader.MasterDataLoader" autowire="byName" />
 
 <bean id="servletContextLoader" class="com.adt.core.loader.ServletContextLoaderImpl" autowire="byName" >
  <property name="servletContextLoaders">
   <list>
    <ref bean="masterDataLoader"/>
   </list>   
  </property>  
 </bean>


最后别忘记在web.xml中配置最初写的那个Listener,并且放置在Spring的Listener之后,就ok了。
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<listener>
		<listener-class>com.adt.core.loader.ServletContextLoaderListener</listener-class>
	</listener>

此时,你在web.xml中的配置简化了,你所需要实现的具体的每个servletContextLoader看上去就像一个POJO,实现某个接口,收到Spring的管理,遵循Spring的IoC。

好了,收工,洗手。
   发表时间:2006-09-26  
既然你都和spring绑定到一起用了,为啥还需要listener呢?
在某个setup bean的afterPropertiesSet() 方法里面做初始化的动作不就OK了吗?

如果没有spring,也可以在web.xml里面配置context-param,多设置几个setup bean的class name,然后统一由一个Listener读入不也OK吗?
0 请登录后投票
   发表时间:2006-09-26  
Readonly 写道
既然你都和spring绑定到一起用了,为啥还需要listener呢?
在某个setup bean的afterPropertiesSet() 方法里面做初始化的动作不就OK了吗?

如果没有spring,也可以在web.xml里面配置context-param,多设置几个setup bean的class name,然后统一由一个Listener读入不也OK吗?


一开始是考虑如果初始化过程无论有数据库读还是写操作,想用到Spring的事务管理。

不过你说得对,既然和Spring绑在一起了,就在afterPropertiesSet()方法里面做初始化动作好了。一开始没想到,脑子进水了。搞得那么麻烦,回去改掉。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics