Jetty:将对象从 main 方法传递给 servlet

Jetty: Pass object from main method to servlet

我有两个 类 Server(使用主要方法,启动服务器)和 StartPageServlet 使用 Servlet。

代码中最重要的部分是:

public class Server {
    public static void main(String[] args) throws Exception {
        // some code

        // I want to pass "anObject" to every Servlet.
        Object anObject = new Object();

        Server server = new Server(4000);
        ServletContextHandler context = 
            new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.addServlet(StartPageServlet.class, "/");
        // more code
}

还有 StartPageServlet:

public class StartPageServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
            throws ServletException, IOException
    {
        // Here I want to access "anObject"
    }

我该怎么做?

单例

您想将同一个实例传递给每个 servlet 吗?

使用 Singleton pattern 创建全局可用的单个实例。

在 Java 中最简单的方法是通过 Enum. See Oracle Tutorial. Also see this article and the book Effective Java: Programming Language Guide, Second Edition (ISBN 978-0-321-35668-0, 2008) by Dr. Joshua Bloch.

所以不需要传递对象。每个 servlet 都可以通过枚举访问同一个实例。

每个网络应用程序

如果您想在网络应用程序首次启动时但在该网络应用程序中的任何 servlet 处理任何请求之前做一些工作,请编写实现 ServletContextListener 接口的 class。

使用 @WebListener 注释标记您的 class 以让您的 Web 容器自动实例化和调用。

Embedded Jetty 在这里太棒了。

您有几个常见的选项:

  1. servlet 的直接实例化,使用构造函数或 setter,然后通过 ServletHolder(可以是任何值或对象类型)
  2. 将其传递给 Jetty
  3. 将其添加到主程序中的 ServletContext,然后通过应用程序中的 ServletContext 访问它(可以是任何值或对象类型)。

示例:

package jetty;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public class ObjectPassingExample
{
    public static void main(String args[]) throws Exception
    {
        Server server = new Server(8080);

        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/");

        // Option 1: Direct servlet instantiation and ServletHolder
        HelloServlet hello = new HelloServlet("everyone");
        ServletHolder helloHolder = new ServletHolder(hello);
        context.addServlet(helloHolder, "/hello/*");

        // Option 2: Using ServletContext attribute
        context.setAttribute("my.greeting", "you");
        context.addServlet(GreetingServlet.class, "/greetings/*");

        server.setHandler(context);
        server.start();
        server.join();
    }

    public static class HelloServlet extends HttpServlet
    {
        private final String hello;

        public HelloServlet(String greeting)
        {
            this.hello = greeting;
        }

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            resp.setContentType("text/plain");
            resp.getWriter().println("Hello " + this.hello);
        }
    }

    public static class GreetingServlet extends HttpServlet
    {
        private String greeting;

        @Override
        public void init() throws ServletException
        {
            this.greeting = (String) getServletContext().getAttribute("my.greeting");
        }

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            resp.setContentType("text/plain");
            resp.getWriter().println("Greetings to " + this.greeting);
        }
    }
}

我有类似的情况,但需要与通过 war 部署的 servlet 共享一个单例,并在 Jetty 容器中进行热(重新)部署。接受的答案并不是我所需要的,因为 servlet 有一个由部署者管理的生命周期和上下文。

我最终采用了蛮力方法,将对象添加到 server 上下文中,该上下文在容器的生命周期内一直存在,然后从 servlet 中获取对象。这需要在父(系统)classloader 中加载对象的 class,这样 war webapp 就不会将它自己的 class 版本加载到它自己的classloader,如 here.

所述,这将导致转换异常

嵌入式 Jetty 服务器代码:

    Server server = new Server(8090);

    // Add all classes related to the object(s) you want to share here.
    WebAppContext.addSystemClasses(server, "my.package.MyFineClass", ...);

    // Handler config
    ContextHandlerCollection contexts = new ContextHandlerCollection();
    HandlerCollection handlers = new HandlerCollection();
    handlers.setHandlers(new Handler[] { contexts });
    server.setHandler(handlers);

    // Deployer config (hot deploy)
    DeploymentManager deployer = new DeploymentManager();
    DebugListener debug = new DebugListener(System.err,true,true,true);
    server.addBean(debug);
    deployer.addLifeCycleBinding(new DebugListenerBinding(debug));
    deployer.setContexts(contexts);
    deployer.setContextAttribute(
            "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
            ".*/[^/]*servlet-api-[^/]*\.jar$|.*/javax.servlet.jsp.jstl-.*\.jar$|.*/[^/]*taglibs.*\.jar$");

    WebAppProvider webapp_provider = new WebAppProvider();
    webapp_provider.setMonitoredDirName("/.../webapps");
    webapp_provider.setScanInterval(1);
    webapp_provider.setExtractWars(true);
    webapp_provider.setConfigurationManager(new PropertiesConfigurationManager());

    deployer.addAppProvider(webapp_provider);
    server.addBean(deployer);

    // Other config...

    // Tuck any objects/data you want into the root server object.
    server.setAttribute("my.package.MyFineClass", myFineSingleton);

    server.start();
    server.join();

示例 servlet:

public class MyFineServlet extends HttpServlet
{
    MyFineClass myFineSingleton;

    @Override
    public void init() throws ServletException
    {
        // Sneak access to the root server object (non-portable).
        // Not possible to cast this to `Server` because of classloader restrictions in Jetty.
        Object server = request.getAttribute("org.eclipse.jetty.server.Server");

        // Because we cannot cast to `Server`, use reflection to access the object we tucked away there.
        try {
            myFineSingleton = (MyFineClass) server.getClass().getMethod("getAttribute", String.class).invoke(server, "my.package.MyFineClass");
        } catch (Exception ex) {
            throw new ServletException("Unable to reflect MyFineClass instance via Jetty Server", ex);
        }
    }

    @Override
    protected void doGet( HttpServletRequest request,
            HttpServletResponse response ) throws ServletException, IOException
    {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);
        response.getWriter().println("<h1>Hello from MyFineServlet</h1>");
        response.getWriter().println("Here's: " + myFineSingleton.toString());
    }
}

我的 servlet (sbt) 构建文件将 my.package.MyFineClass 依赖项放入 "provided" 范围,因此它不会打包到 war 中,因为它已经加载进入 Jetty 服务器。

我建议您研究 Google 对这个问题的解决方案...即:使用 Guice. They have a special servlet package 的依赖注入专门处理 servlet。