Spring 官网对自动注入模型的解释

以下引用 spring 官方文档,1.4.5. Autowiring Collaborators

Table 2. Autowiring modes

Mode Explanation
no (Default) No autowiring. Bean references must be defined by ref elements. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system.
byName Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master and uses it to set the property.
byType Lets a property be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens (the property is not set).
constructor Analogous to byType but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.

从这张表可以看出,Spring 官方给出的解释是自动注入模型只有 4 种,那么 @Autowried 是不是哪一种自动注入模型呢?

你一定会说,@Autowried 是先通过类型查找,再通过名称查找,那么就可以证明 @Autowried 是先通过 byType 再通过 byName 这两种模型实现的

听起来好像的确是这么一回事?但是的确是这么一回事么?我们先看一下源码

属性填充的源码

  1. // 属性填充
  2. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
  3. // 以上省略代码无数...
  4. // ★ 第一段逻辑:XML 装配逻辑
  5. // 判断是不是根据 AUTOWIRE_BY_NAME 或 AUTOWIRE_BY_TYPE 自动注入
  6. // 那么 setter 方法就会被支持
  7. if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
  8. MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
  9. // Add property values based on autowire by name if applicable.
  10. if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
  11. autowireByName(beanName, mbd, bw, newPvs);
  12. }
  13. // Add property values based on autowire by type if applicable.
  14. if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
  15. autowireByType(beanName, mbd, bw, newPvs);
  16. }
  17. pvs = newPvs;
  18. }
  19. boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  20. boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
  21. // ★ 第二段逻辑:注解注入逻辑
  22. PropertyDescriptor[] filteredPds = null;
  23. if (hasInstAwareBpps) {
  24. if (pvs == null) {
  25. pvs = mbd.getPropertyValues();
  26. }
  27. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  28. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  29. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  30. // ★★★ 关键代码:进行属性填充
  31. PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
  32. if (pvsToUse == null) {
  33. if (filteredPds == null) {
  34. filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
  35. }
  36. pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
  37. if (pvsToUse == null) {
  38. return;
  39. }
  40. }
  41. pvs = pvsToUse;
  42. }
  43. }
  44. }
  45. // 以下省略代码无数
  46. }

通过上述代码,我们可以看到 spring 在进行属性填充的时候,执行了两端逻辑,我们先分析以下这两段逻辑

第一段逻辑

我们看到了代码中判断了 AUTOWIRE_BY_NAME,AUTOWIRE_BY_TYPE,很明显这两个属性都是 XML 配置中的属性,因为 @Autowried 并没有提供这两个属性可以使用,所以我们推断,这一段逻辑是针对使用 XML 来配置 bean 的

第二段逻辑

在 for 循环中有一个方法为 getBeanPostProcessors(),这个方法会返回 6 个实现了 BeanPostProcessor 接口的实现类,其中有一个是 AutowiredAnnotationBeanPostProcessor 方法会完成属性填充
image.png
从 AutowiredAnnotationBeanPostProcessor 这个类的名称,我们就可以看出,这个类就是处理 @Autowried 这个注解的

那么问题来了,spring 为什么会单独给 @Autowried 来单独做一段逻辑呢?甚至使用了一个特定的后置处理器来处理这个类的属性注入呢?是不是可以证明 @Autowried 并不是 spring 所说的自动注入模型中的一种方式呢?带着这些问题,我们继续证明

继续证明

我们在回到 XML 中,我们知道从 XML 的 beans 标签中,我们可以指定 beans 下所有的 bean 的默认的装配类型
image.png
没错,的确就是自动装配所说的那 4 种自动装配模型(其中 default 是指采用父级标签的装配模式,如果没有父级,默认为 no)。

如果需要的话,我们也可以单独为 bean 标签指定自动装配类型,如果单独指定了装配类型,就会覆盖默认的装配类型
image.png
细心的你可能会发现,autowire 似乎是 bean 的一个属性,是这样的么?我们继续来寻找官方证明

以下引用 spring 官方文档,1.3. Bean Overview

Table 1. The bean definition

Property Explained in…
Class Instantiating Beans
Name Naming Beans
Scope Bean Scopes
Constructor arguments Dependency Injection
Properties Dependency Injection
Autowiring mode Autowiring Collaborators
Lazy initialization mode Lazy-initialized Beans
Initialization method Initialization Callbacks
Destruction method Destruction Callbacks

我们可以看出,对于 bean 的定义中,其中有一个 Autowiring mode,那么我们就清楚了,当一个 object(POJO)转化为 Spring Bean 的时候,会有一个记录自动装配模型的属性,来标识这个 bean 的自动装配模式

OK,我们的证明继续,既然有一个自动装配模式的属性,那么我们就把标记有 @Autowired 的属性的 beanDefinition 拿出来看看这个属性的值是多少不就 OK 了

找到了有力的证明方法(打破三观)

想要拿到这个 bean 的 beanDefinition,需要写一个后置处理器(想要在 bean 进行创建之前对 bean 进行一些特殊处理,可以使用 BeanPostProcessor 接口,这也是 Spring 的扩展点之一,后续我也会详细介绍这个接口的使用)

  1. @Component
  2. public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
  3. @Override
  4. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  5. GenericBeanDefinition barBeanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("bar");
  6. int autowireMode = barBeanDefinition.getAutowireMode();
  7. System.out.println(autowireMode);
  8. }
  9. }
  10. // 执行结果
  11. // autowireMode = 0

我们可以看出,这段代码的执行结果是 0,0 代表什么意思呢?是 byType?byName?Construct?no?

我们还要继续借助源码

  1. /**
  2. * Constant that indicates no externally defined autowiring. Note that
  3. * BeanFactoryAware etc and annotation-driven injection will still be applied.
  4. * @see #createBean
  5. * @see #autowire
  6. * @see #autowireBeanProperties
  7. */
  8. int AUTOWIRE_NO = 0;
  9. /**
  10. * Constant that indicates autowiring bean properties by name
  11. * (applying to all bean property setters).
  12. * @see #createBean
  13. * @see #autowire
  14. * @see #autowireBeanProperties
  15. */
  16. int AUTOWIRE_BY_NAME = 1;
  17. /**
  18. * Constant that indicates autowiring bean properties by type
  19. * (applying to all bean property setters).
  20. * @see #createBean
  21. * @see #autowire
  22. * @see #autowireBeanProperties
  23. */
  24. int AUTOWIRE_BY_TYPE = 2;
  25. /**
  26. * Constant that indicates autowiring the greediest constructor that
  27. * can be satisfied (involves resolving the appropriate constructor).
  28. * @see #createBean
  29. * @see #autowire
  30. */
  31. int AUTOWIRE_CONSTRUCTOR = 3;
  32. /**
  33. * Constant that indicates determining an appropriate autowire strategy
  34. * through introspection of the bean class.
  35. * @see #createBean
  36. * @see #autowire
  37. * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
  38. * prefer annotation-based autowiring for clearer demarcation of autowiring needs.
  39. */
  40. @Deprecated
  41. int AUTOWIRE_AUTODETECT = 4;

其中 int AUTOWIRE_AUTODETECT = 4; 已经标识过时了,我们暂不讨论,我们可以看到,0 对应的装配模型为 no,即 int AUTOWIRE_NO = 0;

至此,终于可以证明出 @Autowried 中的自动装配方式为 0,即“不进行任何自动装配”

反证明(更加有利的证明)

既然我们可以证明使用 @Autowried 标记的 bean 不是自动装配模型,是不是打破了你的三观,为了更加有效的证明 @Autowried 不是自动装备,我们来做点手脚,看看是否可以在不使用 @Autowried 注解的情况下,通过手动更改 autowireMode 的值,来完成自动装配呢?

我们先去掉 @Autowried 注解

  1. @Component
  2. public class Bar {
  3. }
  4. @Component
  5. public class Foo {
  6. Bar bar;
  7. // 一定要提供 setter 方法 或 含 bar 参数的构造方法
  8. public void setBar(Bar bar) {
  9. this.bar = bar;
  10. }
  11. public void getBar() {
  12. System.out.println("bar = " + bar);
  13. }
  14. }

我们的 Bean工厂后置处理器类

  1. @Component
  2. public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
  3. @Override
  4. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  5. GenericBeanDefinition barBeanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("foo");
  6. // AbstractBeanDefinition.AUTOWIRE_BY_TYPE = 2
  7. barBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  8. int autowireMode = barBeanDefinition.getAutowireMode();
  9. System.out.println("autowireMode = " + autowireMode); // 2
  10. }
  11. }

测试类

  1. public class App {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  4. ac.register(AppConfig.class);
  5. ac.refresh();
  6. ac.getBean(Foo.class).getBar();
  7. }
  8. }
  9. // 执行结果
  10. // autowireMode = 2
  11. // bar = org.wesoft.spring.population.service.Bar@1d9b7cce

是不是很神奇?我们证明从正面和反面都证明了 @Autowried 并不是自动装配模型

总结

官方提供的 4 种注入模型,其实本质上都是基于 setter 或 construct 来进行属性注入的,而 @Autowried 是通过反射机制(field.set)来进行属性注入的,这就是为什么我们使用 @Autowried 时,并不需要添加 setter 方法的原因,上面的实验我们没有使用 @Autowried 注解,可以注入成功的原因也正是因为我们提供了 setter 方法。

总结一句话:@Autowried 是使用了自动装配的技术,而不是 Spring 提供的 4 个注入模型来进行自动装配

恭喜你,你对 @Autowried 的了解更加深入了