Spring系列 - 你可能学了假的Spring

Itachi 2020年01月08日 1,360次浏览

Spring系列 - 你可能学了假的Spring

在提到spring的自动注入,我们肯定都了解,网络上有大幅篇章的博客都有去描述spring的各种依赖注入的方式,以及说明.但是本文的自动注入可能会颠覆你对spring的认知(在debuger源码时,我也被颠覆了.可以说百度上的很多博客对于spring的自动注入都理解错了,下文将会通过我的理论+代码+源码证明理论的方式说明!)

自动注入需要相对于手动装配来说. 在spring应用程序当中假设你的A类依赖了B类. 需要在A类当中提供一个B类的属性, 再加上setter, 继而在xml当中配置、描述一下这两个类之间的依赖关系. 如果做完当容器初始化过程中会实例化A,在实例化A的过程中会填充属性,由于在xml中已经配置、描述好两者的关系,故而spring会把B给A装配上;这种由程序员自己配置、描述好依赖关系的写法叫做手动装配. 看个例子吧!

手动装配

setter注入

application.xml

创建两个类,在xml里配置这两个类,并且TestService依赖于DemoService,在Demo.java内getBean()并调用xxx()方法打印demoService验证是否注入成功.

关于依赖注入的资料可以参考官网

这一章节提到了一个非常重要的知识点,也是一个常见的spring面试题目。spring有几种依赖注入方式?那么这个问题应该怎么回答呢? 主要是最后面这句话:

DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection.

DI存在两个主要变体:基于构造函数的依赖注入和基于Setter的依赖注入。

不管是手动装配还是自动装配都是基于这两种方式或者变体方式来的;但是这里一定要回答到主要和变体两个名词,因为有的注入方式就不是这两种,而是这两种其中一种的变体方式;比如在一个类的属性上面加@Autowired,这种方式注入属性的方式就是利用了java的反射知识,@Autowired这种注入的方式是setter注入方式的一种变体

但是这里需要说明的是所谓的setter其实和属性无关,什么意思呢?一般的setter方法会对应一个属性,但是spring的基于setter的注入方式是不需要属性的,仅仅只需要一个setter方法,下面这个例子来说明这个问题.

TestService.java

application.xml

运行上面的代码可以看到spring也会调用这个setWwwww方法,如果仔细观察调用栈可以看到这个方法是在spring容器初始化的时候实例化TestService,完成TestService的注入功能时候调用过来的

可能有人会说如果使用注解呢?比如如下代码,不过TestService同样会注入DemoService,但不过是通过field.set去完成注入的,不是通过setter,再次说明一下这是setter的一种变体(官方支持的注入方式只有两种!一种是通过setter注入,一种是通过构造方法`注入)

Constructor注入

application.xml

自动装配

前言

我看过很多资料说@Autowired也算自动装配,其实在一个属性上面加@Autowired注解应该属于手动装配,我会通过大量源码和例子来证明的这个理论。spring官网有说明自动装配有四种模型分别是nobyTypebyNameconstructor. 参考官网资料,而现在流行着这么一种说法:@Autowired就是通过byType来完成注入的。其实严格意义上来讲这句话大错特错,因为byType在spring官网来说仅仅是一种自动注入模型而已,每种模型都有自己的代码技术实现,而@Autowired是一个注解,这个注解会被spring的后置处理器(AutowiredAnnotationBeanPostProcessor)解析,和处理byType不是同一回事。如果需要讲清楚他们的区别和证明@Autowired不是自动装配则首先要搞明白什么是自动装配。我接下来会花一定篇幅来解释自动装配的知识,然后回过头来讲他们的区别和证明@Autowired属性是手动装配。

那么接下来讨论自动注入或者叫自动装配;自动注入的出现是因为手动装配过于麻烦,比如某个类X当中依赖了10个其他类那么配置文件将会变的特别冗余和臃肿,spring的做法是可以为这个X类提供一种叫做自动装配的模型,无需程序员去手动配置X类的依赖关系。有人会疑问,用注解不也是可以解决这个xml臃肿的问题?确实用注解可以解决,但是我们现在讨论的是自动装配的问题,就不能用注解;为什么不能用注解来讨论自动装配的问题呢?因为在不配置**BeanFactoryPostProcessor(后置处理器)**和修改BeanDefinition的情况下注解的类是不支持自动装配的(关于BeanFactoryPostProcessorBeanDefinition的知识以后有时间更新).这也是证明@Autowired默认不是自动装配的一个证据,那又如何证明注解类是默认不支持自动装配呢?下文我会解释一个注解类默认是不支持自动装配的。也就是说如果讨论自动装配最好是用xml形式来配置spring容器才会有意义..

application.xml

上面代码运行起来,TestService能注入DemoService,但是在xml配置文件中并没有去手动维护、描述他们之间的依赖关系,而是在xml的根标签上面写了一行default-autowire=“byType”,注意byType是一种注入模式。 因为在官网文档里面spring也是这么定义的,自动注入模式和前面提到的依赖注入方式(setter和构造方法)是两回事,简单说:依赖注入是一个过程,主要通过setter和构造方法以及一些变体的方式完成把对象依赖、或者填充上的这个过程叫做依赖注入,不管手动装配还是自动装配都有这个过程;而自动装配模式是一种完成自动装配依赖的手段体现,每一种模型都使用了不同的技术去查找和填充bean;而从spring官网上面可以看到spring只提出了4中自动装配模式,(第一种是no,表示不使用自动装配.)。这四个模式分别用一个整形来表示,存在spring的beanDefinition当中,任何一个类默认是no这个装配模式,也就是一个被注解的类默认的装配模型是no也就是手动装配;下图我会展示spring源码当中如何定义每个类型对应的int值多少,需要注意的是官网上面说的四种注入模型其中并没有我们熟悉的@Autowired,这也再一次说明@Autowired不是自动装配;可能有人会提出假设我在TestService类的某个属性上面加上@Autowired之后这个TestService类就会不会成了自动装配呢?@Autowired是不是会改变这个类A当中的autowireMode呢?我们z可以写一个例子来证明一下:

在此之前可以先看一下Spring源码中定义的4种装配模型 自定义一个spring后置处理器 控制台输出

可以看到结果为0,说明这个类不是自动装配,其实这已经能证明@Autowried不是自动装配了,但是还有更直接的证据证明他不是自动装配,就是通过spring源码当中处理@Autowried的代码可以看出,下文在上源码证明!

使用byType & byName注入

xml配置了TestServer.java与DemoService.java都是自动装配模型为byType讲道理autowireMode=2

控制台打印结果

如果把注入模型改成byName则结果应该会改变

(写这个破玩意好累啊。。。。等心情好了再更新源码的吧。。。)

简单提一嘴子吧...

调试spring源码当中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean方法,这个方法主要就是完成属性填充的,也就是大家说的注入

总结

不管咋样,写了那么多总结一下吧...

byType是一种自动注入模型;@Autowried是一个注解,两个没有关系,一点关系都没有;@Autowried讲道理算是手动装配;那么一个注解的类到底能不能开启自动装配呢?答案是可以的,

  1. 还有,Spring的注入方式只有两种!
    • setter注入,
    • 构造方法注入,
  2. 注入模式有4种,上面提到过的
    • no、
    • byType、
    • byName、
    • constructor

其他的一些注入方式都只不过是这两种方式内其中一种的变体!