You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在CDI中注入运行时对象?HttpSender注入场景求解

问题描述

我有如下代码:

@WebServlet("/produces")
public class TestProducesServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) ... {
        HttpSender sender  = new HttpSender(response);
    ...
}

我希望将new HttpSender替换为@Inject注入方式,因此将HttpSender重构为带有@Inject HttpSender( HttpServletResponse response)构造方法的类。但我不清楚如何将运行时的HttpServletResponse response传入该构造方法,曾考虑使用@Produces但未能实现。

于是我采用了临时解决方案:给HttpSender添加默认构造方法和HttpServletResponse的setter方法,修改后的Servlet代码如下:

@WebServlet("/produces")
public class TestProducesServlet extends HttpServlet {
    @Inject private HttpSender sender;
    protected void doPost(HttpServletRequest request, HttpServletResponse response) ... {
        sender.setHttpServletResponse(response);
    ...
}

但我不确定@Inject是否在setter调用前执行,且认为存在更优的解决方案。

最优解决方案

要实现运行时注入HttpServletResponse到HttpSender,可以通过以下步骤完成:

1. 定义HttpServletResponse的生产者方法

创建一个CDI生产者类,专门提供当前请求的HttpServletResponse实例。如果直接获取容器内的response有兼容性问题,推荐用过滤器提前把response存入请求属性:

第一步:添加请求过滤器

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/*")
public class ResponseStoringFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setAttribute("CURRENT_RESPONSE", response);
        chain.doFilter(request, response);
    }
}

第二步:编写生产者类

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.inject.Inject;

public class ServletResponseProducer {

    @Inject
    private HttpServletRequest request;

    @Produces
    @RequestScoped
    public HttpServletResponse produceHttpServletResponse() {
        return (HttpServletResponse) request.getAttribute("CURRENT_RESPONSE");
    }
}

2. 保持HttpSender的构造注入

不需要默认构造方法和setter,直接保留带@Inject的构造方法:

import javax.inject.Inject;

public class HttpSender {
    private final HttpServletResponse response;

    @Inject
    public HttpSender(HttpServletResponse response) {
        this.response = response;
    }

    // 你的业务方法,比如send()
}

3. 在Servlet中直接注入HttpSender

Servlet中直接注入HttpSender,无需手动设置response:

import javax.inject.Inject;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/produces")
public class TestProducesServlet extends HttpServlet {
    @Inject
    private HttpSender sender;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        // 直接使用sender,它已经绑定了当前请求的response
        // sender.send(...);
    }
}

关于临时方案的说明

你的临时方案存在线程安全隐患:Servlet默认是单例,@Inject只会在Servlet实例化时执行一次,而doPost是多请求并发调用的,多次调用setHttpServletResponse会覆盖之前的response实例,导致请求串数据。

而上述构造注入+生产者的方式是线程安全的:HttpSender默认是@Dependent作用域,每次注入都会生成新实例并绑定当前请求的response;如果需要HttpSender在整个请求周期内复用,可以给它加上@RequestScoped注解。

内容的提问来源于stack exchange,提问作者ericj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 22:46:03