Spring源码深度解析(中)

        接着上一篇博客 Spring源码深度解析(上) 继续往下讲。

11.调用AbstractApplicationContext#finishBeanFactoryInitialization()方法,在该方法中,会提前初始化单例非懒加载的Bean,代码较长,我只截取关键的代码:

       通过上面截取的代码可以知道,在创建Bean之前会先获取 beanName的集合,进行遍历,调用DefaultListableBeanFactory#getMergedLocalBeanDefinition()方法,获取RootBeanDefinition对象,这里顺便讲一下 BeanDefinition这个接口,实际上是用于描述 Bean的,就类似于Java中描述类的是Class,因此它有很多属性用于描述 Bean,比如 BeanDefinition#setLazyInit()方法,设置 Bean是否懒加载;BeanDefinition#setScope()方法,设置 Bean的作用域(singleton、prototype、request、session等);BeanDefinition#setBeanClassName()方法,表示后续生成的是哪个对象;BeanDefinition#getPropertyValues()方法,拿到MutablePropertyValues,可以给Bean对象的成员属性赋值(该类必须要有相应的Set方法)等等...ok,回到getMergedLocalBeanDefinition()方法,就可以知道为什么在创建Bean之前必须先获取与之对应的BeanDefinition了,当然,在该方法中做了一些"合并"的处理,因为会有这么一种情况:比如在以前的Spring版本中(如2.X版本),那个时候主要还是以为通过xml配置Bean,而不是通过注解。其中有一个<bean> 标签(如下图所示),在该标签中,有一个"parent",他就指向另一个bean标签的id,表示继承关系,当user2在创建的时候,对于没有设置成员属性值的,用"父类"user1的值,如果给user2设置了属性值,则会覆盖user1设置的属性值(优先级更高)。到此时,就获取了这个 beanName对应的BeanDefinition对象了。

        然后就是判断这个BeanDefinition,如果它是懒加载、非单例、抽象,都不会创建提前创建 Bean,这里多一嘴:这里的抽象,不是说这个类是抽象类,因为针对抽象类,在包扫描阶段就过滤掉了,这里的抽象,也是针对的<bean>标签的abstract属性为true的情况(如上图所示)。

        再判断其是否实现了FactoryBean接口,代码如下:

        如果是,则调用DefaultListableBeanFactory#getBean(String name),传入 "&" + beanName,获取Bean对象,该方法中就会创建真正的Bean对象,下一行又通过 instanceof 关键字判断了一次,这里我感觉有点多余,然后将这个bean强转成FactoryBean对象,再判断其是否实现了SmartFactoryBean接口,如果是,调用SmartFactoryBean#isEagerInit()方法,如果返回true,则调用getBean()方法,传入真正的 beanName(不带"&"前缀的),获取FactoryBean#getObject()方法返回的对象,但是这个对象不会存放在单例池,存在了其他的地方;如果没有实现FactoryBean接口,则调用DefaultListableBeanFactory#getBean(beanName),创建Bean对象,具体看看该方法:

如果调用DefaultListableBeanFactory#getSingleton(beanName),可以得到Bean对象,再看看DefaultListableBeanFactory#getObjectForBeanInstance(beanName)方法,代码如下:

        看上面的代码就知道,这里实际上是处理FactoryBean接口类型的情况:调用BeanFactoryUtils.isFactoryDereference(String name)方法,判断name中时候有前缀"&"?如果有,再判断这个从单例池获取到的Bean是否实现了FactoryBean?如果没有实现,则抛异常,表示beanName如果前缀是"&",必须返回FactoryBean类型的对象;如果前缀没有"&",并且该对象不是实现了FactoryBean接口,则直接返回该对象。如果是实现了FactoryBean接口,传入beanName,判断缓存中时候存在了对象,如果不存在在,则调用AbstractBeanFactory#getObjectFromFactoryBean()方法,获取FactoryBean#getObject()方法,得到返回的对象,并将其放入缓存中,也就是FactoryBeanRegistrySupport的factoryBeanObjectCache。当然,再放入缓存之前,会调用后置处理器链的后置处理方法,即BeanPostProcessor#postProcessAfterInitialization(Object bean, String beanName)方法,传入该对象,判断其是否需要进行AOP?如果需要,则放入的是经过AOP后生成的代理对象;否则,返回的是原始对象。代码如下:

        回到调用AbstractBeanFactory#getObjectForBeanInstance()方法的这一行继续往下看,代码如下:

        上面的这段代码的逻辑是当从单例池中,通过 beanName获取不到对象的时候,就需要创建对象了。在创建对象之前会做一些基本的校验,然后会解析该类是否被 @DependsOn注解修饰,如果是,则获取注解里面配置的 value值,也就是beanName,意思是这个待创建的对象是依赖于这个beanName的,因此要调用geBean(String beanName)方法,先创建这个对象。当然,在创建这个对象之前,还会检查:比如 A类 dependsOn B类,同时 B类也 dependsOn A类,这种情况属于循环依赖的一种,无解,这种情况,Spring直接抛异常 。

        然后通过BeanDefinition,判断该类的作用域,分三种情况:

① 如果是 Singleton,则直接调用AbstractBeanFactory#getSingleton()方法,传入beanName和一段λ表达式(用于创建对象);

② 如果是 Prototype,先调用AbstractBeanFactory#beforePrototypeCreation()方法,再调用AbstractBeanFactory#createBean()方法,往prototypesCurrentlyInCreation属性(Set)中设置该beanName,表示当前原形对象正在被创建。由于每次都调用AbstractBeanFactory#createBean()方法,因此可以知道,原型对象每次都会创建;

③ 如果是 Request/Session,表示作用域是一次请求/会话,这种主要针对Web应用,也就是SpringMVC框架,会先从 AbstractBeanFactory的scopes属性拿值,即传入 scopeName,也就是"request"/"session",获取 RequestScope对象(如果是SpringMVC框架,会提前往 scopes属性中方法中放入两个对象,即RequestScope和SessionScope),调用 Scope#get()方法,传入beanName和一段λ表达式(用于创建对象),看看Scope#get()方法,以 RequestScope#get()方法为例(RequestScope#get()方法类似),代码如下:

        根据传入的 ServletRequestAttributes#getAttribute()方法,根据传入的scope,如果是 0,获取的是 HttpServletRequest对象;如果是 1,获取的是HttpSession对象。再从Attribute中,根据beanName获取对象,如果获取到了,直接返回该对象,如果获取不到,则调用ObjectFactory#getObject()方法,获取对象,并设置到Attribute中,key为beanName。因此,此时bean的生命周期是跟一次请求/会话绑定的。当一次请求/会话结束,当然这次Request对象/session对象会被回收,里面的 Attribute属性自然也会被回收,当然Attribute中的bean对象也就没有了。

        回到①,如果是单例,则调用AbstractBeanFactory#getSingleton()方法,看看该方法,代码如下:

        看到这里,当然要去看看ObjectFactory#getObject()方法,干些了啥,就需要回到调用AbstractBeanFactory#getSingleton()方法那里,代码如下:

再看看AbstractBeanFactory#createBean()方法,代码如下:

        看代码可知,首先会判断该类是否被加载,主要是要该类对应的BeanDefinition的BeanClass属性,由于该属性的类型是 Object,在前面设主子的时候,有可能设置的只是一个字符串,即类的全限定名,此时还没有加载,需要通过classLoader进行加载,加载之后,才是 Class对象。然后调用 AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation()方法,代码如下:

        这里的代码,主要逻辑是:获取InstantiationAwareBeanPostProcessor的集合,进行遍历,调用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()方法,只有有一此调用,返回的对象不为空,则直接返回,得到这个对象,再调用BeanPostProcessor#postProcessAfterInitialization()方法,判断其是否需要进行AOP,如果是,则返回代理对象,反之返回原始对象。如果AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation()方法返回的对象不为空,则说明是程序员手动创建的对象,不需要Spring创建对象了,因此直接返回;如果返回的对象为空,则需要Spring创建对象,就会调用AbstractAutowireCapableBeanFactory#doCreateBean()方法,该方法中的代码较长,我只截取关键的地方,代码如下:

        在调用AbstractAutowireCapableBeanFactory#createBeanInstance()方法的时候,会真正创建对象,代码就不展示了,逻辑比较复杂,大概是:Spring默认通过反射,调用无参构造方法,生成对象。如果有了有参构造方法,但是没有无参构造方法,Spring创建对象会报错,除非把@Autowired注解加到该有参构造方法上,告诉Spring框架,我就是需要通过这个有参构造方法来生成对象,这样就不会报错。如果有多个构造方法,都被@Autowired所修饰,Spring框架也会报错,因为它不知道到底应该用哪个构造方法生成对象。在这里要多补充一下,之前留了一个小悬念:在进行包扫描的时候,有一种情况,也是可以被扫描到的而不被过滤,就是,当这个类是抽象类,但是类中有方法是被 @Lookup注解所修饰的情况,在createBeanInstance()方法中会被处理,代码如下:

        也就是在调用AutowiredAnnotationBeanPostProcessor@determineCandidateConstructors()方法,推断构造方法的时候(找出实例化对象应该用那个构造方法),就会找到这个类中所有被@Lookup注解修饰的方法,并封装成LookupOverride对象,设置的该类对应的RootBeanDefinition的methodOverrides属性中。因此RootBeanDefinition.hasMethodOverrides()方法的时候,会返回true,进入else的代码块,即调用SimpleInstantiationStrategy#instantiateWithMethodInjection()方法,代码如下:

        调用CglibSubclassingInstantiationStrategy#instantiateWithMethodInjection()方法,为什么是 CglibSubclassingInstantiationStrategy对象的这个方法,因为之前在Spring源码深度解析(上)中讲过。在实例化DefaultListableBeanFactory对象的时候,它的无参构造就是创建的 CglibSubclassingInstantiationStrategy对象。接着往下看,代码如下:

        可以知道,如果某个类,即便它是抽象类也无所谓,因为最终都是通过CGLib创建代理类(继承该类),然后在Callback类中,具体讲,是LookupOverrideMethodInterceptor#intercept()方法中,会获取到LookupOverride对象,实际上它就是@Lookup注解的封装,可以拿到该注解的相关信息,进而拿到value值,这个value值,就是beanName,通过调用传入的owner对象(即DefaultListableBeanFactory对象),调用DefaultListableBeanFactory#getBean()方法,即可得到需要的对象。也就是说,被@Lookup注解所修饰的方法,最终返回的是该注解的value值为beanName的对象。该注解的意义在于,会有这么一种情况:如果某个bean对应依赖了其他对象,但是这个被依赖的对象是原型,如果想每次获取这个对象都不同,是做不到的,因为bean在实例化的时候,只会进行一次依赖注入,即便被依赖的对象是原型,也是如此,因此无论获取这个被依赖的对象多少次,都是同一个对象。但是有了@Lookup注解就不一样了,调用这个方法,每次都会通过 getBean()方法获取,由于这个对象是原型,因此每次都会重新创建,满足要这种特殊的求。但是这种需求我觉得很少会有。,所以了解@Lookup注解就可以了。

        回到doCreateBean()方法里面来,接着是调用 AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors()方法,代码如下:

在这里,主要是获取 MergedBeanDefinitionPostProcessor对象的集合,遍历,调用 MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)方法,在该方法中,有一个入参是 RootBeanDefinition,因此,可以通过 RootBeanDefinition设置属性:

① 比如获取RootBeanDefinition#getPropertyValues()方法,获取MutablePropertyValues对象,设置bean的成员属性(必须有set方法),如果该成员属性被 @Autowired所修饰,则还是以在这里设置的属性值为准;

② 获取RootBeanDefinition#getConstructorArgumentValues()方法,可以指定具体哪个下标,传什么参数,后面Spring在实例化的时候,就会找到与你设置的最匹配的那个构造方法,调用那个构造方法创建对象,并把你设置的参数给根据也通过构造方法传进入(其实这里设置这个构造方法,没什么用了,因为在前面Spring已经通过反射创建好bean了)。

③ RootBeanDefinition#setInitMethodName(String initMethodName)方法,设置bean的初始化方法;

④ RootBeanDefinition#setDestroyMethodName(String destroyMethodName)方法,设置bean的销毁方法等,其他就不一一列举了。

        AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors()方法执行完毕后,接着执行下面的代码,代码如下:

要解决循环依赖,要满足三个条件,即:① 单例;② allowCircularReferences为true(可设置为false);③ 当前对象正在被创建(createBean()方法中将beanName设置到一个Set属性中)。如果满足这三个条件,则调用AbstractAutowireCapableBeanFactory#addSingletonFactory()方法,传入benaName和一段λ表达式,放入三级缓存 singletonFactories。λ表达式中实际上会调用AbstractAutowireCapableBeanFactory#getEarlyBeanReference()方法,代码如下:

AbstractAutoProxyCreator#wrapIfNecessary()方法就是进行AOP的地方,这块代码后续讲解,到这里可以知道,三级缓存的主要作用就是提前对bean对象进行AOP代理。继续往下看,AbstractAutowireCapableBeanFactory#populateBean()方法,也就是给bean对象进行属性填充,目前bean对象还只是被创建出来,没有进行属性的依赖注入,因此它也被称为早期bean对象。看看populateBean()方法,代码如下:

        由代码可知,这里调用的是后置处理器的实例化后的方法,即 InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation(),返回值是Boolean类型,如果返回的是false,则直接return,不再进行属性的依赖注入。然后是Spring的依赖注入,这里跟@Autowired注解不太一样,主要是看BeanDefinition对象的autowireMode属性,是枚举类型,一般有三种:AUTOWIRE_BY_NAME、AUTOWIRE_BY_TYPE、AUTOWIRE_CONSTRUCTOR,分别是通过beanName/beanType/构造方法,进行依赖注入,如果想通过beanName进行依赖注入,只需要调用BeanDefinition#setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME)方法即可。

        如果是BY_NAME,则调用 AbstractAutowireCapableBeanFactory#autowireByName()方法,代码如下:

        首先是获取需要进行依赖注入的属性,那么问题来了:什么样的属性能进行自动注入?看 AbstractAutowireCapableBeanFactory#unsatisfiedNonSimpleProperties()方法中的if条件判断,就可以知道:① 该属性有对应的set方法;② 没有在ignoredDependencyTypes中(之前设置的接口忽略列表);③ 如果该属性对应的set方法是实现的某个接口中所定义的,那么接口没有在ignoredDependencyInterfaces中;④ 属性类型不是简单类型,比如int、Integer、int[] 等。

然后获取到所有的 set方法,遍历,获取set后面的字符串,当作beanName到单例池中查找,如果找到了bean,将其放入MutablePropertyValues对象中,key为该 beanName,value就是bean对象。

        如果是BY_TYPE,则调用 AbstractAutowireCapableBeanFactory#autowireByType()方法,代码如下:

        可知,类似BY_NAME:也是获取所有的set方法,进行遍历,只不过获取bean对象,是根据bean的类型,即调用AbstractAutowireCapableBeanFactory#resolveDependency()方法获取,然后也是将其放入MutablePropertyValues对象中。

        接着往下看,就是执行属性的依赖注入,包括@Autowired和@Resource注解,代码如下:

        先看看AutowiredAnnotationBeanPostProcessor#postProcessProperties()方法,代码如下:

        找到class中那些被@Autowired、@Value、@Inject注解修饰的method/field,并创建AutowiredMethodElement对象或者AutowiredFieldElement对象存储method或者field相关信息,对于method/field,如果被static关键字修饰,则不会进行依赖注入,因为如果是静态方法/属性,它是属于类的,而不属于某一个具体对象。假设基于这个类创建了多个对象,某个静态属性允许进行依赖注入,某个bean对象静态属性值会被改变(另一个bean对象对这个属性进行依赖注入),即便它已经进行过依赖注入,这是不被允许的。        

        再看看进行依赖注入的关键方法,即调用InjectionMetadata#inject()方法,以AutowiredFieldElement对象为例。即AutowiredFieldElement#inject()方法,代码如下 :

        在进行依赖注入的时候,如果spring发现该注入点还被 @Lazy注解所修饰,则会生成一个代理对象,进行注入,而不是不注入,代码如下:

        如果该注入点没有被@Lazy注解修饰,则会调用DefaultListableBeanFactory#doResolveDependency()方法,看看该方法:

        可知,@Value注解也是在这里处理的,DefaultListableBeanFactory#resolveMultipleBeans()方法可以获取到bean,不过该方法获取的是是一个Map集合(同一类型的bean的集合),key为beanName,value为同一个bean。

        获取到Map集合后,还再进行bean的候选筛选,即调用DefaultListableBeanFactory#findAutowireCandidates()方法,代码如下:

这里的AutowireCandidateResolver实际上是ContextAnnotationAutowireCandidateResolver,它的层级结构为:

        可知:

① 先调用 SimpleAutowireCandidateResolver#isAutowireCandidate()方法,该方法就是调用BeanDefinition#isAutowireCandidate(),判断该bean是否可以进行依赖注入,如 将@Bean注解中的 autowireCandidate()设置为false,表示不能被依赖注入;

② 再调用 GenericTypeAwareAutowireCandidateResolver#isAutowireCandidate()方法,里面调用的是 GenericTypeAwareAutowireCandidateResolver#checkGenericTypeMatch(),检查依赖的类型 dependencyType 和实际注入的类型 targetType 上的泛型是否匹配;

③ 再调用判断 QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate()方法,该方法就是调用QualifierAnnotationAutowireCandidateResolver#checkQualifiers()方法,判断待惊醒依赖注入的属性上是否加了@Qualifier注解,如果加了,则校验 @Qualifier 规则是否匹配成功。

        如果matchingBeans为空,则返回null,表示没有找到bean,如果@Autowired注解的required()返回的是true,则抛异常,表示必须要有一个可以注入的bean对象)。如果注入的类型刚好是Map,则直接返回matchingBeans;如果类型是List,则获取matchingBeans的value的集合并返回;如果返回的是单个bean,则需要继续进行过滤:

① 先找是否有被@Primary注解修饰的bean,得到它的beanName,如果有多个@Primary,则抛异常;

② 如果没有@Primary注解修饰的类,则通过优先级,即实现了 PriorityOrdered接口的类,实现 getOrder()方法,返回的值为int类型,越小优先级越高,如果有两个优先级最高且一样,则也会抛异常;

③ 不满足以上两种情况,才会通过set方法后面的字符串作为beanName(如果是成员属性,beanName就是成员属性的名字)。最后从返回的map中,通过beanName匹配,得到bean;

④ 如果经过前面三次处理后,还是没有得到满足要求的beanName,Spring只有通过类型去找bean,通过类型找到了多个bean,就会抛异常。

        找到符合条件的bean之后,通过反射调用,进行赋值。到这里为止,算是完成了基于 @Autowired注解注解的依赖注入的大概讲解。回到AbstractAutowireCapableBeanFactory#populateBean()方法中,调用InstantiationAwareBeanPostProcessor#postProcessProperties()方法,除了调用前面说的这里会调用AutowiredAnnotationBeanPostProcessor#postProcessProperties()方法,还会调用 这里会调用CommonAnnotationBeanPostProcessor的postProcessProperties()方法,代码如下:

        看代码可以知道,在该方法中,也会寻找注入点,即被@Resource注解所修饰的 field或者method,找到后,调用 InjectionMetadata#inject()方法,代码如下:

        以 field为例,即调用 InjectionMetadata#getResourceToInject()方法,代码如下:

        总结一下 @Resource注解的依赖注入逻辑:如果某个属性或者方法被Resource注解所修饰,则会对其进行依赖注入(注意:静态属性或者方法也不会执行依赖注入),首先是获取@Resource的name属性指定待注入的bean的beanName(当然你也可通过@Resource注解的 type指定待注入的属性类型)。如果能获取到name,就通过这个name到BeanFactory找bean;没有指定,就获取属性的名字,或者setXxx()方法中的xxx,作为name,去获取bean;如果通name找不到bean,则通过类型,要么是你指定的类型 ,没有指定,则获取属性的类型获取set方法入参的类型,到BeanFactory找bean,如果还是没找到,则抛异常。

        对比@Autowired注解和@Resource注解,看过他们的注入原理后,可以知道:@Autowired注解优先通过类型注入,@Resource注解优先通过名字注入。

        到这里为止,属性的依赖注入讲完了,在AbstractAutowireCapableBeanFactory#populateBean()方法中,还有一个较为重要的方法,即:AbstractAutowireCapableBeanFactory#applyPropertyValues()方法(代码就不贴了),在该方法中,会判断传入的PropertyValues对象中的属性是否为空,如果不为空,说明之前对当前Bean中的BeanDefinition中设置了PropertyValues,最终PropertyValues中的值,会覆盖@Autowired/@Resource注的依赖注入。

        由于篇幅较长,剩下的内容就留在Spring源码深度解析(下)中去聊了。如果文中聊的,哪里有问题,欢迎批评指正!

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值