2015-10-20 77 views
1

tl; dr注入servlet的CDI bean如何也可能處於適當的範圍內?CDI bean的servlets和範圍

在官方的oracle教程和一些書中,我們可以看到簡單的例子,顯示瞭如何將CDI bean注入到servlet。這很簡單,因爲我們只需要使用@Inject註釋並在beans.xml中啓用bean發現。我不明白的是,注入到servlet中的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); 
    } 
} 

- 發送3個請求/豆URL後

@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(); 
    } 
} 

結果我們可以看到,每一次我們得到注入不同豆單個servlet。

23:35:18,062 INFO [stdout] (default task-3) [email protected] 
23:35:18,071 INFO [stdout] (default task-3) [email protected] 

23:35:23,883 INFO [stdout] (default task-4) [email protected] 
23:35:23,887 INFO [stdout] (default task-4) [email protected] 

23:35:27,286 INFO [stdout] (default task-5) [email protected] 
23:35:27,288 INFO [stdout] (default task-5) [email protected] 
+0

'ThreadLocal'如何工作,而它是一個靜態變量? :)類似的概念。 – ZhongYu

回答

4

注入的bean保留它們的作用域,因爲真正注入到servlet中的是代理而不是真正的bean。

代理由CDI容器動態創建,並從注入的類或接口派生。對於類,創建一個動態子類,爲接口創建動態代理。

代理負責解析當前上下文,並決定是創建新的bean還是重用現有的bean。然後,所有在注入代理上調用的方法都被轉發到底層創建的或重用的bean。

您甚至可以將注入的代理傳遞給另一個bean或一個普通對象,並且上下文將被正確解析並調用正確的bean方法。這是有效的,因爲線程始終只有一個請求上下文,並且在任何時候,代理都可以訪問其線程,並可以找出將哪個請求上下文分配給該線程。

代理也負責初始化bean,因此您可能會注意到,@PostConstruct方法被延遲並且僅在必要時執行 - 當調用代理上的方法時。換句話說,當注入CDI bean時,其後構建方法不會立即執行。你需要在bean上執行一些方法來觸發post-cnstruct方法。

1

注入的內容不是請求範圍bean的實際實例。它實際上是一個動態生成的代理。當在該代理上調用一個方法(例如foo())時,代理將在請求或會話範圍內查找實際的bean實例,調用它的foo()方法並將結果返回給servlet。