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进入getBean方法--->org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean一、先来看遍历beanDefinition生成实例
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#getObjectFromFactoryBean1.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()中获取到实例。