2012-05-16 23 views
7

Tôi đang sử dụng Spring và JSF 2 để tạo một ứng dụng web. Các đối tượng kinh doanh được lưu giữ trong các thùng chứa mùa xuân, và tôi tiêm chúng vào Đậu Managed sử dụng @ManagedProperty, như thế này:Làm thế nào để tái tiêm một @ManagedProperty thoáng qua tại deserializing?

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Getter @Setter 
    @ManagedProperty("#{someService}") 
    private SomeService someService; 
    // ... 

Vấn đề là, tôi tiếp tục nhận được một NotSerializableException cho một lớp học từ mùa xuân (ServiceLocatorFactoryBean) đang được sử dụng bởi Đậu nành SomeService.

Nếu tôi làm cho nó transient, làm thế nào tôi có thể thực hiện việc tiêm lại nó sau khi deserializing?

Hoặc, cách nào khác để giải quyết vấn đề này?

Tôi đã đọc một số câu hỏi khác tương tự ở đây, nhưng không thể tìm thấy bất kỳ câu hỏi nào được giải quyết chính xác với sự cố này.

+2

FYI: vấn đề này không tồn tại khi bạn chỉ sử dụng EJB riêng của Java EE thay vì Spring. – BalusC

+0

@BalusC Vâng, tôi đọc về điều đó trong các câu hỏi khác, không may là tôi không biết đủ về EJB để sử dụng nó (và tôi không biết liệu tôi có thể thuyết phục các đồng nghiệp để tôi thử nó trong dự án này) . Bạn có thể chỉ cho tôi một nguồn tài nguyên tốt để tìm hiểu về nó, btw? – elias

+0

Nó không phải là khó khăn. Chỉ cần chắc chắn rằng container của bạn hỗ trợ EJB (Glassfish, JBoss, Weblogic, v.v.). Lớp dịch vụ chú thích với '@ Stateless' hoặc' @ Stateful' và chèn nó bằng '@ EJB'. Đó là nó. Không có getter/setter yêu cầu btw. – BalusC

Trả lời

3

Thay vì tiêm đậu xuân qua EL trong chú thích @ManagedProperty (được thực thi trên khởi chạy ManagedBean), hãy lấy các hạt đánh giá EL khi chạy.

Với phương pháp này, đây là những gì Đậu JSF nên hình như:

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private static SomeService someService() { 
     return SpringJSFUtil.getBean("someService"); 
    } 
    // ... 

Và lớp tiện ích SpringJSFUtil.java mà được đậu qua EL:

import javax.faces.context.FacesContext; 

public class SpringJSFUtil { 

    public static <T> T getBean(String beanName) { 
     if (beanName == null) { 
      return null; 
     } 
     return getValue("#{" + beanName + "}"); 
    } 

    @SuppressWarnings("unchecked") 
    private static <T> T getValue(String expression) { 
     FacesContext context = FacesContext.getCurrentInstance(); 
     return (T) context.getApplication().evaluateExpressionGet(context, 
       expression, Object.class); 
    } 
} 

ngày loại bỏ này tài sản bean Spring (với chi phí thực hiện một vài đánh giá EL), do đó tránh được tất cả các vấn đề serialization của việc sở hữu tài sản ở vị trí đầu tiên.

Các phương pháp tương tự, sử dụng OmniFaces:

Trong mã thực tế của tôi, tôi sử dụng phương pháp evaluateExpressionGet(String expression) của một utility class sẵn từ OmniFaces. Vì vậy, đối với những người bạn của những người sử dụng nó quá, đây là những gì mã của tôi thực sự trông giống như:

import static org.omnifaces.util.Faces.evaluateExpressionGet; 

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private static SomeService someService() { 
     return evaluateExpressionGet("#{someService}"); 
    } 
    // ... 

Chú ý rằng đây phương pháp được toàn bộ EL ("# {biểu}"), không chỉ là mùa xuân tên bean (hoặc bạn sẽ nhận được một ClassCastException).

-1

Vâng ghi nhớ điều này từ hướng dẫn Spring ( link to spring):

Constructor-based hoặc setter dựa trên DI?

Vì bạn có thể kết hợp cả hai, DI constructor- và Setter-based DI, nên sử dụng các đối số khởi tạo cho các phụ thuộc bắt buộc và các bộ định vị cho các phụ thuộc tùy chọn. Lưu ý rằng việc sử dụng chú thích @Required trên bộ đặt có thể được sử dụng để làm cho các bộ định cư được yêu cầu phụ thuộc.

Nhóm Spring thường chủ trương tiêm setter, bởi vì số lượng lớn các đối số hàm tạo có thể khó sử dụng, đặc biệt khi các thuộc tính là tùy chọn. Phương pháp Setter cũng làm cho các đối tượng của lớp đó tuân theo cấu hình lại hoặc tái tiêm sau này. Quản lý thông qua JMX MBeans là một trường hợp sử dụng hấp dẫn.

Một số người theo chủ nghĩa thuần túy ủng hộ việc xây dựng dựa trên hàm tạo. Việc cung cấp tất cả các phụ thuộc đối tượng có nghĩa là đối tượng luôn được trả về cho mã khách hàng (gọi) trong trạng thái khởi tạo hoàn toàn. Điểm bất lợi là đối tượng trở nên ít tuân theo để cấu hình lại và tái tiêm.

Sử dụng DI có ý nghĩa nhất đối với một lớp cụ thể. Đôi khi, khi giao dịch với các lớp của bên thứ ba mà bạn không có nguồn, sự lựa chọn được thực hiện cho bạn. Một lớp kế thừa có thể không trưng ra bất kỳ phương thức setter nào, và do đó, việc xây dựng injection là chỉ có sẵn DI.

+0

Xin lỗi, tôi không theo dõi ... DI dựa trên constructor dựa trên setter dựa trên DI phải làm gì với vấn đề của tôi? – elias

2

Hãy thử @Scope (giá trị = BeanDefinition.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.INTERFACES) trên Spring @Service của bạn. Điều này sẽ tiêm một đối tượng proxy tuần tự hóa vào bean được quản lý của bạn sẽ di chuyển dịch vụ khi truy cập sau khi deserialization.

0

Đối với những người cần theo dõi - Tôi gặp vấn đề tương tự với một ResourceBundle được tiêm. Sử dụng một phần câu trả lời của BalusC, tôi đã làm như sau:

@ManagedProperty(value="#{myBundle}") 
private transient ResourceBundle myBundle; 

private Object readResolve() { 
    myBundle = FacesContext.getCurrentInstance().getApplication() 
     .evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{myBundle}", 
     ResourceBundle.class); 
    return this; 
} 

Bằng cách này, EL chỉ được đánh giá khi đậu được quản lý không được khử muối.

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