美文网首页
springboot2.x源码笔记-生成bean的实例以及初始化

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

作者: Eshin_Ye | 来源:发表于2019-06-30 23:04 被阅读0次
    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 注解的配置类的bean beanclass是用EnhancerBySpringCGlib封装的,Enhancer可参考此处
    3、@Bean注解生成的Bean beanclass为空,但是有factoryBeanName和factoryMethodName
    4、用@Scope注解的上述Bean beanclass为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#instantiateUsingFactoryMethodorg.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#getSingletonorg.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()中获取到实例。

    相关文章

      网友评论

          本文标题:springboot2.x源码笔记-生成bean的实例以及初始化

          本文链接:https://www.haomeiwen.com/subject/gzrnqctx.html