tl; dr注入servlet的CDI bean是否也可能在适当的范围内?
在官方oracle教程和一些书中,我们可以看到简单的示例,它们展示了如何将CDI bean注入servlet。这非常简单,因为我们只需使用 @Inject 注释并在beans.xml中启用bean发现。我不明白的是,注入servlet的 @RequestScoped或@SessionScoped bean是否有可能具有正确的范围。 servlet对象只由容器创建一次,因此据我所知,注入也应该只发生一次或发生一些意外行为。但是当我们使用ie时。在bean类的@RequestScoped注入发生在每次请求这个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
答案 0 :(得分:4)
注入的bean保留了它们的范围,因为实际注入到servlet中的是代理而不是真正的bean。
代理由CDI容器动态创建,并从注入的类或接口派生。对于类,创建动态子类,对于接口,创建动态代理。
代理负责解析当前上下文并决定是创建新bean还是重用现有bean。然后,调用注入代理的所有方法都被转发到底层创建或重用的bean。
您甚至可以将注入的代理传递给另一个bean或普通对象,并且将正确解析上下文并调用正确的bean方法。这是有效的,因为一个线程总是最多有一个请求上下文,并且在任何时候,代理都可以访问其线程,并且可以找出为该线程分配的请求上下文。
代理还负责初始化bean,因此您可能会观察到,@PostConstruct
方法仅在必要时延迟并执行 - 当调用代理上的方法时。换句话说,当注入CDI bean时,它的后构造方法不会立即执行。你需要在bean上执行一些方法才能触发post-cnstruct方法。
答案 1 :(得分:1)
注入的内容不是请求范围的bean的实际实例。它实际上是一个动态生成的代理。在该代理上调用方法(例如foo()
)时,代理在请求或会话范围中查找实际的bean实例,调用其foo()
方法并将结果返回给servlet。