容器?工厂?我想知道这些抽象的概念在代码里对应什么代码,到底哪个类的哪个属性在存放我的controller,service对象???如果你也好奇,不妨往下看呢!
在学习Spring的时候,老师应该都教过怎么获取Spring中创建的对象:调用
ApplicationContext::getBean方法,所以我想打断点,看看getBean到底访问了哪个类的什么属性。
首先要解决的问题是:获取到SpringBoot创建的ApplicationContext对象。
SpringBoot整个启动过程只创建了一个Spring IOC 容器,会在SpringApplication.run执行完后作为返回值,我直接写一个工具类ApplicationContextHolder,将返回的ApplicationContext对象作为静态成员变量给他存下来,我们的目的就达到了,看代码:
@SpringBootApplication
@MapperScan(basePackages = "com.example.demo14.**.mapper")
public class Demo14Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Demo14Application.class, args);
// 保存上下文对象
ApplicationContextHolder.setApplicationContext(applicationContext);
}
}
工具类:
package com.example.demo14.util;
import org.springframework.context.ApplicationContext;
/**
* @author pj
* @date 2024/8/17 16:03
**/
public class ApplicationContextHolder {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static void setApplicationContext(ApplicationContext applicationContext) {
ApplicationContextHolder.applicationContext = applicationContext;
}
}
接下来定义一个controller,在里面调用ApplicationContext::getBean进行调试,看代码:
@GetMapping("/hello")
@ResponseBody
public String hello() {
ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext();
System.out.println(applicationContext);
Object userServiceImpl = applicationContext.getBean("userServiceImpl");
System.out.println(userServiceImpl);
return "成功调用接口:/hello/docker/springboot。你好SpringBoot";
}
在ApplicationContext::getBean调用处打断点
启动程序调用接口http://localhost:8081/hello,然后一直进入方法里面查看,我们来看调用顺序:
getBean调用了doGetBean,doGetBean调用了 getSignleton,我们来看getSignleton的代码
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
ok,看到返回signletonObject了,这就是根,访问的类是DefaultSingletonBeanRegistry
看看访问了哪个属性:singletonObjects.get(beanName)和earlySingletonObjects.get(beanName),舒服了,我终于知道这些对象都放在哪了。
可是还是没讲清楚applicationContext是怎么访问到DefaultSingletonBeanRegistry中的呀。往下看:
我们先来看看类uml图:
还有一张DefaultListableBeanFactory,的关系图:
这两张图很重要,要是不知道他们的继承关系,根本不知道属性和方法放在哪
我们来看看执行run方法后给我们返回了ApplicationContext的哪一个子类?
返回的是这个AnnotationConfigServletWebServerApplicationContext,但是这个类里面也没有getBean方法呀。别忘了子类会继承父类所有的公共属性和方法!还记得调用顺序吗?
AbstractApplicationContext这个抽象类实现了getBean方法,来看看代码:
@Override
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
AbstractApplicationContext中并没有实现getBeanFactory方法,只提供了方法声明,让子类去实现
震惊!!!父类竟然可以调用子类实现的方法!!!想想也是,不管先有哪个抽象类,对于最终的实现类而言,都是父类,属性和方法都在一个类里面,肯定能调用的。
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
实际上就是获取GenericApplicationContext.beanFactory,然后去调用getBean(),AbstractBeanFactory实现了这个方法,看代码:
//---------------------------------------------------------------------
// Implementation of BeanFactory interface
//---------------------------------------------------------------------
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
getBean()调用了doGetBean,doGetBean调用了getSingleton(beanName),有没有很熟悉,开始的时候我们看过这个方法了哦。
这样的话,我们就清楚getBean怎么调用getSingleton(beanName)拿到我想要的service对象,也清楚这些对象都放在DefaultSingletonBeanRegistry.singletonObjects这个属性里!对了还不知道singletonObjects是什么呢
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
就是一个Map,不要怕他。完结,欢迎进入源码的世界