SpringBoot创建的对象都放在哪里了???

容器?工厂?我想知道这些抽象的概念在代码里对应什么代码,到底哪个类的哪个属性在存放我的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,不要怕他。完结,欢迎进入源码的世界

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值