首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >如何找到 SpringBoot 应用中的所有 BeanFactory

如何找到 SpringBoot 应用中的所有 BeanFactory

作者头像
AlphaHinex
发布2026-03-16 15:11:36
发布2026-03-16 15:11:36
1060
举报
文章被收录于专栏:周拱壹卒周拱壹卒

还在给每个请求加前缀避免模块间接口冲突呢?[1] 中,我们讨论了在一个 Spring Boot 应用中注册多个 DispatcherServlet 来实现应用上下文隔离的方案,以达到在不同 Servlet 关联的上下文中,注册相同 RequestMappingController,乃至相同名称的 Bean 的效果。

在实际使用这种模式时,可能会遇到某些原因导致上下文隔离的效果跟预期不一致的情况,比如 SpringBootApplication 启动类上使用了 @ComponentScan 注解,导致某些 Bean 被注册到了多个上下文中,从而引发一些奇怪的问题。

为了排查这些问题,我们需要找到应用中所有的 BeanFactory,以及它们各自注册了哪些 Bean。

本文仍以 multi-dispatcher[2] demo 工程为例,给出通过 debug 方式加入的断点位置,以观察所有的 BeanFactoryApplicationContext

断点行数以 Spring Boot 2.2.2.RELEASE 和 Spring Framework 5.2.2.RELEASE 为例,不同版本可能会有差异。

根 ApplicationContext 及 BeanFactory

通常,Spring Boot 应用启动时,会包含如下 main 方法:

代码语言:javascript
复制
public static void main(String[] args) {
    SpringApplication.run(WebApplication.class, args);
}

run 方法返回的 ConfigurableApplicationContext 就是根 ApplicationContext

代码语言:javascript
复制
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args)

BeanFactory 保存在 Spring Context 中提供的 GenericApplicationContext 类的 beanFactory 字段中:

代码语言:javascript
复制
private final DefaultListableBeanFactory beanFactory;

断点:SpringApplication:311

我们可以在 SpringApplication.run 方法中调用 createApplicationContext 方法处加入断点:

代码语言:javascript
复制
context = createApplicationContext();

断点:SpringApplication:588

createApplicationContext 方法会根据 Web 应用类型初始化不同的 ApplicationContext。对于本例中的 Servlet Web 应用来说,通常会返回 AnnotationConfigServletWebServerApplicationContext

代码语言:javascript
复制
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            thrownew IllegalStateException(
                    "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

断点:ClassPathBeanDefinitionScanner:273

ClassPathBeanDefinitionScanner 类的 doScan 方法可以观察到当前初始化的 BeanFactory 会扫描的基础包:

代码语言:javascript
复制
protected Set<BeanDefinitionHolder> doScan(String... basePackages)

断点:DefaultListableBeanFactory:853

DefaultListableBeanFactory 类的 preInstantiateSingletons 方法可以观察到当前 BeanFactory 中包含的 Bean 定义,preInstantiateSingletons 方法会在 BeanFactory 创建的最终阶段被调用:

代码语言:javascript
复制
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

每个 Servlet 关联的 WebApplicationContext 及 BeanFactory

断点:FrameworkServlet:530

DispatcherServlet 的父类 FrameworkServletinitServletBean 方法中,会对 Servlet 关联的 WebApplicationContext 进行初始化:

代码语言:javascript
复制
/**
 * Overridden method of {@link HttpServletBean}, invoked after any bean properties
 * have been set. Creates this servlet's WebApplicationContext.
 */
@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
    if (logger.isInfoEnabled()) {
        logger.info("Initializing Servlet '" + getServletName() + "'");
    }
    long startTime = System.currentTimeMillis();

    try {
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (logger.isDebugEnabled()) {
        String value = this.enableLoggingRequestDetails ?
                "shown which may lead to unsafe logging of potentially sensitive data" :
                "masked to prevent unsafe logging of potentially sensitive data";
        logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                "': request parameters and headers will be " + value);
    }

    if (logger.isInfoEnabled()) {
        logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
    }
}

断点:AbstractRefreshableApplicationContext:130

AbstractRefreshableApplicationContext 类的 refreshBeanFactory 方法会创建 WebApplicationContextBeanFactory

代码语言:javascript
复制
/**
 * This implementation performs an actual refresh of this context's underlying
 * bean factory, shutting down the previous bean factory (if any) and
 * initializing a fresh bean factory for the next phase of the context's lifecycle.
 */
@Override
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        thrownew ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

Demo 应用中的 BeanFactory 和 ApplicationContext 列表

Root

BeanFactory

ApplicationContext

ID

application

org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e01f9b0

Code

@3157

@3149

Parent

null

null

Bar Servlet

BeanFactory

ApplicationContext

ID

org.springframework.web.context.WebApplicationContext:/Bar servlet

org.springframework.web.context.WebApplicationContext:/Bar servlet

Code

@5578

@5591

Parent

@3157

@3149

Foo Servlet

BeanFactory

ApplicationContext

ID

org.springframework.web.context.WebApplicationContext:/Foo servlet

org.springframework.web.context.WebApplicationContext:/Foo servlet

Code

@5778

@5777

Parent

@3157

@3149

参考资料

[1]

还在给每个请求加前缀避免模块间接口冲突呢?: https://alphahinex.github.io/2020/04/24/multi-dispatcherservlet/

[2]

multi-dispatcher: https://github.com/AlphaHinex/multi-dispatcher

还在给每个请求加前缀避免模块间接口冲突呢?
还在给每个请求加前缀避免模块间接口冲突呢?

还在给每个请求加前缀避免模块间接口冲突呢?

Spring 中 BeanFactory 和 ApplicationContext 的关系梳理
Spring 中 BeanFactory 和 ApplicationContext 的关系梳理

Spring 中 BeanFactory 和 ApplicationContext 的关系梳理

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 周拱壹卒 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 根 ApplicationContext 及 BeanFactory
    • 断点:SpringApplication:311
      • 断点:SpringApplication:588
      • 断点:ClassPathBeanDefinitionScanner:273
      • 断点:DefaultListableBeanFactory:853
  • 每个 Servlet 关联的 WebApplicationContext 及 BeanFactory
    • 断点:FrameworkServlet:530
      • 断点:AbstractRefreshableApplicationContext:130
  • Demo 应用中的 BeanFactory 和 ApplicationContext 列表
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档