springboot2.x源码笔记-生成bean的实例以及初始化

本人博客原地址:springboot2.x源码笔记-生成bean的实例以及初始化
创作时间: 2019.06.30 23:04:35

springboot的源码(spring)主要分为几个部分

1、构造SpringApplication,完成spring.factories文件中Initializers与Listeners的加载
2、加载配置文件,通过ConfigFileApplicationListener
3、加载BeanDefinitionRegistryPostProcessor与BeanFactoryPostProcessor完成bean的定义包装(非生成实例)
4、生成bean实例以及初始化

本文主要针对第四点,主要看如何AbstractBeanFactory的getBean方法完成bean的实例化和初始化

基于springboot2.1.4

项目地址:https://gitee.com/eshin/springbootdemo.git#autotest

通过3中bean的定义可知有如下几类的bean定义

1、普通注解的bean(eg:@Component @Service @Controller)beanclass是原始类名
2、@Configuration 注解的配置类的beanbeanclass是用EnhancerBySpringCGlib封装的,Enhancer可参考此处
3、@Bean注解生成的Beanbeanclass为空,但是有factoryBeanName和factoryMethodName
4、用@Scope注解的上述Beanbeanclass为ScopedFactoryBean,但会通过targetBeanName查找到真正的定义,而真正的定义name前面会加上scopedTarget的前缀

记住这几个特征,在getBean的时候会用得上

进入org.springframework.context.support.AbstractApplicationContext#refresh–>org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization–>org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

一、先来看遍历beanDefinition生成实例

进入getBean方法—>org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

1、先来看第一个getSingleton,org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
2、getObjectForBeanInstance,这个在多个位置用到,除了首次调用在1中获取到的bean不为空时,其余都是在bean创建完之后

org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
因此,如果beanClass是ScopeProxyFactoryBean,缓存到beanFactory的单例实例就是beanName对应的ScopeProxyFactoryBean,不管是原型模式和Scope模式(注意,ScopeProxyFactoryBean都是以单例形式存在beanFactory中,但是根据ScopeProxyFactoryBean找到targetBeanName生成代理对象就不是单例了,Scope的介绍放在最后)

3、接下来是三种场景下的createBean

原型模式在每次调用的时候都会生成新的bean,而Scope模式,会通过scope.get(),不同的scope按照不同的逻辑,判断是否生成新的bean,比如,SessionScope,在调用scope.get()的时候,会判断是否是新的session,如果是新的session就生成新的bean,否则使用已有的bean。本文重点在于解读singleton模式。
进入org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
现在重点关注如何创建bean–>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

3.1、创建bean的实例

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance

创建@Bean方法的bean与通过带参构造方法创建bean的原理类似,org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod与org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireConstructor两个方法都是又臭又长,就不展开了(可以设置多个重载方法带不同的参数来debug)通过工厂方法和构造方法获取Bean可参考此处,大概说下做了什么:
instantiateUsingFactoryMethod 如果bean的定义中factoryMethodName不为空进入该方法
3.1.1.1、获取factoryBean,也就是@Bean方法所在的类的实例,由于在定义的时候被enhancer封装了,所以是个代理对象,如果factoryBean还没创建,先创建,否则直接获取。
3.1.1.2、在factoryBean 的原始class中通过org.springframework.beans.factory.support.ConstructorResolver#getCandidateMethods查找到所有的方法,然后从中找到目标方法。
3.1.1.3、如果目标方法有参数,调用org.springframework.beans.factory.support.ConstructorResolver#createArgumentArray–>org.springframework.beans.factory.support.ConstructorResolver#resolveAutowiredArgument–>org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency—>org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency完成参数的bean的注入
3.1.1.4、调用org.springframework.beans.factory.support.ConstructorResolver#instantiate(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object, java.lang.reflect.Method, java.lang.Object[])–>org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory, java.lang.Object, java.lang.reflect.Method, java.lang.Object…)执行目标方法,返回的实例就是需要的bean
由于factoryBean是enhancer封装的,执行factoryMethod.invoke(factoryBean, args)调用目标方法的时候会经过org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#intercept—>org.springframework.cglib.proxy.MethodProxy#invokeSuper (Enhancer代理拦截的原理)

autowireConstructor如果检测到beanClass包含有带参构造方法进入该方法并传入检测到的构造方法
3.1.2.1、筛选出匹配的构造方法
3.1.2.2、如果目标构造方法有参数,调用org.springframework.beans.factory.support.ConstructorResolver#createArgumentArray–>org.springframework.beans.factory.support.ConstructorResolver#resolveAutowiredArgument–>org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency—>org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency完成参数的bean的注入
3.1.2.3、调用org.springframework.beans.factory.support.ConstructorResolver#instantiate(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.reflect.Constructor<?>, java.lang.Object[])-->org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory, java.lang.reflect.Constructor<?>, java.lang.Object…)完成使用构造方法创建实例

**注意:**不管是通过@bean方法还是构造方法生成目标bean,在执行目标方法前(即创建实例前),就需要对参数bean完成注入,此时在BeanFactory中是没有这个目标bean的引用的,所以如果参数bean中如果也要注入目标bean,那此时参数bean就无法从singletonFactories,或者earlySingletonObject或者singletonObjects中找到相关引用,就出现循环注入的异常。也就是说,两个bean如果相互注入,则需要先开始处理的bean先实例化并在singletonsCurrentlyInCreation中标识已经创建,并且在singletonFactories,或者earlySingletonObject中能够找到。这也是为何说org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton和org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory能够解决循环注入问题有图可知,相互注入的两个bean中先处理的bean除了不能使用构造方法和@bean方法之外,还不能在创建实例后经过代理封装处理,如果设置allowRawInjectionDespiteWrapping=true,不会抛异常,但存在隐患,因为允许注入到后者中的前者,不是最终的版本。因此开发过程中要尽量避免循环注入的情况,可以在两者都成为完全体的bean后,手动在应用代码中互相调对方的set方法。

最后一个无参构造方法的构造就简单多了直接调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateBean

3.2、bean的成员变量的填充

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

完成注入的processor如:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties,完成@value和@autowired属性的注入
注入线索->postProcessPropertiest–>org.springframework.beans.factory.annotation.InjectionMetadata#inject—>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#inject—>org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency—>org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency具体的注入逻辑,不看了,大概就是通过org.springframework.context.support.PropertySourcesPlaceholderConfigurer#processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver)中定义的StringValueResolver解析@value注解的String变量(配置项解析参考此处第3点),通过getBean方法获取需要autowired的bean。
如:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties完成@resource注解的属性注入
注入线索org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject—>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject—>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResource–>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource—>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByName具体的逻辑也不看了,反正就是到getBean方法继续获取需要注入的bean

执行完所有的BeanPostProcessor的postProcessProperties方法后,需要填充的变量的值就获取完了,再调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues把变量值设置回bean中整个填充过程就完成了

######3.3、bean的初始化,一系列初始化方法的调用
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean()
因此初始化方法的执行顺序1、@PostConstruct注解的方法->2、实现了InitializingBean的Bean(如果有)的afterPropertiesSet方法->3、自定义指定的初始化方法

对于自定义初始化方法,xml配置方法可以通过 init-method="initMethod"指定,但是注解方式就没必要了,可以通过@PostConstruct注解再不行,还能实现InitializingBean,在调用下afterPropertiesSet方法。是在手痒可以修改bean的定义,方法如下

/**
 * BeanFactoryPostProcessor主要用于修改bean定义,对beanFactory中的相关参数进行修改
 */
@Component
public class CustomBeanFactoryPostProcessor implements  BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.getBeanDefinition("helloController").setInitMethodName("initController");
    }

}

到这里bean的实例化和初始化就完成了
现在回到org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean中的下面部分

// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

getObjectForBeanInstance(sharedInstance, name, beanName, mbd);在本文第2点中已经有说明
继续回到org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
在完成bean的实例化初始化后,调用SmartInitializingSingleton的实例,具体可参考这里第3点其中一个SmartInitializingSingleton实例是EventListenerMethodProcessor完成对@EventListener注解的方法封装成listener注册到beanfactory,其具体解析可参考@EventListener注解解析部分

至此,应用启动后,非懒加载的bean就都加载好了。
现在回来说说最开始的几种bean的定义,configuration类生成的bean带enhancer的后缀,以及从configuration类生成的bean中执行@bean注解的方法生成bean,前面都已说明,还有种在开启了异步的情况下,bean的定义中还是普通注解的bean,但在生成bean的时候如果有方法使用了@Async注解,则也会通过enhancer封装,具体对@Async解析可参考此处
懒加载的bean,只有第一次调用的时候才会通过getBean方法进行实例化初始化(第一次手动getBean,或者第一次被注入到其他的bean),只有也都是缓存在beanfactory的singleObjects中

二、最后来看看用@Scope注解的bean

先上一段demo(完整代码从文章开头位置下载)

@Configuration
public class HelloConfigurationScope {

    @PostConstruct
    public void init(){
        System.out.println("HelloConfigurationScope init...");
    }

    @Bean
    @Scope(value = "singleton",proxyMode = ScopedProxyMode.TARGET_CLASS)
    public OrderBean orderBeanScope(){
        System.out.println("create default orderBeanScope in HelloConfigurationScope");
        return new OrderBean();
    }
    @Bean
    @Scope(value = "thread",proxyMode = ScopedProxyMode.TARGET_CLASS)
    public RefundBean refundBeanScope(){
        System.out.println("create default refundBeanScope in HelloConfigurationScope");
        return new RefundBean();
    }

@RunWith(SpringRunner.class)
@SpringBootTest
public class GetBeanTests {

    @Autowired
    ApplicationContext context;

    @Test
    public void loadBean() throws InterruptedException {

        Object bean = context.getBean("orderBean1");
        ((OrderBean)bean).setName("orderBean1");
        Object obean = context.getBean("orderBeanScope");
        ((OrderBean)obean).setName("orderBeanScope");
        Object rbean = context.getBean("refundBeanScope");
        /**
         * 强转会出发拦截器的拦截,提前实例化,等到setName拦截事,threadlocal已经有值
         * 此处要debug不能单步执行,得现在org.springframework.aop.target.SimpleBeanTargetSource#getTarget()打断点,
         * 然后按F8才能得到第一次getBean的过程,等到setName都是第二次了
         */
        ((RefundBean)rbean).setName("refundBeanScope");//
//        第二次調用
        Object bean1 = context.getBean("orderBean1");
        ((OrderBean)bean1).setName("orderBean1");
        Object obean1 = context.getBean("orderBeanScope");
        ((OrderBean)obean1).setName("orderBeanScope");
        Object rbean1 = context.getBean("refundBeanScope");
        ((RefundBean)rbean1).setName("refundBeanScope");//

       Thread tt =  new Thread(new Runnable() {
            @Override
            public void run() {
                Object rbean = context.getBean("refundBeanScope");
                ((RefundBean)rbean).setName("refundBeanScope");
            }
        });
        tt.start();
        tt.join();
    }

}

由最开始的beanDefinition图可知,用@Scope(value = “xxx”,proxyMode = ScopedProxyMode.TARGET_CLASS)注解的bean,会有两个bean的definition,其中beanName对应的beanclass是org.springframework.aop.scope.ScopedProxyFactoryBean,另外一个scopedTarget.beanName,带有scopeTarget前缀的名字,这个对应的是bean的原始定义。不管是xxx=singleton还是其他,都是会有这两个定义,但是要设置proxyMode = ScopedProxyMode.TARGET_CLASS

1、singleton的场景@Scope(value = “singleton”,proxyMode = ScopedProxyMode.TARGET_CLASS)

beanName对应beanclass为ScopedProxyFactoryBean的beanDefinition,是不设置scope的值,默认为空,空代表是singleton。
scopedTarget.beanName对应的beanDefinition是原始的定义,scope值也是singleton。因此,beanFactory的singletonObjects中两者的实例都有。

bean的实例化过程前面已经讲述。但是缺少了ScopedProxyFactoryBean如何生成代理bean的过程。
ScopedProxyFactoryBean实现了BeanFactoryAware接口,在bean实例化之后,初始化阶段进入org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)—>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods
进入对应的setBeanFactory方法

通过ProxyFactory获取代理,以及拦截过程可以参考这里第3点这里第2点

1.1、先看看如何获取到代理对象

有前面可以知道,当某个beanName对应的beanDefinition的beanclass是ScopedProxyFactoryBean,那在实例化非懒加载对象阶段,会通过&+beanName的name去获取ScopedProxyFactoryBean,从而根据beanName存入到beanFactory的singletonObjects的对象时scopedProxyFactoryBean,而不是scopedProxyFactoryBean.getObject()获取的proxy对象(其实存proxy对象也是可以的,proxy对象也是个单例(即便是scope模式,proxy对象也是个单例,只不过targetSource.getTarget()获取的bean可能不一样)),当通过context.getBean(beanName)获取bean是才获取到scopedProxyFactoryBean中的proxy对象由图,此时图中get到的singleton肯定是不为空(先不管懒加载方式)那么进入org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance方法后,判断是个factoryBean,且不是带&的name就进入org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean

1.2、现在主要看看targetSource.getTarget();

当上面获取到的代理对象执行目标方法的时候,就会被拦截,到执行targetSource.getTarget()的位置。由于构造ScopedProxyFactoryBean对象时,使用的SimpleBeanTargetSource,因此此时进入的org.springframework.aop.target.SimpleBeanTargetSource#getTarget

@Override
	public Object getTarget() throws Exception {
		return getBeanFactory().getBean(getTargetBeanName());
	}

此时就通过带scopeTarget前缀的beanName获取到bean,由于单例的时候,beanFactory中的singletonObjects中已经有对应的实例(懒加载的如果是第一次调用则在此时加载),然后执行其目标方法。

2、非singleton场景(以ThreadScope为例)

ThreadScope,spring启动的时候并没有注册,先注册下

@Component
public class CustomBeanFactoryPostProcessor implements  BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("thread",new SimpleThreadScope());
    }

}

非singleton场景下,ScopeProxyFactoryBean的生成,以及从中获取proxy对象都是和singleton一样的,不同的在于targetSource.getTarget(),由于非singleton,因此beanFactory中的singletonObjects并不会有scopeTarget前缀的beanName对应bean,因此每次都需要经过scope.get()如图:
以SimpleThreadScope为例:

由图,两个线程获取的代理bean都是同一个,获取到不同的bean需要通过targetSource.getTarget()

总结:

1、所有的@Configuration类的bean都是经过enhancer封装的
2、@EnableAsync(proxyTargetClass=true)情况下,非@Configuration的类的方法如果使用了@Async,那这个类也会被enhancer封装(跟1的封装不同,具体可看上面的内容)
3、所有需要实例化的bean都是(能通过getBean获取)的bean,都要求beanFactory中有对应的beanDefinition,所以如果有bean在定义过程中,被条件注解过滤,后续无法创建其实例。
4、spring允许两个bean相互注入,但是先处理的bean,不能通过构造方法或者工厂方法注入后者,否则会出现cycle异常,后者可以用工厂方法或者构造方法注入前者。相互注入的bean,最好都通过成员变量注入的方式。如果前者存在@Async注解的方法等有被封装成代理的bean的情况下,也会导致相互注入失败,此时建议通过set方法手动设置原本需要注入的bean。
5、当bean类有多个构造方法时,需要使用@Autowired指定用来构建实例的构造方法,不允许有多个构造方式注解@Autowired,当无定义构造方法或者只有一个构造方法,无需使用@Autowired
6、当有多个工厂方法重载(都是用@Bean,没有@Bean的重载方法不参与比较),首先public的有限,然后看参数个数,个数多的优先,然后看参数类型匹配度。
7、初始化方法的执行顺序1、@PostConstruct注解的方法->2、实现了InitializingBean的Bean(如果有)的afterPropertiesSet方法->3、自定义指定的初始化方法,执行这些初始化方法的时候,可以在这些方法中使用注入的对象
8、实现了ApplicationListener接口的bean,会注册到context的listeners列表中,而没有实现该接口的bean,如果有方法注解了@EvenListener,则该方法会被封装成一个listener并注册
9、当@Scope的值时singleton,beanFactory中会有beanName对应的ScopeProxyFactoryBean实例,和一个scopeTarget.beanName对应的目标bean实例。scope值为其他则没有scopeTarget.beanName对应的目标bean实例,需要在从ScopeProxyFactoryBean获取到proxy实例后,再执行目标方法时,才会从scope.get()中获取到实例。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值