【问题标题】:How to perform an expensive one time initialization in a Servlet filter?如何在 Servlet 过滤器中执行昂贵的一次性初始化?
【发布时间】:2014-03-22 04:18:53
【问题描述】:

以下是问题的要求:

  • 在 Tomcat 7 中运行的 java Web 应用程序
  • 初始化代码需要与外部数据库对话
  • 外部数据库在应用程序启动期间可能不可用
  • 应用程序启动不能失败,否则tomcat会将应用程序标记为未运行并且不会向应用程序发送任何请求。相反,应用程序应该启动接受请求,如果它发现应用程序特定的初始化未完成,它应该尝试在请求处理期间完成它。

我正在尝试使用下面的 servlet 过滤器来解决问题。我有以下问题。

  • 这个 Servlet 过滤线程安全吗,它会只执行一次初始化代码吗?
  • 有没有办法在不使用 servlet 过滤器的情况下解决这个问题?
  • 有没有更好的方法来解决这个问题?
 package com.exmaple;
    @WebFilter("/*")

 public class InitFilter implements Filter 
 {
    private volatile boolean initialized = false;

    public void destroy() {
        System.out.println("InitFilter.destroy()");
    }

        // is this method thread safe and will only execute the init code once
        // and will cause all requests to wait until initialization code is executed
        // thread code
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            System.out.println("InitFilter.doFilter()");

            if (initialized == false) {
                synchronized (this) {
                    // do expensive initialization work here
                    initialized = true;
                }
            }
            chain.doFilter(request, response);
        }

        public void init(FilterConfig fConfig) throws ServletException {
            System.out.println("InitFilter.init()");
        }
      }

【问题讨论】:

  • 您可以使用Executors.newSingleThreadPool() 执行FutureTask<Foo>;那么你将使用这个Future.get()访问你的Foo
  • 模式 test-synchronize-set 本质上是不安全的,因为线程 A 可以测试 initialized==false 然后在 synchronized(this) 之前挂起 - 线程 B 然后测试初始化​​,进行同步,然后启动在里面;然后线程 B 在 I/O 上挂起,线程 A 尝试 synchronized(this) 并被阻塞,因为 B 仍然有同步。当线程 B 完成并解锁时,线程 A 将继续并再次运行初始化。
  • 至少初始化 = true;应该是第一行代码。最好看看单例模式并使用它,即使它不是最好的——出于学术原因,在现实生活中的应用程序中,我已经看到这种初始化工作得很好。我们通过预先接触一个 servlet 来连接做 db,并有一个单例加载资源,但检查更像是 stackoverflow.com/questions/70689/…stackoverflow.com/questions/427902/…

标签: java jakarta-ee tomcat servlets


【解决方案1】:

我会将其作为ServletContextListener 来处理,并在contextInitialized 方法中运行初始化,作为一个单独的线程,可能使用@fge 建议的FutureTask,或者newSingleThreadExecutor 而不是池。

也许是……

public void contextInitialized(ServletContextEvent sce)
{
    Executors.newSingleThreadExecutor().execute(new Runnable() {

        @Override
        public void run()
        {
            // Long running initialization code, or a call to it.
        }

    });

}

这避免了同步问题,并且只运行一次初始化。 (每个上下文一次)

在任何情况下,执行此操作或过滤器,程序的其余部分都必须处理初始化不完整的问题。

【讨论】:

  • 这样应用程序启动成功但是如何失败取决于初始化完成的请求直到初始化完成。
  • @ams - 要使请求失败,您可以使用过滤器,但它需要检查的只是initiazationComplete 标志;不需要同步或锁定争用。您可以使用 AtomicBoolean 作为标志,并在完成后让初始化代码将其设置为 true。
【解决方案2】:

我建议您将长时间运行的初始化放在一个线程中,例如:

public void init() throws ServletException {
    //Configure logging, app, pool ...
    MyAppStarter.getInstance().start();
}

【讨论】:

    猜你喜欢
    • 2016-06-14
    • 2016-12-12
    • 2021-12-02
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多