jetty+guice:添加servlet失败:javax.servlet.ServletException: Servlets must be bound as singletons

1、问题描述

    项目使用Jetty作为Servlet容器,使用Google的guice作为依赖注入的工具。在添加自定义Servlet的时候出错,异常信息如下:

javax.servlet.ServletException: Servlets must be bound as singletons. Key[type=com.mobvoi.be.utils.PrometheusMeterServlet, annotation=[none]] was not bound in singleton scope.
	at com.google.inject.servlet.ServletDefinition.init(ServletDefinition.java:106)
	at com.google.inject.servlet.ManagedServletPipeline.init(ManagedServletPipeline.java:82)
	at com.google.inject.servlet.ManagedFilterPipeline.initPipeline(ManagedFilterPipeline.java:103)
	at com.google.inject.servlet.GuiceFilter.init(GuiceFilter.java:220)
	at org.eclipse.jetty.servlet.FilterHolder.initialize(FilterHolder.java:138)
	at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:850)
	at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298)
	at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
	at org.eclipse.jetty.server.Server.start(Server.java:387)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
	at org.eclipse.jetty.server.Server.doStart(Server.java:354)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
...

2、问题分析

    根据异常查看代码。

    直接找到抛出异常的类ServletDefinition,这是guice中定义的类。在它的init方法中,会对servlet的作用于进行验证:

    // This absolutely must be a singleton, and so is only initialized once.
    if (!Scopes.isSingleton(injector.getBinding(servletKey))) {
      throw new ServletException("Servlets must be bound as singletons. "
        + servletKey + " was not bound in singleton scope.");
    }

    这里会调用Scopes.isSingleton方法来检测servletKey,判断该servlet的作用域是否被指定为Singleton。追踪代码可以发现,servletKey是由servlet类和注解共同决定的:

public void with(HttpServlet servlet, Map<String, String> initParams) {
  Key<HttpServlet> servletKey = Key.get(HttpServlet.class, UniqueAnnotations.create());
  binder.bind(servletKey).toInstance(servlet);
  with(servletKey, initParams, servlet);
}

    对于进行校验的方法Scopes.isSingleton,不妨也戳进去看一下:

  /**
   * Returns true if {@code binding} is singleton-scoped. If the binding is a {@link
   * com.google.inject.spi.LinkedKeyBinding linked key binding} and belongs to an injector (ie. it
   * was retrieved via {@link Injector#getBinding Injector.getBinding()}), then this method will
   * also true if the target binding is singleton-scoped.
   *
   * @since 3.0
   */

    根据方法说明可以知道,这个方法确实是用来判断传入的参数是的作用域是否是singleton-scoped。

3、结论:

1、guice框架注入依赖时,根据作用域的不同,有两种方式:

    1)Singleton:单例,整个内存中该类的实例只有一个。当内存中存在该类的实例时,下次注入只需要调用该实例,而无需再去创建新的实例;

    2)NO_SCOPE:每次注入时都会创建新的实例。

2、使用guice向Jetty容器中加入自定义的servlet时,需要声明servlet的作用域是Singleton。

4、解决方案:

方法一:在servlet类上面添加注解指定作用域:

import com.google.inject.Singleton;
@Singleton
public class PrometheusMeterServlet extends HttpServlet {

方法二:添加servlet容器的时候,用代码指定servlet的作用域:

    我是通过继承ServletModule并复写configureServlets方法来配置servlet的,可以在复写后的方法中指定servlet的作用域:

@Override
protected void configureServlets() {
  for (Entry<String, Class<? extends HttpServlet>> servletEntry : servletsMap.entrySet()) {
    serve(servletEntry.getKey()).with(servletKV.getValue());
    bind(servletEntry.getValue()).in(Scopes.SINGLETON);
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值