不是吧,这八股文真能解决问题


theme: condensed-night-purple

1. 引言

Hi,你好,我是有清
何谓八股,乃“空疏无用,实于政事无涉”,在我们面试中经常碰到一些八股问题,我们常常嗤之以鼻,这玩意,我可以背诵并默写,但是八股真正的“空疏无用”么,直到有一天,射出去的子弹击中了我......image.png

2. 背景

在一个风和日丽的下午,啪啪啪,十几个风险处理单打破了美好的摸鱼时光,中间件团队要求升级一个二方包,否则就直接断流掉不给我们的应用提供查询服务,属实霸道。但毕竟寄人二方包下,我们还是按照操作文档升级一波。


经过在一个应用上的严谨的测试与调试,发现该升级未影响到业务的使用,为了和团队小伙伴分享升级二方包的乐趣,我将风险处理单进行了分发,让大家一起升级。


结果,团队中的一位同学干活非常快,在半小时就在群里@我,你这个升级文档不行啊,查询的时候报了空指针
出现了问题,首先不要慌,先把锅甩出去“不对啊,我升级的没问题”
然后为了展示我们的责任感,我们需要“我来看看你的改动”

3. 大胆分析

小窗私聊了这个同学发生错误的应用,以及相关的 trace 信息
观察堆栈,发现在 userService.getUser(id) 这一行发生了空指针
认真核实这个同学的升级步骤,发现果然和我操作的升级步骤一摸一样,果然这个同学没有骗人,看一下这个 UserCenter 的代码

```java @Component(userCenter) public class UserCenter{

@Autowired
private UserService userService;

...

// 这一段是空指针的位置
public UserDO getUser(Long id) {
    return userService.getUser(id);
}

} ```

朴实无华的 userService 注入,朴实无华的 getUser 查询
控制变量,问题肯定出现在这个二方包团队提供的包上,为了找出实锤,我们分析一波当前升级的版本和之前我们使用的版本差异在哪里
最明显的差异在于,新增 UserSecurityProcessor 类,我们来看一下 UserSecurityProcessor 类代码

实际上的代码会比较复杂,这边简化一大版

public class UserSecurityProcessor implements BeanFactoryPostProcessor {

    private static final AtomicBoolean registed = new AtomicBoolean(false);

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (registed.compareAndSet(false, true)) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            Map<String, BBean> beansOfType = beanFactory.getBeansOfType(UserService.class);
        }
    }

    ......
}

4. 八股论证

现场先勘查到目前为止,接下来来一波理论知识

4.1 Spring refresh 过程

image.png

  1. postProcessBeanFactory(beanFactory);

允许在上下文子类中对bean工厂进行后处理。这是一个扩展点,让开发者有机会在标准初始化之后,但在任何bean定义被加载之前,对BeanFactory进行自定义修改。

  1. invokeBeanFactoryPostProcessors(beanFactory);

调用在上下文中注册为bean的BeanFactoryPostProcessor。这一步允许所有的BeanFactoryPostProcessor对bean的定义进行修改之前初始化一些特别的bean设置,比如通过PropertyPlaceholderConfigurer解析属性文件。

  1. registerBeanPostProcessors(beanFactory);

注册那些拦截bean创建过程的BeanPostProcessor。这一步是注册所有的BeanPostProcessor实例到BeanFactory中,这些处理器会在bean初始化前后应用(如@Autowired注解处理)。

  1. initMessageSource();

初始化消息源,为这个上下文设置国际化能力,例如用于解析消息代码。

  1. initApplicationEventMulticaster();

初始化应用事件广播器,设置一个应用事件多播器,用于后续的事件发布。

  1. onRefresh();

初始化其他特殊bean,在具体的上下文子类中。这是一个扩展点,允许在特定的上下文实现中进行进一步的初始化。

  1. registerListeners();

检查监听器bean并注册它们。注册之前通过XML或者注解配置的应用事件监听器。

  1. finishBeanFactoryInitialization(beanFactory);

实例化所有剩余的(非懒加载的)单实例。这一步完成所有单实例的bean的创建工作,但不包括懒加载的bean。

  1. finishRefresh();

最后一步:发布相应的事件。这些事件包括ContextRefreshedEvent,告知相关监听者上下文刷新事件已经完成,以及如果web应用中定义了,则还包括RequestHandledEvent。

4.2 BeanFactoryPostProcessor 作用

先看一下原汁原味的注释image.png
这个注释的的整体意思指的是 BeanFactoryPostProcessor 开放的目的是**允许开发者修改容器中的 BeanDefinition ,但是不允许在在此处实现 Bean 的实例化,**如果需要 hack 到 bean 的实例话过程,请考虑使用 BeanPostProcessor

5. 破案

来看一版简化版代码

```java

@Component public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    Map<String, BBean> beansOfType = beanFactory.getBeansOfType(BBean.class);
}

}

@Component("bBean") public class BBean {

@Autowired
private ABean aBean;

public ABean getABean() {
    return aBean;
}

}

```

此处 getABean 为空。那么经过上面两个八股复习,铁汁,你是否顿悟?
@AutoWired起作用依赖于AutowiredAnnotationBeanPostProcessor。AutowiredAnnotationBeanPostProcessor 是 BeanPostProcessors 的实现。那 BeanPostProcessors在**registerBeanPostProcessors(beanFactory) **的时候被调用,在 postProcessBeanFactory 之后。
也就是说BBean被触发提前初始化的时候,AutowiredAnnotationBeanPostProcessor还没有被注册自然也不会被执行到,自然 ABean=null。一图概之image.png

6.总结

总结一下,本文踩坑的点就在于,使用 BeanFactoryPostProcessor 的时候去实现了类的加载,当然这里再留下几个八股给大家 - Bean 的加载顺序固定吗? - 当我想指定 Bean 的加载顺序有可能吗?

文章到这里结束啦,你的点赞评论收藏,是我持续更新的动力。

7. 闲言碎语

一个打工的下午,我说这个太阳好好看
隔壁同事:这就是工作日吗

image.png

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值