【问题标题】:Jetty: Pass object from main method to servlet码头:将对象从主要方法传递给servlet
【发布时间】:2017-01-18 04:50:06
【问题描述】:

我有两个类 Server(使用 main 方法,启动服务器)和 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 中创建 Object 实例?如果您需要对象中的全局值/方法,您可以使用 static 关键字,您根本不需要实例化它们。
  • 因为该对象包含我无法在 Servlet 中获取的值。替代方案将是一个全局静态变量,但我想知道这是否是要走的路......

标签: java servlets jetty


【解决方案1】:

嵌入式码头在这里真是太棒了。

您有几个常用选项:

  1. 直接实例化 servlet,使用构造函数或设置器,然后通过 ServletHolder 将其交给 Jetty(可以是任何值或对象类型)
  2. 将其添加到 main 中的 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);
        }
    }
}

【讨论】:

  • 被覆盖的init 方法完美运行!谢谢。
  • 如果我使用 WebAppProvider 会怎样?如何在 main 方法中获取 ServletContext?
  • @Ben 这超出了这个问题的范围。请提出一个新问题(如果看到我会回答)
【解决方案2】:

单例

您想将相同的单个实例传递给每个 servlet?

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

在 Java 中最简单的万无一失的方法是通过Enum。见Oracle Tutorial。另请参阅this articleDr. Joshua Bloch 撰写的Effective Java: Programming Language Guide, Second Edition (ISBN 978-0-321-35668-0, 2008) 一书。

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

每个网络应用

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

使用@WebListener 注释标记您的类,让您的网络容器自动实例化和调用。

【讨论】:

    【解决方案3】:

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

    我最终采用了一种蛮力方法,将对象添加到 server 上下文中,该上下文在容器的生命周期内持续存在,然后从 servlet 中获取对象。这需要在父(系统)类加载器中加载对象的类,以便 war webapp 不会将其自己的类版本加载到自己的类加载器中,这将导致转换异常,如 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 依赖项置于“已提供”范围内,因此它不会被打包到战争中,因为它已经被加载到 Jetty 服务器中。

    【讨论】:

    • 对我来说,在 servlet 的 init 方法中,servlet 上下文没有“org.eclipse.jetty.server.Server”的属性。你必须做一些特别的事情才能得到它吗?
    • @Ben,您使用的是码头还是其他容器?如前所述,这是不可移植到其他 servlet 容器的,至少按原样,您需要使用容器实现的 Server 类的完全限定类名,并且这可能会改变Jetty 也有不同的版本。
    【解决方案4】:

    我建议您调查 Google 对此问题的解决方案...即:使用 Guice 进行依赖注入。他们有一个特殊的 servlet package 专门处理 servlet。

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2011-09-27
      • 2014-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多