2008-11-24 29 views
16

Tôi đang xây dựng ứng dụng web JSF + Facelets, một phần trong số đó là phương pháp quét thư mục thường xuyên và lập chỉ mục bất kỳ thay đổi nào. Phương thức này là một phần của bean nằm trong phạm vi ứng dụng. Tôi đã xây dựng một lớp con của TimerTask để gọi phương thức mỗi X mili giây. Vấn đề của tôi là nhận được bean khởi tạo. Tôi có thể tham khảo các bean trên một trang, và khi tôi đi đến trang, đậu được khởi tạo, và làm việc theo chỉ dẫn; những gì tôi muốn thay vào đó là cho đậu được khởi tạo khi bối cảnh web được khởi tạo, do đó nó không yêu cầu một lần truy cập trang để bắt đầu phương pháp lập chỉ mục. Google đã cho thấy một vài người muốn có chức năng này, nhưng không có giải pháp thực sự ngoài tích hợp với Spring, mà tôi thực sự không muốn làm chỉ để có được chức năng này.JSF khởi tạo phạm vi ứng dụng khi bối cảnh được khởi tạo

Tôi đã thử chơi xung quanh với cả hai servlet có bộ tải "khởi động" và ServletContextListener để thực hiện mọi thứ, và không thể thiết lập đúng, vì có không có FacesContext, hoặc vì tôi không thể tham khảo bean từ môi trường JSF.

Có cách nào để lấy hạt JSF được khởi chạy khi khởi động ứng dụng web không?

Trả lời

14

Nếu mã của bạn gọi FacesContext, nó sẽ không làm việc bên ngoài một sợi liên kết với một vòng đời yêu cầu JSF. Một đối tượng FacesContext được tạo ra cho mọi yêu cầu và được xử lý ở cuối yêu cầu. Lý do bạn có thể tìm nạp thông qua số static call là vì nó được đặt thành số ThreadLocal khi bắt đầu yêu cầu. Vòng đời của FacesContext không có liên quan gì đến ServletContext.

Có thể điều này không đủ (có vẻ như bạn đã đi xuống tuyến đường này), nhưng bạn sẽ có thể sử dụng ServletContextListener để thực hiện những gì bạn muốn. Chỉ cần đảm bảo rằng mọi cuộc gọi đến FacesContext đều được giữ trong chuỗi yêu cầu của JSP.

web.xml:

<listener> 
    <listener-class>appobj.MyApplicationContextListener</listener-class> 
</listener> 

Thực hiện:

public class MyApplicationContextListener implements ServletContextListener { 

    private static final String FOO = "foo"; 

    public void contextInitialized(ServletContextEvent event) { 
     MyObject myObject = new MyObject(); 
     event.getServletContext().setAttribute(FOO, myObject); 
    } 

    public void contextDestroyed(ServletContextEvent event) { 
     MyObject myObject = (MyObject) event.getServletContext().getAttribute(
       FOO); 
     try { 
      event.getServletContext().removeAttribute(FOO); 
     } finally { 
      myObject.dispose(); 
     } 
    } 

} 

Bạn có thể giải quyết đối tượng này thông qua phạm vi ứng dụng JSF (hoặc chỉ cần trực tiếp nếu không có biến khác tồn tại có cùng tên):

<f:view> 
    <h:outputText value="#{applicationScope.foo.value}" /> 
    <h:outputText value="#{foo.value}" /> 
</f:view> 

Nếu bạn muốn truy xuất đối tượng trong một bean được quản lý JSF, bạn có thể lấy nó từ ExternalContext:

FacesContext.getCurrentInstance() 
      .getExternalContext().getApplicationMap().get("foo"); 
+0

Điều này đã kết thúc - tôi đã bỏ lỡ bit "setAttribute" để làm cho nó có thể truy cập được trong mã JSF. Cảm ơn! –

0

Sử dụng người nghe hoặc tải-on-khởi động, hãy thử này: http://www.thoughtsabout.net/blog/archives/000033.html

+0

Tôi đã sử dụng mẹo đó trước đây, nhưng nó không hoạt động với ServletContextListener vì bạn không có yêu cầu/phản hồi cho facesContext = contextFactory.getFacesContext (servletContext, yêu cầu, đáp ứng, vòng đời); Không thành công nếu tham số bất kỳ là rỗng. Cùng một vấn đề với tải-on-startup/init. –

+0

Tôi có thể làm tốt nhất/dễ dàng hơn để tạo lớp học khi khởi động và lưu trữ nó. Sau đó, khi đậu jsf được truy cập lần đầu tiên, nó sẽ kiểm tra lớp đó và sử dụng nó hoặc sao chép dữ liệu từ một đến cái kia. – Loki

0

Trong JSF 2+ bạn có thể sử dụng SystemEventListener để xử lý. Bạn sẽ đặt nó để thực hiện hành động trên PostConstructApplicationEvent để khởi tạo nó.

<system-event-listener> 
    <system-event-listener-class> 
    listeners.SystemEventListenerImpl 
    </system-event-listener-class> 
    <system-event-class> 
    javax.faces.event.PostConstructApplicationEvent 
    </system-event-class>      
</system-event-listener> 

Việc thực hiện sẽ giống như thế:

public class SystemEventListenerImpl implements SystemEventListener { 

    @Override 
    public void processEvent(SystemEvent event) throws AbortProcessingException { 
    Application application = (Application) event.getSource(); 
    //TODO 
    } 

    @Override 
    public boolean isListenerForSource(Object source) { 
    return (source instanceof Application); 
    } 
} 

này sẽ cho phép bạn làm nhiều hơn chỉ đơn giản là đi qua một giá trị.

+1

Lưu ý rằng OP đang sử dụng JSF 1.x (JSF 2.x không tồn tại khi câu hỏi được hỏi). Đối với JSF 2.x, có một cách dễ dàng hơn nhiều: chỉ cần một '@ManagedBean (eager = true) @ ApplicationScoped'. Không cần XML hoặc giao diện lộn xộn. Xem thêm http://stackoverflow.com/questions/3600534/jsf-how-do-i-force-an-application-scoped-bean-to-instantiate-at-application-st/3600740#3600740 – BalusC

+0

Tôi quay lại và đọc câu hỏi và câu trả lời của bạn giải quyết phần cuối của câu hỏi của anh ấy về việc tải mong muốn cho JSF 2.x. Nắm bắt tốt. –

Các vấn đề liên quan