【问题标题】:How to access the same instance of a bean from HttpServlet?如何从 HttpServlet 访问同一个 bean 实例?
【发布时间】:2020-05-26 06:25:29
【问题描述】:

我对 Spring 框架有些陌生。我有一个用 Spring (4.2.1) 编写的 Web 应用程序。我正在尝试使用 Micrometer 库公开指标,并将使用 Prometheus 进行抓取。

应用程序的相关结构是这样的:
- 核心模块 (JAR)
- 网络服务模块(WAR)

我创建了一个 PrometheusService 类,它是 core-module 中定义的一个 bean。在 bean 内部定义的是 PrometheusMeterRegistryCounter

@Service
public class PrometheusService {

    private static PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
    private static Counter newAssetCounter = Counter
            .builder("new_asset_counter")
            .description("count of created assets")
            .tags("region", "na")
            .register(registry);

    public PrometheusService() {
        new JvmMemoryMetrics().bindTo(registry);
        new DiskSpaceMetrics(new File("/")).bindTo(registry);
        new ProcessorMetrics().bindTo(registry);
        new UptimeMetrics().bindTo(registry);
    }

    public static PrometheusMeterRegistry getRegistry() {
        return registry;
    }

    public Counter getNewAssetCounter() {
        return this.newAssetCounter;
    }

}

我创建了 MetricsResource,它是一个暴露 /metrics 端点的 HttpServlet。当尝试@Autowire PrometheusService bean 时,这里总是为空。快速搜索告诉我 HttpServlet 不是由 Spring 管理的。如果我想@Autowire,我需要添加如下内容:

SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(webApplicationContext);

现在,我能够@Autowire Servlet 中的 PrometheusService bean。

在 bean 中定义的 Counter 在核心模块中递增。 MetricsResource doGet 方法写入存储在PrometheusMeterRegistry 中的指标。

@WebServlet("/metrics")
public class MetricsResource extends HttpServlet  {

    private PrometheusService promService; // @Autowired
    private PrometheusMeterRegistry registry;

    @Override
    public void init() throws ServletException {
        super.init();

//        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(webApplicationContext);

//        promService = (PrometheusService) getServletContext().getAttribute("prometheusService");

//        WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//        AutowireCapableBeanFactory ctx = context.getAutowireCapableBeanFactory();
//        ctx.autowireBean(this);
    }

    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
        resp.setStatus(HttpServletResponse.SC_OK);
        resp.setContentType(TextFormat.CONTENT_TYPE_004);

        registry = promService.getRegistry();

        Writer writer = resp.getWriter();
        try {
            registry.scrape(writer);
            writer.flush();
        } finally {
            writer.close();
        }
    }
}

问题在于Counter 的值在 /metrics 端点处始终为 0。

无论是 @Autowired 还是我手动尝试获取 bean。

这怎么可能?我的 PrometheusService bean 是一个单例。甚至 PrometheusMeterRegistry 和 Counter 都被标记为静态,那么为什么我的 servlet 中会出现不同的对象呢?经过一番搜索,我发现 Spring 将为每个容器创建一个单例 bean。所以我假设这里发生的是两个容器或上下文。一个主应用程序上下文和一个 servlet 上下文。

我尝试过的一些事情:
制作 PrometheusService 实现ApplicationContextAware
使用实现 ApplicationContextAware 并返回 bean 的 ServiceLocator 类
将上下文参数添加到 web.xml
在 app-context.xml
中使用 ServletContextAttributeExporter 使用WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext())

我继续获取对象的新实例。我想要做的就是能够使用 Micrometer 创建和公开自定义指标。我的方法有缺陷吗?如何从我的 HttpServlet 中访问正确的 bean?

Spring dependency injection to other instance
http://senthadev.com/sharing-spring-container-between-modules-in-a-web-application.html
Spring dependency injection to other instance
ApplicationContext and ServletContext

【问题讨论】:

标签: java spring servlets dependency-injection


【解决方案1】:

试试processInjectionBasedOnServletContext(Object target, ServletContext servletContext)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-09
    • 2016-01-04
    • 1970-01-01
    • 2016-03-23
    • 1970-01-01
    • 2012-12-18
    • 1970-01-01
    相关资源
    最近更新 更多