Spring使用ApplicationContext报InstanceAlreadyExistsException异常
问题根因
抛出BeanCreationException嵌套InstanceAlreadyExistsException的核心原因是重复创建了两个独立的Spring应用上下文:
- 代码中
SpringApplication.run(DemoApplication.class, args)执行时,已经启动了完整的Spring Boot容器,自动完成了配置扫描、Bean注册、JMX管理Bean注册的全流程,其中就包括名为org.springframework.boot:type=Admin,name=SpringApplication的MBean。 - 后续手动实例化
new AnnotationConfigApplicationContext(ApplicationConfig.class)会启动第二个独立容器,启动过程中会再次尝试注册同名JMX MBean,而JMX规范不允许同标识MBean重复注册,直接触发异常。
除此之外你的代码里还有两个暂未触发的隐藏问题,等JMX冲突解决后就会报新的Bean创建错误:
UserDAO的构造方法用private修饰,Spring无法通过反射调用私有构造器实例化BeanUserDAO没有添加@Component等Spring托管注解,也没有在配置类中通过@Bean显式注册,默认不会被容器识别管理
修复方案
根据你的学习目标二选一即可:
方案1:标准Spring Boot开发模式(日常开发推荐)
删除手动创建容器的冗余代码,直接使用SpringApplication.run返回的上下文即可。Spring Boot会自动扫描启动类所在包及子包下的@Configuration配置类,不需要手动加载:
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(DemoApplication.class, args); // 可直接从ctx中获取Bean做测试 } }
同时修复UserDAO的问题:
- 将构造方法的访问修饰符从
private改为public - 给类添加
@Component注解标记为Spring托管组件,单构造方法场景下Spring会自动注入构造器需要的DataSource参数,无需额外加@Autowired:
package com.example.demo; import org.springframework.stereotype.Component; import javax.sql.DataSource; @Component public class UserDAO { private DataSource dataSource; public UserDAO(DataSource dataSource) { this.dataSource = dataSource; } }
方案2:原生Spring注解容器练习模式
如果你是专门练习原生AnnotationConfigApplicationContext的使用逻辑,不想引入Spring Boot的自动配置能力,就不要使用@SpringBootApplication注解和SpringApplication.run启动方式,直接写纯main方法启动容器:
package com.example.demo; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class DemoApplication { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class); // 后续Bean测试逻辑写在这里 } }
该场景下同样需要把UserDAO的构造方法改为public,要么给类加@Component让配置类上的@ComponentScan扫描到,要么直接在ApplicationConfig中写@Bean方法显式注册UserDAO。
临时规避方案(不推荐生产使用)
如果确实有特殊场景需要启动两个容器,可以通过配置关闭JMX自动注册绕过MBean冲突,在application.properties中添加如下配置:
spring.jmx.enabled=false
该方式只是掩盖了重复创建容器的问题,多余的容器会占用额外内存,还会导致Bean注入逻辑混乱,非必要不要使用。
内容的提问来源于stack exchange,提问作者Sunnyshechka
相关产品推荐
相关产品推荐

