Java HttpServlet中介类未按预期初始化

Java HttpServlet中介类未按预期初始化,java,tomcat,instantiation,Java,Tomcat,Instantiation,我有一个web应用程序,其中多个servlet使用一定数量的相同逻辑进行预初始化(设置日志记录、会话跟踪等)。我所做的是在javax.servlet.http.HttpServlet和我的具体servlet类之间引入一个中间层: public abstract class AbstractHttpServlet extends HttpServlet { // ... some common things ... } 然后: public class MyServlet extends

我有一个web应用程序,其中多个servlet使用一定数量的相同逻辑进行预初始化(设置日志记录、会话跟踪等)。我所做的是在
javax.servlet.http.HttpServlet
和我的具体servlet类之间引入一个中间层:

public abstract class AbstractHttpServlet extends HttpServlet {
    // ... some common things ...
}
然后:

public class MyServlet extends AbstractHttpServlet {
    // ... specialized logic ...
}
我在AbstractHttpServlet的默认(也是唯一)构造函数中所做的一件事就是设置几个私有成员变量。在本例中,它是一个UUID,用作会话标识符:

public abstract class AbstractHttpServlet extends HttpServlet {
    private UUID sessionUuid;

    public AbstractHttpServlet() {
        super();
        this.sessionUuid = UUID.randomUUID();
        // ... there's more, but snipped for brevity ...
    }

    protected UUID getSessionUuid() {
        return this.sessionUuid;
    }
}
然后,我在
MyServlet
中使用
getsessionuid()
在请求中提供会话跟踪。这非常有用,例如在日志记录中,能够筛选大型日志文件并获取与单个HTTP请求相关的所有条目。原则上,会话标识符可以是任何东西;我之所以选择使用UUID,是因为它很容易生成一个随机的UUID,而且不需要担心不同服务器之间的冲突、种子问题、搜索日志文件将匹配项作为较长字符串的一部分等等

我看不出为什么多个执行应该在
sessionuid
成员变量中获得相同的值,但实际上,似乎是这样。这就好像该类的实例即使在很长一段时间内(似乎直到服务器进程重新启动)也会被多个请求重用

在我的例子中,与类所做的有用工作相比,类实例化开销较小,因此理想情况下,我希望Tomcat总是为每个请求创建新的类实例,从而强制它每次单独执行构造函数是否可以对类进行注释,以确保每个请求都实例化该类?不需要更改服务器配置的答案更受欢迎

如果做不到这一点,是否有其他方法(除了在每个do*()方法中分别这样做之外,例如
doGet()
doPost()
,等等)要确保对每个HTTP请求执行某种初始化,从而执行特定的servlet?

就好像该类的实例在很长一段时间内被多次请求重用一样

在每个JVM的任何给定时间点上,总是有一个Servlet类的实例。因此,实例变量在Servlet中不是线程安全的。Servlet的每个请求都将由一个线程处理。在
service()
doPost()
doGet()
中声明的局部变量将是线程安全的

  • 因此,您可以将逻辑移动到其他类,在服务方法中实例化它,并以线程安全的方式使用它。您甚至可以使用ThreadLocal对象

  • 有一个条款是要实现的,这是不赞成的,这样做不仅是不好的,而且是荒谬的

    确保servlet一次只处理一个请求。此接口没有任何方法

    如果servlet实现此接口,则可以保证在servlet的服务方法中不会有两个线程同时执行。servlet容器可以通过同步对servlet单个实例的访问,或者通过维护servlet实例池并将每个新请求分派给一个免费的servlet来保证这一点。

  • 更好的方法是实现一个逻辑并将其放在那里


  • 此代码不是线程安全的。servlet容器通常会创建一个servlet实例,所有请求都会使用它。这意味着SessionUID将被所有请求共享,并将不断被覆盖

    如果需要按每个请求的值保留这个值,请考虑使用TyLead本地对象并将uuID放在其中。

    这就好像该类的实例即使在很长一段时间内(似乎直到服务器进程重新启动)也会被多个请求重用

    是的,这正是将要发生的事情,也是你应该期待的

    servlet并不意味着它是一个会话,它只是一个处理程序

    如果您想对每个请求执行“某些操作”,无论使用什么方法,都可以重写该方法,执行任何操作,然后调用
    super.service()
    。但是,您不应该更改servlet本身的状态——请记住,多个请求可能同时在同一个servlet中执行


    基本上,您的要求与servlet的设计背道而驰——您应该使用该设计,而不是反对它。您可以修改请求本身(使用
    setAttribute
    )来存储一些与此请求相关的信息,但无论如何,我可能会在比HTTP更高的级别上这样做。(我会尽量使servlet本身非常小,只是尽可能地委托给不支持servlet的类,这使它们更易于测试。)

    不仅如此,而且也不能保证构造函数中的初始化代码会在每个请求中执行,迈克尔:这在一定程度上取决于你的代码的结构和你所说的“有用”代码的数量。如果整个用例都是为请求生成一个唯一的id,并在整个生命周期中提供该id,那么您可能会更好地遵循Jon提到的方法。在服务方法中调用UUID.randomUUID(),然后将返回的值添加到请求中,或者将其放入ThreadLocal对象中。将其放入ThreadLocal可以使整个请求都可以使用,而不会污染需要访问它的方法的签名。是的,看起来我将采用Jon在回答中提到的方法,并简单地引入另一个类来完成这项工作。不过,这是一个有用的答案。为了以防万一,在接受任何答案之前,我会先把这个问题留待一会儿