
@postconstruct方法本应在spring容器初始化bean时仅执行一次,若在浏览器刷新时反复触发,说明该bean未以单例(singleton)模式正确托管——根本原因是bean作用域配置错误或类未被spring容器管理。
@postconstruct方法本应在spring容器初始化bean时仅执行一次,若在浏览器刷新时反复触发,说明该bean未以单例(singleton)模式正确托管——根本原因是bean作用域配置错误或类未被spring容器管理。
@PostConstruct 是 Spring 中用于标识初始化逻辑的注解,其语义明确:仅在 Bean 被 Spring 容器创建并完成依赖注入后执行一次。它绝不会因 HTTP 请求、WebSocket 连接建立或浏览器刷新而重复触发。因此,当你观察到 @PostConstruct 方法在每次页面刷新时都被调用,这并非正常行为,而是暴露了底层 Bean 生命周期管理的异常。
最常见且核心的原因是:目标类未被 Spring 正确识别为受管 Bean,或被显式声明为 prototype 作用域。
例如,在你提供的代码中:
@Autowired
private WebPushDao temp;
private static WebPushDao service;
@PostConstruct
private void WebPushDao() { service = temp; }该方法虽加了 @PostConstruct,但若其所在类(如 WebSocket Endpoint 类)未通过 @Component、@Service 等 Spring 注解声明,也未在配置类中通过 @Bean 显式注册,则 Spring 容器根本不会管理该实例——此时该类极可能由 Tomcat 或 Java EE 容器(如通过 ServerEndpoint 注解)直接实例化,每次 WebSocket 握手或会话创建都生成新对象,导致 @PostConstruct 被反复执行。
立即学习“Java免费学习笔记(深入)”;
另一个典型场景是误用 @Scope("prototype")。Spring 默认作用域为 singleton,但一旦显式指定 prototype,每次 getBean() 调用都会新建实例,@PostConstruct 自然随之重复执行。参考如下对比示例:
@Configuration
class AppConfig {
@Bean
@Scope("prototype") // ⚠️ 错误:每次获取都新建
public C1 c1() {
return new C1();
}
@Bean // ✅ 默认 singleton,整个容器仅一个实例
public C2 c2() {
return new C2();
}
}
class C1 {
@PostConstruct
public void init() {
System.out.println("C1 init —— 每次 getBean 都执行");
}
}运行结果将输出两次 "C1 init",而 C2 仅一次。
✅ 正确实践建议:
- 确保 WebSocket Endpoint 类(如继承 TextWebSocketHandler 或标注 @ServerEndpoint 的类)同时被 Spring 托管:推荐使用 @Component + @ServerEndpoint(需配合 ServerEndpointExporter),或更稳妥地采用 Spring WebSocket 的 @MessageMapping 编程模型;
- 检查类上是否遗漏 @Component/@Service,或是否错误添加了 @Scope("prototype");
- 避免在非 Spring 管理对象中使用 @PostConstruct —— 它仅对 Spring Bean 生效;
- 若必须在 WebSocket 中初始化共享资源(如 WebPushDao),应将其抽取为独立的 @Service 单例 Bean,并通过 @Autowired 注入,而非在 Endpoint 内部手动赋值静态字段(静态字段易引发并发与内存泄漏风险)。
最后,你提到“仅在你的电脑复现”,这往往指向本地环境差异:检查 web.xml 是否存在重复配置、IDE(如 IntelliJ)是否启用热部署插件强制重载类、Tomcat 的 context.xml 是否启用了 reloadable="true" 导致上下文频繁重建——这些都可能间接导致 Spring 容器重启,从而触发所有 @PostConstruct 方法重新执行。统一清理缓存、禁用自动重载、验证 application.properties 中 spring.main.web-application-type= servlet 设置,可有效排除环境干扰。


















