Skip to content

面试速答(先看这里)

**一句话结论:**面对这个问题的时候,读者朋友们可能会有点懵逼,但是我们回到Bean初始化的本质上来看,Bean初始化有两个时机:

60秒标准回答:

面对这个问题的时候,读者朋友们可能会有点懵逼,但是我们回到Bean初始化的本质上来看,Bean初始化有两个时机

从这两个出发点来思考解决问题方案的话,大概有如下几种方式

如上,在加载Bean A的时候,一定会先初始化Bean B

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **直接依赖某Bean:**如上,在加载Bean A的时候,一定会先初始化Bean B
  • **DependsOn:**对于应用之外的二方或者三方库来说,因为我们不能修改外部库的代码,如果想要二方库的Bean在初始化之前就初始化我们内部的某个bean,就不能用第一种直接依赖的方式,可以使用 @DependsOn 注解来完成,如下代码所示:
  • **BeanFactoryPostProcessor:**前两种方式只能对于特定的Bean生效,如果我们希望某个Bean在其他所有Bean加载之前就初始化,用前面两种方式显然是不合适的,我们这个时候,就需要从Spring容器的生命周期中去找方法。

**记忆锚点:**Bean → DependsOn → Spring → bean → BeanFactoryPostProcessor → PrimaryBean

易错提醒:

  • 面对这个问题的时候,读者朋友们可能会有点懵逼,但是我们回到Bean初始化的本质上来看,Bean初始化有两个时机: Spring容器主动去初始化该Bean 其他Bean依赖该Bean,该Bean会先被初始化 从这两个出发点来思考解决问题方案的话,大概有如下几种方式: 直接依赖某Bean 如上,在加载Bean A的时候,一定会先初始化Bean B Depends…
  • 通过上面的参考文章我们可以知道,Spring的Bean在初始化之前,会通过 BeanFactoryPostProcessor#postProcessBeanFactory 对工厂进行处理,我们可以依赖这个特性,在此刻提前初始化我们需要的bean 这个时候我们通过控制台发现,PrimaryBean的初始化等级会优于其他Bean,如下所示: Order只能控制同…

加分表达:

  • 三方库:由独立于你和你公司以外的第三方开发,通常是公开可用的,广泛用于多个项目中 BeanFactoryPostProcessor 前两种方式只能对于特定的Bean生效,如果我们希望某个Bean在其他所有Bean加载之前就初始化,用前面两种方式显然是不合适的,我们这个时候,就需要从Spring容器的生命周期中去找方法。

追问准备:

  • 围绕「Bean」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「DependsOn」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「Spring」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

面对这个问题的时候,读者朋友们可能会有点懵逼,但是我们回到Bean初始化的本质上来看,Bean初始化有两个时机:

  1. Spring容器主动去初始化该Bean
  2. 其他Bean依赖该Bean,该Bean会先被初始化

从这两个出发点来思考解决问题方案的话,大概有如下几种方式:

直接依赖某Bean ​

如下代码所示:

java
@Component
public class A {

    @Autowired
    private B b;
}

如上,在加载Bean A的时候,一定会先初始化Bean B

DependsOn ​

对于应用之外的二方或者三方库来说,因为我们不能修改外部库的代码,如果想要二方库的Bean在初始化之前就初始化我们内部的某个bean,就不能用第一种直接依赖的方式,可以使用@DependsOn注解来完成,如下代码所示:

java
@Configuration
public class BeanOrderConfiguration {

    @Bean
    @DependsOn("beanB")
    public BeanA beanA(){
        return new BeanA();
    }
}

当然,DependsOn注解也可以作用在@Component注解上面。

> - 一方库:由你自己或你的团队开发并使用。 > - 二方库:由与你有直接合作关系的合作伙伴开发,通常用于双方的合作项目中。 > - 三方库:由独立于你和你公司以外的第三方开发,通常是公开可用的,广泛用于多个项目中

BeanFactoryPostProcessor ​

前两种方式只能对于特定的Bean生效,如果我们希望某个Bean在其他所有Bean加载之前就初始化,用前面两种方式显然是不合适的,我们这个时候,就需要从Spring容器的生命周期中去找方法。

📄 ✅SpringBean的初始化流程

打开文档:✅SpringBean的初始化流程

通过上面的参考文章我们可以知道,Spring的Bean在初始化之前,会通过BeanFactoryPostProcessor#postProcessBeanFactory对工厂进行处理,我们可以依赖这个特性,在此刻提前初始化我们需要的bean

java
@Component
public class PrimaryBeanProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        PrimaryBean bean = beanFactory.getBean(PrimaryBean.class);
        System.out.println(bean);
    }
}
@Component
public class PrimaryBean {

    public PrimaryBean() {
        System.out.println("init primary bean");
    }

    @Override
    public String toString() {
        return "PrimaryBean{aaa}";
    }
}

这个时候我们通过控制台发现,PrimaryBean的初始化等级会优于其他Bean,如下所示:

bash
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2023-05-21 14:32:09.499  INFO 59380 --- [           main] cn.wxxlamp.spring.boot.Main              : Starting Main using Java 1.8.0_291 on B-13CKQ05P-0131.local with PID 59380 (/Users/chenkai/code/test/target/classes started by chenkai in /Users/chenkai/code/test)
2023-05-21 14:32:09.503  INFO 59380 --- [           main] cn.wxxlamp.spring.boot.Main              : No active profile set, falling back to default profiles: default
init primary bean // 希望初始化的Bean
PrimaryBean{aaa}
aware applicationContext // 系统配置Bean
initializingBean

踩坑陷阱 ​

Order只能控制同一个Bean类型中集合的顺序,不能控制不同Bean的初始化顺序,举个例子:

java
@Component
public class Container {

	private final List<Bean> beanList;
    
    public Container(List<Bean> beanList) {
        this.beanList = beanList;
    }
}
@Order(1)
@Component
class BeanA implements Bean {}
@Order(2)
@Component
class BeanB implements Bean {}

这样在读取的时候,beanList中,BeanA的顺序是先于BeanB的。