【问题标题】:Servlets and scopes of CDI beansCDI bean 的 Servlet 和范围
【发布时间】:2015-10-20 21:45:25
【问题描述】:

tl;dr 注入到 servlet 的 CDI bean 怎么可能也在适当的范围内?

在官方的 oracle 教程和一些书籍中,我们可以看到一些简单的示例,这些示例展示了如何将 CDI bean 注入到 servlet。这非常简单,因为我们只需要使用 @Inject 注释并在 beans.xml 中启用 bean 发现。我不明白的是,注入到 servlet 的 @RequestScoped 或 @SessionScoped bean 怎么可能具有正确的范围。 servlet 对象仅由容器创建一次,据我所知,注入也应该只发生一次,或者应该发生一些意外行为。但是当我们使用 ie 时。 @RequestScoped 在 bean 类上,注入发生在对这个 servlet 的每个请求之后(很棒)。问题是它是如何深入工作的?

简单示例

public interface BeanInterface {
    public void beanInfo();
}

-

@RequestScoped
public class BeanImpl implements BeanInterface {
    @Override
    public void beanInfo() {
        System.out.println(this);
    }
}

-

@WebServlet("/bean")
public class BeanServlet extends HttpServlet {
    //how is it injected with every GET/POST/... request
    @Inject
    private BeanInterface bean;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(this);
        bean.beanInfo();
    }
}

向 /bean URL 发送 3 个请求后的结果,我们可以看到每次我们将不同的 bean 注入到单个 servlet。

23:35:18,062 INFO  [stdout] (default task-3) com.test.BeanServlet@1f2521b7
23:35:18,071 INFO  [stdout] (default task-3) com.test.BeanImpl@4a49ab25

23:35:23,883 INFO  [stdout] (default task-4) com.test.BeanServlet@1f2521b7
23:35:23,887 INFO  [stdout] (default task-4) com.test.BeanImpl@6ff1609e

23:35:27,286 INFO  [stdout] (default task-5) com.test.BeanServlet@1f2521b7
23:35:27,288 INFO  [stdout] (default task-5) com.test.BeanImpl@1edc9ec

【问题讨论】:

  • ThreadLocal 是静态变量时如何工作? :) 类似的概念。

标签: java servlets dependency-injection cdi


【解决方案1】:

注入的 bean 保留其范围,因为真正注入 servlet 的是代理而不是真正的 bean。

代理由 CDI 容器动态创建,并从注入的类或接口派生。对于类,创建一个动态子类,对于接口,创建一个动态代理。

代理负责解析当前上下文并决定是创建新 bean 还是重用现有 bean。然后,所有在注入代理上调用的方法都被转发到底层创建或重用的 bean。

您甚至可以将注入的代理传递给另一个 bean 或普通对象,上下文将被正确解析并调用正确的 bean 方法。之所以可行,是因为一个线程最多只能有一个请求上下文,并且在任何时候,代理都可以访问其线程,并且可以找出分配给线程的请求上下文。

代理还负责初始化 bean,因此您可以观察到,@PostConstruct 方法被延迟并仅在必要时执行 - 当调用代理上的方法时。换句话说,当 CDI bean 被注入时,它的 post-construct 方法不会立即执行。您需要在 bean 上执行一些方法才能触发 post-cnstruct 方法。

【讨论】:

    【解决方案2】:

    注入的不是请求范围 bean 的实际实例。它实际上是一个动态生成的代理。在该代理上调用方法(例如foo())时,代理会在请求或会话范围内查找实际的bean 实例,调用其foo() 方法并将结果返回给servlet。

    【讨论】:

      猜你喜欢
      • 2012-01-20
      • 1970-01-01
      • 1970-01-01
      • 2014-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-04
      • 2017-11-26
      相关资源
      最近更新 更多