2017-09-06 78 views
0

給出以下代碼,我想知道爲什麼在調用@PreDestroy註釋方法(CacheManager#doCleanup)之後CacheManager仍然「活着」(請參閱​​本文後面的輸出)。 不是Weld意識到它仍然被引用的事實嗎?以及如何在對象真的不再使用時調用此方法?在Java SE中焊接CDI:PreDestroy註釋的方法太早調用?

主類

public class Main { 
    public static void main(String[] parameters) { 
     //Init weld container   
     Weld weld = new Weld(); 
     WeldContainer container = weld.initialize(); 
     container.select(MyLauncher.class).get().startScanner(); 
     weld.shutdown(); 
    } 
} 

MyLaucher類

@Singleton 
public class MyLauncher { 

    @Inject 
    private Logger logger; 
    @Inject 
    private PeriodicScanner periodicScanner; 

    public Future startScanner() { 
     logger.info("Starting file producers..."); 
     return periodicScanner.doScan(); 
    } 
} 

PeriodicScanner類...

@Singleton 
public class PeriodicScanner { 

    @Inject 
    private Logger logger; 
    @Inject 
    private CacheManager myCacheMgr; 
    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder() 
      .setNameFormat("periodic-%d") 
      .build()); 

    public Future doScan() { 
     return scheduledExecutorService.scheduleAtFixedRate(() -> { 
      myCacheMgr.doStuff(); 
      logger.info("Hello from PeriodicScanner"); 
     }, 1, 15, TimeUnit.SECONDS); 
    } 

} 

的CacheManager類

@Singleton 
public class CacheManager { 
    @Inject 
    Logger logger; 

    @PostConstruct 
    private void doInit(){ 
     logger.info("PostConstruct called for ID {}", this.hashCode()); 
    } 

    @PreDestroy 
    private void doCleanup(){ 
     logger.info("Cleaning up for ID {}", this.hashCode()); 
    } 

    public int doStuff(){ 
     logger.info("Doing stuff from instance ID {}", this.hashCode()); 
     return 1; 
    } 
} 

輸出是:

Sep 06, 2017 3:47:51 PM org.jboss.weld.bootstrap.WeldStartup <clinit> 
INFO: WELD-000900: 2.4.4 (Final) 
Sep 06, 2017 3:47:51 PM org.jboss.weld.bootstrap.WeldStartup startContainer 
INFO: WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously. 
Sep 06, 2017 3:47:52 PM org.jboss.weld.environment.se.WeldContainer fireContainerInitializedEvent 
INFO: WELD-ENV-002003: Weld SE container 2d18aac9-f66d-4373-b581-9c5cababd65a initialized 
[main] INFO com.mycompany.cdiplayground.CacheManager - PostConstruct called for ID 611572016 
[main] INFO com.mycompany.cdiplayground.MyLauncher - Starting file producers... 
[main] INFO com.mycompany.cdiplayground.CacheManager - Cleaning up for ID 611572016 
Sep 06, 2017 3:47:52 PM org.jboss.weld.environment.se.WeldContainer shutdown 
INFO: WELD-ENV-002001: Weld SE container 2d18aac9-f66d-4373-b581-9c5cababd65a shut down 
[periodic-0] INFO com.mycompany.cdiplayground.CacheManager - Doing stuff from instance ID 611572016 
[periodic-0] INFO com.mycompany.cdiplayground.PeriodicScanner - Hello from PeriodicScanner 
[periodic-0] INFO com.mycompany.cdiplayground.CacheManager - Doing stuff from instance ID 611572016 
[periodic-0] INFO com.mycompany.cdiplayground.PeriodicScanner - Hello from PeriodicScanner 

正如你所看到的,定期掃描儀仍然是集裝箱關機後還活着。目前,由我來防止doCleanup()只有這樣,才能被稱爲太早是)由startScanner(返回未來的對象上調用get():

container.select(MyLauncher.class).get().startScanner().get(); 

這樣,主應用程序線程不會退出。

有沒有人知道更好的方法來做到這一點?

感謝

+0

我認爲這個bevaiour是合理的。 CDI對調用其beans的* thread *一無所知。 CDI對豆類的「破壞」並不意味着垃圾收集它們,只是CDI稱他們的@ PreDestroy鉤子並忘記了它們。如果您想在CDI關閉後停止'PeriodicScanner',請嘗試在'PeriodicScanner'的'@ PreDestroy'鉤子中關閉'ScheduledExecutorService'。 –

回答

1

產量預計 - 焊接不知道你旋轉起來,並在主線程只是一直走,直到它到達container.shutdown()其他線程。

此方法(令人驚訝地)終止容器,這意味着調用@PreDestroy方法,然後釋放這些bean的引用。但另一個線程仍然繼續使用這些實例。

你可以做的是:

  • 移動container.shutdown()出主要方法
    • 焊接容器將繼續努力main()方法退出
    • 後,您應該將container.shutdown()到這將是一個方法在執行器完成後調用(取決於您的代碼)
  • 完全不
    • 焊接自稱container.shutdown()註冊一個shutdown hook觸發在JVM終止
    • 這種解決方案的可行性取決於你如何終止程序
    • 您也可以實現自己的關閉鉤子和註冊一個替代

作爲一個側面說明 - 如果你只是在尋找一種方式來創建一個「等待」在你的主線程只是讓另一個threa d做這項工作,那麼把這個邏輯放入主線程可能會更好。