@Resource 默认注入源码分析

505 阅读4分钟
原文链接: mp.weixin.qq.com
正文共:2223 字 48 图 预计阅读时间: 6 分钟 昨天文章讲了 @Resource 的注入方式,今天我们来看下源码中 @Resource 默认是怎么自动注入属性的。本文图贼多,慎入。

项目结构如下图

对 SpringTest 类做了一点改动,不过效果是一样的。默认情况下 beanName userDao 不存在 spring.xml 配置中。我们就从这里开始一步步深入源码,查看 Spring 是如何完成依赖查找并自动注入的。 重点关注 refresh 方法,下面看看具体实现 此方法是 Spring 容器启动的核心方法,但是我们的重点是 finishBeanFactoryInitialization 方法,此方法负责实例化所有的单例 bean,懒加载的 bean 除外。下面看看具体实现 虽然内容很多,但是我们不需要都看,只用暂时先关注我们需要的方法。beanFactory.preInstantiateSingletons。 beanFactory.preInstantiateSingletons 方法会遍历 Spring 容器中所有的 beanName,经过逻辑判断,获得 beanName 对应的 bean 实例。如上图,经过判断调用 AbstractBeanFactory.getBean 方法获取 bean 实例。

AbstractBeanFactory.getBean 再调用自身的 doGetBean 方法。

doGetBean 方法调用了 getSingleton 方法。

然后又调用了自身的 getSingleton 重载方法,经过经过判断不满足条件,返回了 null。

然后我们返回到 doGetBean 方法。

注意图中红框判断,然后执行 getSingleton 另一个重载方法,注意第二个参数是一个函数式接口,下面我们看看具体内容。

注意图中 singletonFactory.getObject 方法,这个方法就是执行,前面一张图中的函数式接口里面的 crateBean 方法。下面我们来看看 createBean。

然后调用 doCreateBean 方法,接下来继续看。

然后使用 BeanWrapper 类包装 bean 实例,我们来看看 createBeanInstance 方法。

又是经过若干逻辑判断,最终调用了 instantiateBean 方法。

然后获取实例化策略,执行相应策略 instantiate 方法。

然后通过默认的无参构造方法初始化 UserServiceImpl。

此时,虽然 UserServiceImpl 已经实例化,但是其内部属性仍然为空。

此时我们再回到 doCreateBean 方法中,执行 populateBean 方法填充属性。

populateBean 方法比较复杂,不过没有关系,我们只关注红框中部分,也就是 ibp.postProcessPropertyValues 这段代码。我们可以看到这段代码是在循环里面的,那就是说明会执行多次。 如果你足够了解 Spring 你就会知道,Spring 中最重要的部分就是这些 BeanPostProcessor,Spring 就是依靠这些 BeanPostProcessor 来完成了很多重要的功能,来看幅图。

这是我从 Debug 里面拿到的当前项目中的所有的 BeanPostProcessor,其中重点关注一下 CommonAnnotationBeanPostProcessor 这个类就是 @Resource 注解完成自动注入的关键。

接下来我们来看看,CommonAnnotationBeanPostProcessor中的方法postProcessPropertyValues ,首先获取到注解的源数据对象,然后调用对象的 inject 方法。

可以看到方法里面通过一个循环来注入属性,我们的类中只注入了一个 UserDao 所以整个集合只有一个元素,下面我们来看看,具体内容。

可以看到当前的 Field 对象就是 UserDao 通过反射给 userDao 属性设置内容。接下来看看 getResourceToInject 方法。

此处判断是否有 Lazy 注解,若干没有执行 getResource 方法。

然后调用 autowireResource 方法,自动注入属性。

调用 resolveDependency 方法解决依赖问题。

我们没有使用 Lazy 注解,所以返回空,执行下面 doResolveDependecy 方法。

经过判断,执行 findAutowireCandidates 方法。

可以看到下图中方法,意思大致是通过类型获取类型对应的 beanName 数组。

又是通过调用 getBeanNamesForType 方法。

然后又调用当前类的 。doGetBeanNamesForType 方法

到这里已经渐渐清晰了,可以看到,Spring 遍历所有的 beanName,逐个找到与 UserDao 类型匹配的 beanName,最终找到了 userDaoImpl1 也就是我们 spring.xml 中配置的 beanName。

doGetBeanNamesForType 方法返回的结果数组里面包含了一个 beanName 就是我们在配置文件配置的 beanName。

我们回到 doGetBean 方法中可以看到,最终的实现类 UserServiceImpl 的 userDao 属性已经不为空了。

到此,整个源码部分已经看完了,但是我们发现 Spring 做了很多事情,我们没有办法一一讲解清楚,我们只需要关注我们需要的部分,要不然如果源码的每个点都要看的话,那就真的深入大海了。

上一篇文章 @Resource 真的是 byName 吗?