方案1: 把类移动到另一个类装载器中
这个方案适合在webapps之间共享这个类的情况,或者服务器仅包含一个webapp。那就是我们需要在同一个服务器中使用同一个实例跨越几个webapps,或者不需要担心这个事情。在这个情况下,类需要在一个共享类装载器上布置。这意味着这个类必须放在共享的 lib或者共享的classes目录下。这种方法,类被一个父类装载器装载,而不是webapp类装载器本身,因此没有资源需要卸载在webapp重新装载时。这个方案并不总适合你的代码和设计。特别需要注意避免单一模式类参考通过webapp类装载器装载的类,因为这样的参考将会阻止类装载器的销毁。Servlet上下文监听器(ServletContextListener)可以用来在上下文销毁的时候去除这个参考。
方案2: 使用commons-discovery
如果你有一个为每一个webapp使用单一模式实例,你应该使用commons-discovery。这个库提供了一个名字为DiscoverSingleton的类用来在你的webapp中实现单一模式。为了使用它,作为单一模式类需要实现方法使用的一个接口(SPI)。下面的代码是使用这个库的例子:
MyClass instance = DiscoverSingleton.find(MyClass.class, MyClassImpl.class.getName());
对于这个库工作正常它是非常重要的,不需亚对返回的实例保持静态的参考。
使用这个语法,你能得到如下好处:
●任一类都可用作单一模式类,只要其实现了一个SPI接口。
●你的单一模式类已经被转换成一个可替换的组件,因此可以在你需要的时候插入一个不同的实现。
但是仅这些不不是本方案的全部,重要的益处是其DiscoverSingleton.release()方法,它会释放当前webapp类装载器参考的单一模式类。这个方法应该放在ServletContextListener中的contextDestroyed()方法中。
简单的示例如下:
public class SingletonReleaser implements ServletContextListener {
public contextInitialized(ServletContextEvent event) { }
public contextDestroyed(ServletContextEvent event) {
DiscoverSingleton.release();
}
}
当然需要在在其它监听器使用一个单一模式类之前在web.xml中注册这个监听器。
方案3: 使用ServletContext属性
这个方案在ServletContext实例可用的时候工作的很好,作为一个本地变量或者一个参数。它比使用commons-discovery更有效率,但是不利之处是依赖于web层(ServletContext class)。我发现在一些情况下,它也是合理的方式。
有很多方式来实现它,在此我仅例示我使用良好的代码:
●创建如下ServletContextListener:
public class SingletonFactory implements ServletContextListener {
public static final String MY_CLASS = "...";
/**
* @see ServletContextListener#contextInitialized(ServletContextEvent)
*/
public void contextInitialized(ServletContextEvent event) {
ServletContext ctx = event.getServletContext();
ctx.setAttribute(MY_CLASS, new MyClass());
}
/**
* @see ServletContextListener#contextDestroyed(ServletContextEvent)
*/
public void contextDestroyed(ServletContextEvent event) {
ctx.setAttribute(MY_CLASS, null);
}
/**
* Optional method for getting the MyClass singleton instance.
*/
public static MyClass getMyClassInstance(ServletContext ctx) {
return (MyClass)ctx.getAttribute(MY_CLASS);
}
}
●在web.xml中注册监听器
●通过如下方式替换对MyClass.getInstance()的调用:
MyClass instance = (MyClass)ctx.getAttribute(SingletonFactory.MY_CLASS);
/* or, if implemented:
MyClass instance = SingletonFactory.getMyClassInstance(ctx);
*/