Skip to content

customizeBeanFactory方法扩展点

一、customizeBeanFactory的定位:容器初始化流程中的关键节点

customizeBeanFactoryAbstractRefreshableApplicationContext类中的protected方法,属于Spring容器初始化流程中BeanFactory定制的核心扩展点。其调用链路如下:

1. 容器初始化入口:refresh()方法

Spring ApplicationContext的初始化核心是AbstractApplicationContext#refresh()方法,该方法定义了容器启动的标准流程(共12个步骤)。其中,获取/刷新BeanFactory是第2步(obtainFreshBeanFactory())。

2. 刷新BeanFactory:refreshBeanFactory()

obtainFreshBeanFactory()会调用AbstractRefreshableApplicationContext#refreshBeanFactory(),该方法负责销毁旧BeanFactory(若存在)创建新BeanFactory加载Bean定义。其关键逻辑如下:

java
@Override
protected final void refreshBeanFactory() throws BeansException {
    // 1. 销毁旧BeanFactory(若已存在)
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 2. 创建新的DefaultListableBeanFactory(Spring默认的BeanFactory实现)
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId()); // 设置序列化ID,用于跨JVM反序列化
        // 3. 定制BeanFactory(扩展点:子类重写此方法)
        customizeBeanFactory(beanFactory);
        // 4. 加载Bean定义(如XML、注解、配置类等)
        loadBeanDefinitions(beanFactory);
        // 5. 保存BeanFactory实例
        this.beanFactory = beanFactory;
    } catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source", ex);
    }
}

3. 定制BeanFactory:customizeBeanFactory()

customizeBeanFactory()refreshBeanFactory()中的第3步位于BeanFactory创建之后、Bean定义加载之前。其作用是允许子类修改BeanFactory的配置,如全局属性、类型转换器、Scope等。

二、customizeBeanFactory的默认实现与核心作用

AbstractRefreshableApplicationContextcustomizeBeanFactory()的默认实现如下:

java
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    // 1. 设置是否允许Bean定义覆盖(默认:true)
    if (this.allowBeanDefinitionOverriding != null) {
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // 2. 设置是否允许循环引用(默认:true)
    if (this.allowCircularReferences != null) {
        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    }
}

1. 核心作用1:控制Bean定义覆盖(allowBeanDefinitionOverriding

  • 默认值true(允许同名Bean定义覆盖,后定义的Bean会覆盖前定义的)。
  • 场景:若需禁止同名Bean覆盖(如防止配置错误),可将其设为false,此时重复定义会抛出BeanDefinitionStoreException
  • 源码逻辑DefaultListableBeanFactory#registerBeanDefinition()会检查该属性,若为false且存在同名Bean定义,则抛出异常。

2. 核心作用2:控制循环引用(allowCircularReferences

  • 默认值true(允许循环引用,如A依赖B、B依赖A)。
  • 场景:若需禁止循环引用(如避免复杂依赖导致的初始化问题),可将其设为false,此时循环引用会抛出BeanCurrentlyInCreationException
  • 源码逻辑DefaultListableBeanFactory#doCreateBean()会根据该属性决定是否提前暴露半成品BeanaddSingletonFactory()),以解决循环引用。

三、customizeBeanFactory的扩展场景:除了默认属性,还能做什么?

customizeBeanFactory()的价值远不止修改默认的两个属性,它允许开发者深度定制BeanFactory,满足各种复杂需求。以下是常见的扩展场景:

1. 注册自定义Scope(如线程范围、会话范围)

Scope用于定义Bean的生命周期(如singletonprototype)。通过beanFactory.registerScope()可注册自定义Scope,例如线程范围SimpleThreadScope):

java
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    super.customizeBeanFactory(beanFactory);
    // 注册线程范围的Scope(key为"thread")
    beanFactory.registerScope("thread", new SimpleThreadScope());
}

使用场景:需为每个线程创建独立的Bean实例(如Controller中的线程局部变量)。

2. 添加自定义ConversionService(类型转换)

ConversionService用于将配置中的字符串(如XML/注解中的值)转换为目标类型(如StringLocalDate)。通过beanFactory.setConversionService()可添加自定义转换器:

java
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    super.customizeBeanFactory(beanFactory);
    // 创建默认转换服务
    DefaultConversionService conversionService = new DefaultConversionService();
    // 添加自定义转换器(String→LocalDate)
    conversionService.addConverter(new Converter<String, LocalDate>() {
        @Override
        public LocalDate convert(String source) {
            return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }
    });
    // 设置转换服务
    beanFactory.setConversionService(conversionService);
}

使用场景:需自定义类型转换规则(如日期格式、枚举转换)。

3. 设置自定义BeanExpressionResolver(SpEL扩展)

BeanExpressionResolver用于解析SpEL表达式(如@Value("#{systemProperties['user.name']}"))。通过beanFactory.setBeanExpressionResolver()可扩展SpEL功能:

java
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    super.customizeBeanFactory(beanFactory);
    // 创建标准SpEL解析器
    StandardBeanExpressionResolver resolver = new StandardBeanExpressionResolver();
    // 扩展SpEL:添加自定义函数(如"myFunction")
    ExpressionParser parser = new SpelExpressionParser();
    parser.getConfiguration().addFunction("myFunction", MyUtil.class.getMethod("myMethod", String.class));
    resolver.setExpressionParser(parser);
    // 设置表达式解析器
    beanFactory.setBeanExpressionResolver(resolver);
}

使用场景:需在SpEL中调用自定义函数(如加密/解密、字符串处理)。

4. 手动注册BeanPostProcessor

BeanPostProcessor用于拦截Bean的初始化过程(如@Autowired注入、@PostConstruct处理)。虽然通常通过@Component注册,但也可在customizeBeanFactory()中手动添加:

java
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    super.customizeBeanFactory(beanFactory);
    // 手动注册自定义BeanPostProcessor(无需@Component注解)
    beanFactory.addBeanPostProcessor(new CustomBeanPostProcessor());
}

注意:手动注册的BeanPostProcessor不会参与Spring的排序(如@Order注解),执行顺序为添加顺序。

5. 修改BeanFactory的类加载器

通过beanFactory.setBeanClassLoader()可设置自定义类加载器(如加载外部Jar包中的类):

java
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    super.customizeBeanFactory(beanFactory);
    // 设置类加载器为当前线程的上下文类加载器(常用于动态加载类)
    beanFactory.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
}

四、customizeBeanFactory与其他扩展点的区别

为了更清晰地理解customizeBeanFactory的定位,我们将其与Spring中其他常见扩展点对比:

扩展点触发时机核心作用示例场景
customizeBeanFactoryBeanFactory创建后,Bean定义加载前定制BeanFactory的全局配置修改allowBeanDefinitionOverriding、注册Scope
BeanFactoryPostProcessorBean定义加载后,Bean实例化前修改Bean定义(如属性值)修改@Value中的占位符
BeanPostProcessorBean实例化后,初始化前后拦截Bean初始化(如注入)处理@Autowired注解
InitializingBeanBean初始化完成后执行Bean的自定义初始化逻辑初始化数据库连接

五、customizeBeanFactory的实践案例:禁止Bean定义覆盖

假设我们需要禁止同名Bean定义覆盖(防止配置错误),可通过以下步骤实现:

1. 自定义ApplicationContext

继承AbstractRefreshableApplicationContext(或其子类,如AnnotationConfigApplicationContext),重写customizeBeanFactory()

java
public class NoOverrideApplicationContext extends AnnotationConfigApplicationContext {
    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        super.customizeBeanFactory(beanFactory);
        // 禁止Bean定义覆盖(设为false)
        beanFactory.setAllowBeanDefinitionOverriding(false);
    }
}

2. 使用自定义ApplicationContext启动容器

java
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        // 使用自定义ApplicationContext启动Spring Boot
        SpringApplication application = new SpringApplication(Application.class);
        application.setApplicationContextClass(NoOverrideApplicationContext.class);
        application.run(args);
    }
}

3. 测试效果

若存在同名Bean定义(如两个@Component("user")),容器启动时会抛出BeanDefinitionStoreException

org.springframework.beans.factory.BeanDefinitionStoreException: 
Cannot register bean definition [Root bean: class [com.example.User]; scope=singleton; ...] 
for bean 'user': There is already [Root bean: class [com.example.User]; scope=singleton; ...] bound.

六、注意事项

  1. 调用时机customizeBeanFactory()loadBeanDefinitions()之前执行,此时Bean定义尚未加载,无法获取Bean实例(如beanFactory.getBean())。
  2. BeanFactory类型customizeBeanFactory()的参数是DefaultListableBeanFactory(Spring默认的BeanFactory实现),若需使用自定义BeanFactory,可重写createBeanFactory()方法(如返回CustomBeanFactory子类)。
  3. 属性默认值allowBeanDefinitionOverridingallowCircularReferences的默认值为true,若未修改,customizeBeanFactory()不会改变其值。
  4. 扩展边界customizeBeanFactory()主要用于定制BeanFactory的配置,而非修改Bean定义(后者应使用BeanFactoryPostProcessor)。

七、总结

customizeBeanFactory是Spring容器初始化过程中最早期的扩展点之一,其核心价值在于允许开发者深度定制BeanFactory的全局配置。通过重写该方法,我们可以:

  • 控制Bean定义覆盖和循环引用;
  • 注册自定义Scope、ConversionService、BeanExpressionResolver;
  • 手动添加BeanPostProcessor;
  • 修改类加载器等。

理解customizeBeanFactory的底层逻辑与应用场景,有助于我们在Spring框架中实现更灵活、更符合需求的容器定制,提升系统的可扩展性和稳定性。

参考源码

  • org.springframework.context.support.AbstractRefreshableApplicationContext#customizeBeanFactory
  • org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
  • org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
  • org.springframework.context.support.AbstractApplicationContext#refresh