2011-12-07 26 views
7

Tôi đang cố gắng tạo các cá thể của bean được quản lý bằng cách sử dụng BeanManager thay vì Instance .select(). Get().Làm thế nào để tạo và hủy diệt Đậu Quản lý CDI (Weld) thông qua BeanManager?

Điều này đã được đề xuất như là một giải pháp cho một vấn đề tôi đã gặp với hạt ApplicationScoped và thu gom rác của người phụ thuộc của họ - xem CDI Application and Dependent scopes can conspire to impact garbage collection? cho nền và giải pháp được đề xuất này.

Nếu bạn sử dụng phương pháp tra cứu lập trình Instance trên một hạt ApplicationScoped, đối tượng Instance và bất kỳ bean nào bạn nhận được từ nó đều phụ thuộc vào bean ApplicationScoped, và do đó chia sẻ vòng đời của nó. Tuy nhiên, nếu bạn tạo bean với BeanManager, bạn có một handle trên bản thân Bean, và rõ ràng có thể phá hủy nó một cách rõ ràng, cái mà tôi hiểu có nghĩa là nó sẽ được GC.

cách tiếp cận hiện tại của tôi là tạo ra đậu bên trong một lớp BeanManagerUtil, và trả về một đối tượng hỗn hợp của Bean, ví dụ, và CreationalContext:

public class BeanManagerUtil { 

    @Inject private BeanManager beanManager; 

    @SuppressWarnings("unchecked") 
    public <T> DestructibleBeanInstance<T> getDestructibleBeanInstance(final Class<T> type, 
      final Annotation... qualifiers) { 

     DestructibleBeanInstance<T> result = null; 
     Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(type, qualifiers)); 
     if (bean != null) { 
      CreationalContext<T> creationalContext = beanManager.createCreationalContext(bean); 
      if (creationalContext != null) { 
       T instance = bean.create(creationalContext); 
       result = new DestructibleBeanInstance<T>(instance, bean, creationalContext); 
      } 
     } 
     return result; 
    } 
} 

public class DestructibleBeanInstance<T> { 

    private T instance; 
    private Bean<T> bean; 
    private CreationalContext<T> context; 

    public DestructibleBeanInstance(T instance, Bean<T> bean, CreationalContext<T> context) { 
     this.instance = instance; 
     this.bean = bean; 
     this.context = context; 
    } 

    public T getInstance() { 
     return instance; 
    }  

    public void destroy() { 
     bean.destroy(instance, context); 
    } 
} 

Từ đó, trong đoạn code gọi, tôi có thể sau đó nhận được Ví dụ thực tế, đặt nó trong một bản đồ để thu hồi sau, và sử dụng như bình thường:

private Map<Worker, DestructibleBeanInstance<Worker>> beansByTheirWorkers = 
    new HashMap<Worker, DestructibleBeanInstance<Worker>>(); 
... 
DestructibleBeanInstance<Worker> destructible = 
     beanUtils.getDestructibleBeanInstance(Worker.class, workerBindingQualifier); 
Worker worker = destructible.getInstance(); 
... 

Khi tôi đang thực hiện với nó, tôi có thể tra cứu các wrapper phá hủy và gọi tiêu diệt() vào nó, và đậu và người phụ thuộc của nó nên được làm sạch:

DestructibleBeanInstance<JamWorker> workerBean = 
     beansByTheirWorkers.remove(worker); 
workerBean.destroy(); 
worker = null; 

Tuy nhiên, sau khi chạy một số công nhân và để lại JBoss của tôi (7.1.0.Alpha1-SNAPSHOT) trong vòng 20 phút hoặc lâu hơn, tôi có thể thấy GC xảy ra

2011.002: [GC 
Desired survivor size 15794176 bytes, new threshold 1 (max 15) 
1884205K->1568621K(3128704K), 0.0091281 secs] 

Tuy nhiên, một biểu đồ jmap vẫn show các công nhân cũ và các trường hợp phụ thuộc của họ treo quanh, unGCed. Tôi đang thiếu gì?

Thông qua gỡ lỗi, tôi có thể thấy rằng trường ngữ cảnh của bean được tạo có ngữ cảnh của loại Công việc chính xác, không có không đầy đủVật và không có parentDependentInstances. Nó có một số dependInstances, như mong đợi từ các lĩnh vực trên công nhân.

Một trong các trường này trên Công nhân thực sự là một Trường hợp và khi tôi so sánh trường này với trường Công nhân được truy xuất thông qua tra cứu Sơ đồ lập trình, chúng có trang điểm CreationalContext hơi khác. Trường Instance trên Worker được tra cứu thông qua Instance có bản thân nhân viên trong undompleteInstances, trong khi trường Instance trên Worker được lấy từ BeanManager thì không. Cả hai đều có parentDependentInstances và dependsInstances giống nhau.

Điều này gợi ý với tôi rằng tôi đã không phản ánh việc truy xuất phiên bản chính xác. Điều này có thể góp phần vào việc thiếu sự hủy diệt không?

Cuối cùng, khi gỡ lỗi, tôi có thể thấy bean.destroy() được gọi trong DestructibleBeanInstance.destroy() của tôi, và điều này đi qua để ManagedBean.destroy và tôi có thể thấy các đối tượng phụ thuộc bị hủy như một phần của .release(). Tuy nhiên họ vẫn không thu gom rác!

Bất kỳ trợ giúp nào về điều này sẽ được đánh giá rất nhiều! Cảm ơn.

Trả lời

2

Tôi muốn thay đổi một vài thứ trong mã bạn đã dán.

  1. Làm cho lớp đó trở thành lớp java thông thường, không được tiêm và vượt qua trong BeanManager. Một cái gì đó có thể được rối tung lên theo cách đó. Nó không có khả năng, nhưng có thể.
  2. Tạo CreationalContext mới bằng cách sử dụng BeanManager.createCreationContext(null), điều này sẽ cung cấp cho bạn một phạm vi phụ thuộc cơ bản mà bạn có thể phát hành khi bạn hoàn tất bằng cách gọi CreationalContext.release().

Bạn có thể để có được tất cả mọi thứ để làm việc một cách chính xác theo cách bạn muốn bằng cách gọi phương thức phát hành trên CreationalContext bạn đã có trong DestructibleBeanInstance, giả sử không có khác Beans trong CreationalContext rằng sẽ lộn xộn lên ứng dụng của bạn. Hãy thử điều đó trước và xem liệu nó có làm rối tung mọi thứ không.

+0

Cảm ơn một lần nữa, Jason. Tôi đã thực hiện các thay đổi bạn đã đề xuất nhưng vẫn không thấy bất kỳ bộ sưu tập rác nào. Tuy nhiên khi tôi chờ đợi một GC _full_, cả hai cách tiếp cận dẫn đến các đối tượng được thu thập - thành công! Đây không phải là trường hợp trên một GC đầy đủ trước đây. Nếu bạn có thời gian, vui lòng giải thích sự khác biệt giữa '.createCreationContext (null)' và '.createCreationContext (bean)'? Tôi đã nhìn thấy trước đây trong các tài liệu để viết một phần mở rộng, nhưng tôi nghĩ đó là khi phiên bản 'đậu' của loại (nếu bạn thấy những gì tôi có nghĩa là) đã không tồn tại? Cảm ơn một lần nữa vì sự giúp đỡ. –

+0

Theo spec, nó cung cấp cho bạn một thể hiện không theo ngữ cảnh của bean, do đó không nên có bất cứ điều gì khác có liên quan đến nó bên cạnh phần mã được chạy để tạo ra nó. Tôi đã hỏi Pete Muir về một số thông tin chi tiết bổ sung, nhưng tôi chưa nghe lại. – LightGuard

2

Việc bỏ qua chỉ nên thực hiện khi bạn tiêm một số lớp khác ngoài một bean. Trong trường hợp của bạn, bạn đang tiêm đậu. Tuy nhiên tôi vẫn mong đợi GC làm việc trong trường hợp này, vì vậy bạn có thể nộp một JIRA trong bộ theo dõi vấn đề Weld với một trường hợp thử nghiệm và các bước để tái sản xuất?

1

Cách tốt hơn để giải quyết vấn đề của bạn có thể là sử dụng proxy động để xử lý sự hủy diệt hạt. Mã để có được một cá thể lớp bean programaticaly sẽ là:

public static <B> B getBeanClassInstance(BeanManager beanManager, Class<B> beanType, Annotation... qualifiers) { 
    final B result; 
    Set<Bean<?>> beans = beanManager.getBeans(beanType, qualifiers); 
    if (beans.isEmpty()) 
     result = null; 
    else { 
     final Bean<B> bean = (Bean<B>) beanManager.resolve(beans); 
     if (bean == null) 
      result = null; 
     else { 
      final CreationalContext<B> cc = beanManager.createCreationalContext(bean); 
      final B reference = (B) beanManager.getReference(bean, beanType, cc); 
      Class<? extends Annotation> scope = bean.getScope(); 
      if (scope.equals(Dependent.class)) { 
       if (beanType.isInterface()) { 
        result = (B) Proxy.newProxyInstance(bean.getBeanClass().getClassLoader(), new Class<?>[] { beanType, 
          Finalizable.class }, new InvocationHandler() { 
         @Override 
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
          if (method.getName().equals("finalize")) { 
           bean.destroy(reference, cc); 
          } 
          try { 
           return method.invoke(reference, args); 
          } catch (InvocationTargetException e) { 
           throw e.getCause(); 
          } 
         } 
        }); 
       } else 
        throw new IllegalArgumentException("If the resolved bean is dependent scoped then the received beanType should be an interface in order to manage the destruction of the created dependent bean class instance."); 
      } else 
       result = reference; 
     } 
    } 
    return result; 
} 

interface Finalizable { 
    void finalize() throws Throwable; 
} 

Bằng cách này, mã người dùng đơn giản hơn. Nó không phải chăm sóc sự hủy diệt. Giới hạn của approuch này là trường hợp khi beanType nhận được không phải là một giao diện và lớp bean được giải quyết là @Dependent không được hỗ trợ. Nhưng dễ dàng để làm việc arround. Chỉ cần sử dụng một giao diện. Tôi đã thử nghiệm mã này (với JBoss 7.1.1) và nó cũng hoạt động đối với các bean phiên trạng thái phụ thuộc.

+0

Ý tưởng tuyệt vời, cảm ơn @Readren –

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