先上一个调用栈的图:
我的代码结构是:
@Component
public class UserServiceImpl {
@Autowired
private ITest test1;
public String hello(){
test1.sayHello();
System.out.println("hello world");
return "hello";
}
}
/**
*
* @since 2019-06-11 11:09
*/
@Component
public class Test1 implements ITest {
@Autowired
UserServiceImpl userService;
@Override
public void sayHello() {
System.out.println("hello Test1");
}
}
public class Mytest {
public static void main(String[] args) {
//System.out.println(111);
//AnnotationC
AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext();
configApplicationContext.scan("com.test");
//configApplicationContext.register(UserServiceImpl.class);
configApplicationContext.refresh();
UserServiceImpl o = (UserServiceImpl)configApplicationContext.getBean("userServiceImpl");
System.out.println(o.hello());
}
}
从调用栈看,spring在执行UserServiceImpl的初始化(因为我打断点的条件是beanName.equals("userServiceImpl")),这里简单说下,spring初始化bean生命周期,上面的调用栈已经到了spring容器初始的尾声,已经执行到了refresh方法里面的finishBeanFactoryInitialization(初始化剩余的bean,简单理解其实就是用户加了service或component那些bean)方法,在finishBeanFactoryInitialization方法前,其实执行了好多工作,比如执行BeanFactoryPostProcessors,注册BeanPostProcessors等,至于这些的用途,不在这里说明。
再从堆栈的preInstantiateSingletons看,里面是一个for循环去把beanDefinitionNames里面的bean定义实例化,首先是调用getBean方法,然后第一次调用doGetBean,在这个方法里头,先尝试去重cache里面找提前曝光的对象Object sharedInstance = getSingleton(beanName);第一次调用肯定没有,就继续走下去,第一次调用getSingleton方法,先从singletonObjects里面找有没有已经创建好的bean,如果没有就继续往后走,这里方法有个beforeSingletonCreation(beanName);方法,这个方法有个list(singletonsCurrentlyInCreation)用于存放这个beanname,用于判断bean是否正在创建,然后就执行createBean方法,这里里头有执行好几个beanPostProcessor,然后就第一次执行populateBean,这个方法是用于填充属性,是一个继承InstantiationAwareBeanPostProcessorAdapter的AutowiredAnnotationBeanPostProcessor的postProcessProperties方法中完成,里面执行对象的inject(注入),当遇到要注入class,还会判断是根据type,和name注入,主要在DefaultListableBeanFactory中的doResolveDependency完成,好了,到这里,如果返现Test baen没有创件就执行getBean方法,这里就有点像递归了,然后就执行Test的创建,流程和上面一样,关键是在执行Test的populateBean填充属性的时候,去填充UserServiceImpl的时候,会首先判断该bean是否正在创建,由于上面已经把这个bean放在一个正在创建的list(singletonsCurrentlyInCreation)中,最终会走到上图堆栈的最后一个方法getEarlyBeanReference,把提前曝光的对象拿出来,至于这个getEarlyBeanReference所在的对象什么时候放进去,这个就要看前面代码,这里不贴出来。在此已经基本完成解决循环引用的过程。