JAX-RS资源中@PreDestroy未触发,如何关闭/释放资源?
解决方案
首先,你的@PreDestroy没触发大概率是因为REST资源类默认是请求作用域(per-request),这个注解会在每个请求对应的资源实例销毁时触发,而非容器关闭时。以下是几种可靠的实现方式:
1. 用Jersey容器生命周期监听器
实现Jersey的ContainerLifecycleListener,它能监听容器的启动/关闭事件,适合在容器 shutdown 时清理资源:
import org.glassfish.jersey.server.ContainerLifecycleListener; import org.glassfish.jersey.server.Container; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; public class ExecutorCleanupListener implements ContainerLifecycleListener { private final ExecutorService executorService; public ExecutorCleanupListener(ExecutorService executorService) { this.executorService = executorService; } @Override public void onStartup(Container container) {} @Override public void onReload(Container container) {} @Override public void onShutdown(Container container) { executorService.shutdown(); // 可选:等待任务完成,超时则强制终止 try { if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } } }
然后在你的Jersey应用类里注册这个监听器,同时把ExecutorService绑定为可注入的服务:
import jakarta.ws.rs.ApplicationPath; import org.glassfish.jersey.server.ResourceConfig; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @ApplicationPath("/api") public class MyApplication extends ResourceConfig { private final ExecutorService executorService = Executors.newFixedThreadPool(5); public MyApplication() { // 注册REST资源类 register(MyResource.class); // 注册清理监听器 register(new ExecutorCleanupListener(executorService)); // 绑定ExecutorService,方便资源类注入 bind(executorService).to(ExecutorService.class); } }
资源类通过注入获取ExecutorService,无需自行创建:
import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.inject.Inject; import java.util.concurrent.ExecutorService; @Path("/test") public class MyResource { private final ExecutorService executorService; @Inject public MyResource(ExecutorService executorService) { this.executorService = executorService; } @GET public String doGet() { executorService.submit(() -> { // 执行异步任务逻辑 }); return "OK"; } }
2. 将ExecutorService包装为Singleton Bean并使用@PreDestroy
把ExecutorService包装成单例Bean,Jersey会在容器关闭时调用它的@PreDestroy方法:
import jakarta.annotation.PreDestroy; import jakarta.inject.Singleton; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @Singleton public class ExecutorHolder { private final ExecutorService executorService = Executors.newFixedThreadPool(5); public ExecutorService getExecutorService() { return executorService; } @PreDestroy public void cleanup() { executorService.shutdown(); try { if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } } }
在应用类注册这个Bean,资源类注入使用:
@ApplicationPath("/api") public class MyApplication extends ResourceConfig { public MyApplication() { register(MyResource.class); register(ExecutorHolder.class); } } // 资源类 @Path("/test") public class MyResource { private final ExecutorService executorService; @Inject public MyResource(ExecutorHolder executorHolder) { this.executorService = executorHolder.getExecutorService(); } // ... REST方法逻辑 }
3. 使用ServletContextListener(Servlet容器层面)
基于Jetty的Servlet容器特性,实现ServletContextListener,在容器销毁时清理资源:
import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @WebListener public class ExecutorServletListener implements ServletContextListener { private ExecutorService executorService; @Override public void contextInitialized(ServletContextEvent sce) { executorService = Executors.newFixedThreadPool(5); // 将Executor存入ServletContext,供资源类获取 sce.getServletContext().setAttribute("executorService", executorService); } @Override public void contextDestroyed(ServletContextEvent sce) { executorService.shutdown(); try { if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } } }
资源类中通过ServletContext获取ExecutorService:
import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.inject.Inject; import jakarta.servlet.ServletContext; import java.util.concurrent.ExecutorService; @Path("/test") public class MyResource { private final ExecutorService executorService; @Inject public MyResource(ServletContext servletContext) { this.executorService = (ExecutorService) servletContext.getAttribute("executorService"); } // ... REST方法逻辑 }
注意:需确保Servlet容器能扫描到@WebListener注解,或在web.xml中手动配置该监听器。
内容的提问来源于stack exchange,提问作者Shaik Zakir Hussain
相关产品推荐
相关产品推荐

