Spring Boot如何实现单例常驻服务供全局共享访问
实现说明
你对Spring Bean生命周期的认知存在偏差:Spring 管理的 Bean 默认作用域为 singleton 单例,在整个应用上下文启动后仅会创建一个实例,实例内的字段状态会在应用运行期间一直留存,完全满足你「全局唯一常驻、跨请求共享状态、全应用可调用」的需求,不需要额外做自定义的单例管控。
你当前的代码没有被Spring容器托管,所以才会出现无法全局复用、状态不留存的问题,按以下步骤调整即可。
具体修改步骤
- 给
LimboQueueImpl类添加Spring业务组件注解,标记为被容器托管的Bean:@Service public class LimboQueueImpl implements LimboQueue { // 原有业务逻辑无需改动 private ConcurrentLinkedQueue<Long> queue = new ConcurrentLinkedQueue<Long>(); private int numberCounter = 0; @Override public String getCurrentUser() { return null; } @Override public boolean addPetition(Long userId) throws UserNotFoundException { int ticketNumber = this.addNumber(); // TODO: save ticket return this.queue.add(userId); } @Override public Long getNextPetition() { return this.queue.isEmpty() ? null : this.queue.remove(); } @Override public boolean deletePetition(Long userId) { return this.queue.remove(userId); } private synchronized int addNumber() { return this.numberCounter++; } private synchronized void resetNumberCounter() { this.numberCounter = 0; } } - 所有需要调用该服务的位置(Controller、其他业务Service类)禁止手动
new LimboQueueImpl()创建实例,统一通过Spring依赖注入获取实例,推荐使用构造器注入,示例:@RestController @RequestMapping("/petitions") public class PetitionController { private final LimboQueue limboQueue; // Spring 4.3及以上版本,单构造器场景无需额外添加@Autowired注解 public PetitionController(LimboQueue limboQueue) { this.limboQueue = limboQueue; } @PostMapping public ResponseEntity<Boolean> add(@RequestParam Long userId) throws UserNotFoundException { return ResponseEntity.ok(limboQueue.addPetition(userId)); } @GetMapping("/next") public ResponseEntity<Long> getNext() { return ResponseEntity.ok(limboQueue.getNextPetition()); } }
并发优化建议
你当前代码的并发控制逻辑基本可用,有两处可以优化的点:
ConcurrentLinkedQueue本身是线程安全的无界队列,自带的增、删、判空方法都保证原子性,不需要额外加同步控制。- 计数器
numberCounter当前用synchronized修饰操作方法可以保证线程安全,更轻量的实现是替换为AtomicInteger,省去手动加锁的开销:// 替换原int类型计数器定义 private final AtomicInteger numberCounter = new AtomicInteger(0); private int addNumber() { return numberCounter.incrementAndGet(); } private void resetNumberCounter() { numberCounter.set(0); }
注意:这种内存存储状态的方式仅适用于单实例部署场景。如果后续应用做集群多实例部署,每个应用实例会持有独立的队列和计数器,多实例间状态不互通,这种场景需要将共享状态下沉到Redis等集中式存储组件保证全局一致性。
内容的提问来源于stack exchange,提问作者Miguel Moreton
相关产品推荐
相关产品推荐

