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 在这里太棒了。
您有几个常见的选项:
- servlet 的直接实例化,使用构造函数或 setter,然后通过
ServletHolder
(可以是任何值或对象类型) 将其传递给 Jetty
- 将其添加到主程序中的
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。
我有两个 类 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 在这里太棒了。
您有几个常见的选项:
- servlet 的直接实例化,使用构造函数或 setter,然后通过
ServletHolder
(可以是任何值或对象类型) 将其传递给 Jetty
- 将其添加到主程序中的
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。