Java HttpServlet中介类未按预期初始化
我有一个web应用程序,其中多个servlet使用一定数量的相同逻辑进行预初始化(设置日志记录、会话跟踪等)。我所做的是在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
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()
中声明的局部变量将是线程安全的
此代码不是线程安全的。servlet容器通常会创建一个servlet实例,所有请求都会使用它。这意味着SessionUID将被所有请求共享,并将不断被覆盖 如果需要按每个请求的值保留这个值,请考虑使用TyLead本地对象并将uuID放在其中。 这就好像该类的实例即使在很长一段时间内(似乎直到服务器进程重新启动)也会被多个请求重用 是的,这正是将要发生的事情,也是你应该期待的 servlet并不意味着它是一个会话,它只是一个处理程序 如果您想对每个请求执行“某些操作”,无论使用什么方法,都可以重写该方法,执行任何操作,然后调用
super.service()
。但是,您不应该更改servlet本身的状态——请记住,多个请求可能同时在同一个servlet中执行
基本上,您的要求与servlet的设计背道而驰——您应该使用该设计,而不是反对它。您可以修改请求本身(使用
setAttribute
)来存储一些与此请求相关的信息,但无论如何,我可能会在比HTTP更高的级别上这样做。(我会尽量使servlet本身非常小,只是尽可能地委托给不支持servlet的类,这使它们更易于测试。)不仅如此,而且也不能保证构造函数中的初始化代码会在每个请求中执行,迈克尔:这在一定程度上取决于你的代码的结构和你所说的“有用”代码的数量。如果整个用例都是为请求生成一个唯一的id,并在整个生命周期中提供该id,那么您可能会更好地遵循Jon提到的方法。在服务方法中调用UUID.randomUUID(),然后将返回的值添加到请求中,或者将其放入ThreadLocal对象中。将其放入ThreadLocal可以使整个请求都可以使用,而不会污染需要访问它的方法的签名。是的,看起来我将采用Jon在回答中提到的方法,并简单地引入另一个类来完成这项工作。不过,这是一个有用的答案。为了以防万一,在接受任何答案之前,我会先把这个问题留待一会儿