JavaSE中的Weld CDI:PreDestroy注释方法调用得太早?
给定以下代码,我想知道为什么在调用@PreDestroy注释方法(CacheManager#doCleanup)后CacheManager仍然“活动”(请参阅本文末尾的输出)。 Weld不知道它仍然被引用吗?当对象真的不再使用时,如何调用这个方法
主类
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();
}
}
周期扫描类强>
@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);
}
}
以及缓存管理器类:
@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();
这样,主应用程序线程就不会退出
有人知道更好的方法吗
谢谢
我
# 1 楼答案
输出是预期的-Weld无法知道旋转的其他线程,主线程只是继续运行,直到到达
container.shutdown()
这个方法(令人惊讶地)终止了容器,这意味着调用
@PreDestroy
方法,然后放弃这些bean的引用。但是另一个线程仍然在使用这些实例你能做的是:
container.shutdown()
移出主方法main()
方法退出后继续工作container.shutdown()
放在一个方法中,该方法将在执行器完成后调用(取决于您的代码)container.shutdown()
顺便说一句——如果您只是想在主线程中创建一个“等待”来让另一个线程完成任务,那么最好将该逻辑放到主线程中